diff --git a/.circleci/bootstrap.sh b/.circleci/bootstrap.sh deleted file mode 100755 index 79d1940..0000000 --- a/.circleci/bootstrap.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The Helm Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -euo pipefail - -curl -sSL https://github.com/golangci/golangci-lint/releases/download/v$GOLANGCI_LINT_VERSION/golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz | tar xz -sudo mv golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64/golangci-lint /usr/local/bin/golangci-lint -rm -rf golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64 diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index ef19b8e..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- -version: 2 - -jobs: - build: - working_directory: ~/helm.sh/helm - docker: - - image: circleci/golang:1.13 - - environment: - GOCACHE: "/tmp/go/cache" - GOLANGCI_LINT_VERSION: "1.21.0" - - steps: - - checkout - - run: - name: install test dependencies - command: .circleci/bootstrap.sh - - run: - name: test style - command: make test-style - - run: - name: test - command: make test-coverage - - deploy: - name: deploy - command: .circleci/deploy.sh - -workflows: - version: 2 - build: - jobs: - - build: - filters: - tags: - only: /.*/ diff --git a/.circleci/deploy.sh b/.circleci/deploy.sh deleted file mode 100755 index bbb08e6..0000000 --- a/.circleci/deploy.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The Helm Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -euo pipefail - -# Skip on pull request builds -if [[ -n "${CIRCLE_PR_NUMBER:-}" ]]; then - exit -fi - -: ${AZURE_STORAGE_CONNECTION_STRING:?"AZURE_STORAGE_CONNECTION_STRING environment variable is not set"} -: ${AZURE_STORAGE_CONTAINER_NAME:?"AZURE_STORAGE_CONTAINER_NAME environment variable is not set"} - -VERSION= -if [[ -n "${CIRCLE_TAG:-}" ]]; then - VERSION="${CIRCLE_TAG}" -elif [[ "${CIRCLE_BRANCH:-}" == "master" ]]; then - VERSION="canary" -else - echo "Skipping deploy step; this is neither a releasable branch or a tag" - exit -fi - -echo "Installing Azure CLI" -echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ stretch main" | sudo tee /etc/apt/sources.list.d/azure-cli.list -curl -L https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -sudo apt install apt-transport-https -sudo apt update -sudo apt install azure-cli - - -echo "Building helm binaries" -make build-cross -make dist checksum VERSION="${VERSION}" - -echo "Pushing binaries to Azure" -az storage blob upload-batch -s _dist/ -d "$AZURE_STORAGE_CONTAINER_NAME" --pattern 'helm-*' --connection-string "$AZURE_STORAGE_CONNECTION_STRING" diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index 48f48e5..0000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,9 +0,0 @@ - - -Output of `helm version`: - -Output of `kubectl version`: - -Cloud Provider/Platform (AKS, GKE, Minikube etc.): - - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 6378036..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,307 +0,0 @@ -# Contributing Guidelines - -The Helm project accepts contributions via GitHub pull requests. This document outlines the process to help get your contribution accepted. - -## Reporting a Security Issue - -Most of the time, when you find a bug in Helm, it should be reported -using [GitHub issues](https://github.com/helm/helm/issues). However, if -you are reporting a _security vulnerability_, please email a report to -[cncf-kubernetes-helm-security@lists.cncf.io](mailto:cncf-kubernetes-helm-security@lists.cncf.io). This will give -us a chance to try to fix the issue before it is exploited in the wild. - -## Sign Your Work - -The sign-off is a simple line at the end of the explanation for a commit. All -commits needs to be signed. Your signature certifies that you wrote the patch or -otherwise have the right to contribute the material. The rules are pretty simple, -if you can certify the below (from [developercertificate.org](https://developercertificate.org/)): - -``` -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -1 Letterman Drive -Suite D4700 -San Francisco, CA, 94129 - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. -``` - -Then you just add a line to every git commit message: - - Signed-off-by: Joe Smith - -Use your real name (sorry, no pseudonyms or anonymous contributions.) - -If you set your `user.name` and `user.email` git configs, you can sign your -commit automatically with `git commit -s`. - -Note: If your git config information is set properly then viewing the - `git log` information for your commit will look something like this: - -``` -Author: Joe Smith -Date: Thu Feb 2 11:41:15 2018 -0800 - - Update README - - Signed-off-by: Joe Smith -``` - -Notice the `Author` and `Signed-off-by` lines match. If they don't -your PR will be rejected by the automated DCO check. - -## Support Channels - -Whether you are a user or contributor, official support channels include: - -- [Issues](https://github.com/helm/helm/issues) -- Slack: - - User: [#helm-users](https://kubernetes.slack.com/messages/C0NH30761/details/) - - Contributor: [#helm-dev](https://kubernetes.slack.com/messages/C51E88VDG/) - -Before opening a new issue or submitting a new pull request, it's helpful to search the project - it's likely that another user has already reported the issue you're facing, or it's a known issue that we're already aware of. It is also worth asking on the Slack channels. - -## Milestones - -We use milestones to track progress of releases. There are also 2 special milestones -used for helping us keep work organized: `Upcoming - Minor` and `Upcoming - Major` - -`Upcoming - Minor` is used for keeping track of issues that aren't assigned to a specific -release but could easily be addressed in a minor release. `Upcoming - Major` keeps track -of issues that will need to be addressed in a major release. For example, if the current -version is `3.2.0` an issue/PR could fall in to one of 4 different active milestones: -`3.2.1`, `3.3.0`, `Upcoming - Minor`, or `Upcoming - Major`. If an issue pertains to a -specific upcoming bug or minor release, it would go into `3.2.1` or `3.3.0`. If the issue/PR -does not have a specific milestone yet, but it is likely that it will land in a `3.X` release, -it should go into `Upcoming - Minor`. If the issue/PR is a large functionality add or change -and/or it breaks compatibility, then it should be added to the `Upcoming - Major` milestone. -An issue that we are not sure we will be doing will not be added to any milestone. - -A milestone (and hence release) is considered done when all outstanding issues/PRs have been closed or moved to another milestone. - -## Semantic Versioning - -Helm maintains a strong commitment to backward compatibility. All of our changes to protocols and formats are backward compatible from one major release to the next. No features, flags, or commands are removed or substantially modified (unless we need to fix a security issue). - -We also try very hard to not change publicly accessible Go library definitions inside of the `pkg/` directory of our source code. - -For a quick summary of our backward compatibility guidelines for releases between 3.0 and 4.0: - -- Command line commands, flags, and arguments MUST be backward compatible -- File formats (such as Chart.yaml) MUST be backward compatible -- Any chart that worked on a previous version of Helm 3 MUST work on a new version of Helm 3 (barring the cases where (a) Kubernetes itself changed, and (b) the chart worked because it exploited a bug) -- Chart repository functionality MUST be backward compatible -- Go libraries inside of `pkg/` SHOULD remain backward compatible, though code inside of `cmd/` and `internal/` may be changed from release to release without notice. - -## Support Contract for Helm 2 - -With Helm 2's current release schedule, we want to take into account any migration issues for users due to the upcoming holiday shopping season and tax season. We also want to clarify what actions may occur after the support contract ends for Helm 2, so that users will not be surprised or caught off guard. - -After Helm 2.15.0 is released, Helm 2 will go into "maintenance mode". We will continue to accept bug fixes and fix any security issues that arise, but no new features will be accepted for Helm 2. All feature development will be moved over to Helm 3. - -6 months after Helm 3.0.0's public release, Helm 2 will stop accepting bug fixes. Only security issues will be accepted. - -12 months after Helm 3.0.0's public release, support for Helm 2 will formally end. Download links for the Helm 2 client through Google Cloud Storage, the Docker image for Tiller stored in Google Container Registry, and the Google Cloud buckets for the stable and incubator chart repositories may no longer work at any point. Client downloads through `get.helm.sh` will continue to work, and we will distribute a Tiller image that will be made available at an alternative location which can be updated with `helm init --tiller-image`. - -## Issues - -Issues are used as the primary method for tracking anything to do with the Helm project. - -### Issue Types - -There are 5 types of issues (each with their own corresponding [label](#labels)): - -- `question/support`: These are support or functionality inquiries that we want to have a record of for -future reference. Generally these are questions that are too complex or large to store in the -Slack channel or have particular interest to the community as a whole. Depending on the discussion, -these can turn into `feature` or `bug` issues. -- `proposal`: Used for items (like this one) that propose a new ideas or functionality that require -a larger community discussion. This allows for feedback from others in the community before a -feature is actually developed. This is not needed for small additions. Final word on whether or -not a feature needs a proposal is up to the core maintainers. All issues that are proposals should -both have a label and an issue title of "Proposal: [the rest of the title]." A proposal can become -a `feature` and does not require a milestone. -- `feature`: These track specific feature requests and ideas until they are complete. They can evolve -from a `proposal` or can be submitted individually depending on the size. -- `bug`: These track bugs with the code -- `docs`: These track problems with the documentation (i.e. missing or incomplete) - -### Issue Lifecycle - -The issue lifecycle is mainly driven by the core maintainers, but is good information for those -contributing to Helm. All issue types follow the same general lifecycle. Differences are noted below. - -1. Issue creation -2. Triage - - The maintainer in charge of triaging will apply the proper labels for the issue. This - includes labels for priority, type, and metadata (such as `good first issue`). The only issue - priority we will be tracking is whether or not the issue is "critical." If additional - levels are needed in the future, we will add them. - - (If needed) Clean up the title to succinctly and clearly state the issue. Also ensure - that proposals are prefaced with "Proposal: [the rest of the title]". - - Add the issue to the correct milestone. If any questions come up, don't worry about - adding the issue to a milestone until the questions are answered. - - We attempt to do this process at least once per work day. -3. Discussion - - issues that are labeled as `feature` or `bug` should be connected to the PR that resolves it. - - Whoever is working on a `feature` or `bug` issue (whether a maintainer or someone from - the community), should either assign the issue to themself or make a comment in the issue - saying that they are taking it. - - `proposal` and `support/question` issues should stay open until resolved or if they have not been - active for more than 30 days. This will help keep the issue queue to a manageable size and - reduce noise. Should the issue need to stay open, the `keep open` label can be added. -4. Issue closure - -## How to Contribute a Patch - -1. If you haven't already done so, sign a Contributor License Agreement (see details above). -2. Fork the desired repo, develop and test your code changes. -3. Submit a pull request. - -Coding conventions and standards are explained in the [official developer docs](https://helm.sh/docs/developers/). - -## Pull Requests - -Like any good open source project, we use Pull Requests (PRs) to track code changes. - -### PR Lifecycle - -1. PR creation - - PRs are usually created to fix or else be a subset of other PRs that fix a particular issue. - - We more than welcome PRs that are currently in progress. They are a great way to keep track of - important work that is in-flight, but useful for others to see. If a PR is a work in progress, - it **must** be prefaced with "WIP: [title]". Once the PR is ready for review, remove "WIP" from - the title. - - It is preferred, but not required, to have a PR tied to a specific issue. There can be - circumstances where if it is a quick fix then an issue might be overkill. The details provided - in the PR description would suffice in this case. -2. Triage - - The maintainer in charge of triaging will apply the proper labels for the issue. This should - include at least a size label, `bug` or `feature`, and `awaiting review` once all labels are applied. - See the [Labels section](#labels) for full details on the definitions of labels. - - Add the PR to the correct milestone. This should be the same as the issue the PR closes. -3. Assigning reviews - - Once a review has the `awaiting review` label, maintainers will review them as schedule permits. - The maintainer who takes the issue should self-request a review. - - Any PR with the `size/large` label requires 2 review approvals from maintainers before it can be - merged. Those with `size/medium` or `size/small` are per the judgement of the maintainers. -4. Reviewing/Discussion - - All reviews will be completed using Github review tool. - - A "Comment" review should be used when there are questions about the code that should be - answered, but that don't involve code changes. This type of review does not count as approval. - - A "Changes Requested" review indicates that changes to the code need to be made before they will be merged. - - Reviewers should update labels as needed (such as `needs rebase`) -5. Address comments by answering questions or changing code -6. LGTM (Looks good to me) - - Once a Reviewer has completed a review and the code looks ready to merge, an "Approve" review is used - to signal to the contributor and to other maintainers that you have reviewed the code and feel that it is - ready to be merged. -7. Merge or close - - PRs should stay open until merged or if they have not been active for more than 30 days. - This will help keep the PR queue to a manageable size and reduce noise. Should the PR need - to stay open (like in the case of a WIP), the `keep open` label can be added. - - Before merging a PR, refer to the topic on [Size Labels](#size-labels) below to determine if - the PR requires more than one LGTM to merge. - - If the owner of the PR is listed in the `OWNERS` file, that user **must** merge their own PRs - or explicitly request another OWNER do that for them. - - If the owner of a PR is _not_ listed in `OWNERS`, any core maintainer may merge the PR. - -#### Documentation PRs - -Documentation PRs will follow the same lifecycle as other PRs. They will also be labeled with the -`docs` label. For documentation, special attention will be paid to spelling, grammar, and clarity -(whereas those things don't matter *as* much for comments in code). - -## The Triager - -Each week, one of the core maintainers will serve as the designated "triager" starting after the -public stand-up meetings on Thursday. This person will be in charge triaging new PRs and issues -throughout the work week. - -## Labels - -The following tables define all label types used for Helm. It is split up by category. - -### Common - -| Label | Description | -| ----- | ----------- | -| `bug` | Marks an issue as a bug or a PR as a bugfix | -| `critical` | Marks an issue or PR as critical. This means that addressing the PR or issue is top priority and must be addressed as soon as possible | -| `docs` | Indicates the issue or PR is a documentation change | -| `feature` | Marks the issue as a feature request or a PR as a feature implementation | -| `keep open` | Denotes that the issue or PR should be kept open past 30 days of inactivity | -| `refactor` | Indicates that the issue is a code refactor and is not fixing a bug or adding additional functionality | - -### Issue Specific - -| Label | Description | -| ----- | ----------- | -| `help wanted` | Marks an issue needs help from the community to solve | -| `proposal` | Marks an issue as a proposal | -| `question/support` | Marks an issue as a support request or question | -| `good first issue` | Marks an issue as a good starter issue for someone new to Helm | -| `wont fix` | Marks an issue as discussed and will not be implemented (or accepted in the case of a proposal) | - -### PR Specific - -| Label | Description | -| ----- | ----------- | -| `awaiting review` | Indicates a PR has been triaged and is ready for someone to review | -| `breaking` | Indicates a PR has breaking changes (such as API changes) | -| `in progress` | Indicates that a maintainer is looking at the PR, even if no review has been posted yet | -| `needs rebase` | Indicates a PR needs to be rebased before it can be merged | -| `needs pick` | Indicates a PR needs to be cherry-picked into a feature branch (generally bugfix branches). Once it has been, the `picked` label should be applied and this one removed | -| `picked` | This PR has been cherry-picked into a feature branch | - -#### Size labels - -Size labels are used to indicate how "dangerous" a PR is. The guidelines below are used to assign the -labels, but ultimately this can be changed by the maintainers. For example, even if a PR only makes -30 lines of changes in 1 file, but it changes key functionality, it will likely be labeled as `size/L` -because it requires sign off from multiple people. Conversely, a PR that adds a small feature, but requires -another 150 lines of tests to cover all cases, could be labeled as `size/S` even though the number of -lines is greater than defined below. - -PRs submitted by a core maintainer, regardless of size, only requires approval from one additional -maintainer. This ensures there are at least two maintainers who are aware of any significant PRs -introduced to the codebase. - -| Label | Description | -| ----- | ----------- | -| `size/XS` | Denotes a PR that changes 0-9 lines, ignoring generated files. Very little testing may be required depending on the change. | -| `size/S` | Denotes a PR that changes 10-29 lines, ignoring generated files. Only small amounts of manual testing may be required. | -| `size/M` | Denotes a PR that changes 30-99 lines, ignoring generated files. Manual validation should be required. | -| `size/L` | Denotes a PR that changes 100-499 lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. | -| `size/XL` | Denotes a PR that changes 500-999 lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. | -| `size/XXL` | Denotes a PR that changes 1000+ lines, ignoring generated files. This should be thoroughly tested before merging and always requires 2 approvals. | diff --git a/KEYS b/KEYS deleted file mode 100644 index 95e52f7..0000000 --- a/KEYS +++ /dev/null @@ -1,854 +0,0 @@ -This file contains the PGP keys of developers who have signed releases of Helm. - -For your convenience, commands are provided for those who use pgp and gpg. - -For users to import keys: - pgp < KEYS - or - gpg --import KEYS - -Developers to add their keys: - pgp -kxa and append it to this file. - or - (pgpk -ll && pgpk -xa ) >> KEYS - or - (gpg --list-sigs - && gpg --armor --export ) >> KEYS - -pub rsa4096/0x461449C25E36B98E 2017-11-10 [SC] - 672C657BE06B4B30969C4A57461449C25E36B98E -uid [ultimate] Matthew Farina -sig 3 0x461449C25E36B98E 2017-11-10 Matthew Farina -sig 0x2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein -sig 0x1EF612347F8A9958 2018-12-12 Adam Reese -sig 0x62F49E747D911B60 2018-12-12 Matt Butcher -sub rsa4096/0xCCCE67689DF05738 2017-11-10 [E] -sig 0x461449C25E36B98E 2017-11-10 Matthew Farina -sub rsa4096/0x9436E80BFBA46909 2017-11-10 [S] [expires: 2022-11-09] -sig 0x461449C25E36B98E 2017-11-10 Matthew Farina - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBFoFERgBEADdhgM8EPo9fxnu2iW75r4uha2TrhWaO3EJIo53sa6U9nePIeWc -oWqjDZqYvIMJcylfocrVi4m6HdNcPrWo5pSWeKd8J9X8d4BUhoKFmJdHqWzgokwW -Rk06Doro2FHFyHoPPrI3a1HGVWA0xFhBYqSbim4j/Q0FouS566MofeRGnnacJ88z -Z7yErN5Gy4jk7pOgwvMewoGpEd8FMcyYSJfSjeoqdIZYp89EKTLbgQZuOJ9yVZnY -c0mtpH57UbkrkGv8hRuViWSO99q/mpMQyWQGYVoTV4QM/0q4jUbkRazaeY3N4hGC -I6Xf4ilWyNmmVODI6JcvWY+vXPtxIKjEjYiomVCF6jCYWWCA7cf3+kqJ+T4sc0NF -fseR/TAOkDV/XsZ1ufbSHBEiZTIjLvoAGJ+u+3go+UysVVCw4L1NSGFeDrZ97KSe -w0MeuV2SYfdZ4so7k4YDNbBLTVx0V/wl+laFtdjo167D18AYw54HIv3snHkjABfY -7Q06Ye7FuuKzdrj9KpmzUYnN3hRGqe84GIcM3D5+vElj0vyg8th32Dig5Xi38s0M -sz7hPg+oFk7csslMVAnLtWYvsv2FMSKB9FUHYv9AJ6yjYfyLlQgjjda0z6Sq5zpu -qVZqTNSxEIZFDKfTgQV6rocIK5VKP063KS6qwpHzPxKADaLTUPOWeum9/wARAQAB -tCRNYXR0aGV3IEZhcmluYSA8bWF0dEBtYXR0ZmFyaW5hLmNvbT6JAk4EEwEIADgW -IQRnLGV74GtLMJacSldGFEnCXja5jgUCWgURGAIbAwULCQgHAwUVCgkICwUWAwIB -AAIeAQIXgAAKCRBGFEnCXja5jjtQEADJvSx67Qz8gTxvUH3HaMsXaeb6BG3zLJXj -34pqAGNkKB4/ZgpFVYE1R0QuvYn9CbFpD1UcSank3L3xBroeOEUN3kvOg3D6Bv8f -mtwtW1TDjaWDTa0mZ8icanjXVNfK3K8pAwni2FPrW/tesEt/8GI48ZxPMzHk1qrL -8mETLRn1EBL3vq5qPDIK87XhhW9WAgwsadn6BQKSTSVVUACBAlV7EbqE4DHqhwYz -D1HrEIAtXkkb9JJejUnAbiOqPmm9s6iWC13K1P27FB8EEYiKxL8kb7xv5xW7+Pmg -kb03OqZtZYu9Fl1MF1zVQe4mXVflcbj7mYU1kb8vepD6bOUA89z8FggU2Q38cxkD -TYQsxpGwWz3nvEu29KbHmjQja1+G5D8kQ8bv1mNdiXQbOz51v2+7vowKKUoPQfp9 -n8Ez4dxWVrFtf218Mtt8wbYmmVYijLIBDArYKDeVqNNua8YC9641DcvRdCCvaYEx -Q9vWKjpAWmXKy2bb7TQ2TjGRh+Ly47z+PTluqUeYuBREAN4Hd4xwiClRbhb3I9To -YTJkPOkaOR967zBho5orA8xww4hcsufhjqsoU0/MGbG6jvJihHFR9Jq+0gVzakca -K8tGRSA8l5xdjow5dVOPzeXuKDPuvHEwa63TWsH5H8s6iembNT1H9bate8wQT1TN -9PH/6sthz4kCMwQQAQgAHRYhBFER2nPfEtjoEspGLyzbv7s3roIqBQJcET6LAAoJ -ECzbv7s3roIqozgQAIG5IqJ7hYjndCLW2MBLEa9oA04QSgF9qcqfiG00tjhBVwEK -YE6r7BUgC7r7dP1xVa/+5lVRATfiJ+Raq7udm/RQsamyp9Q8xBOuavPcJDZMX5m7 -OqPZMs+TDFPYM914GIWPAQf9ehaHHnmCNZXExxYlnZBPFsOcLYSNGH/xQeiA+q3F -tCOdRhjcpbt4rcx+Jq/l6X3cxstFwcYeljhvebblpwcVNJVArVrWZmosFl3rz3bs -PKfZKAvjV65knRkra73ZjN+YEYMMr6MzvVh/cnigk9XHgu5Y7imLv9qf1leyFCaa -oJoQDAcHIfs/eQmaEbYUyw/jX53/PyGqXlmkW7D3wqAGH5yx+ske7otCiaHHoTK0 -vHsEvO9b4dLtr0uMMNRO7St+3EtMa070s537XymG1HSeW8QbVEg/+w2YW5DyTe5p -WaNJS6WUc7UuIgEWvgitVxhUheZRumh5/EW673yI8iUchGslAuL1W5R1rXQfMPVA -BsI8D8pWs9EKjP4Lpu1Wgoxm0O4kaAxRbbHjrIYLtoRRrakr+kfqjZ/rJM89JQpl -NWNBZ61IDKROj7U2kLAxCJSB3RfAuqinyFGjxod7ENW7u6z0SCdupybbmylAfD+T -t3Z2DBB9tjxNnsgb2pbcm8cDGrJOZhIDdcVChvMXnHNxEmXbHvTKocci0t4viQIz -BBABCgAdFiEESdCchsPcjaPwoHYiHvYSNH+KmVgFAlwRP38ACgkQHvYSNH+KmVgP -rxAAkhggTXggRwpWzgU7PRsj347DqtH3f/2EfTOhAi6PGOiw2EFocTrx47WHAjs6 -XFT+c0yHCv58fGHKrrfeOT1VCjk2xf0NSdf00CTHO+DqepNiXzFYCJ0fUTL3w2JC -ugrfhwEdVH3TYJffFlmi0VZVCrGT3ZU1H+N/mVcd4FniOPWaGYoSG15iift4cAO/ -CynMFUbl5NYCuE/z9lR8o/3KSu7vuffLsvXdkxCX6fjxkSWcBKgH7ts7OWyPv9H1 -r/I295CoG9ZmeKVtScY7lamb+vOw9ryHbTACo0aprPQ1kCjr+3JIJdodNkRQvzZX -Ayxmc/zWSmPlJ7zjVkmoLaU7YmN7dPaVpQiELQGKhm/TyH++ZxoA4Rw4dwtqqk86 -+F5ncsqJ107IW7ce6lnZVEvUBD4DHkMRQQZOA9hWBxVeDznjXzfpNNTB07mtzArG -nrbbnNu3epUPthZlhQ8C+dZeBOfGzyr3Aj6CQqKMziiL2Tf4Coa7PhHRBs6rf1PD -xNhnnybCvaMJEMSyX6b/lqb967yVI6g3TXQvi0cGGvYmwEBOiKkXSRHtQBjC1Ocq -qUjzg1dvyfJu84S0kSt2oEHL5n1TAvIrwqNNOwS6CL0x2pSLOVhZmpummSqybvsF -YJjctDJvBA7URB9asMOK3CS6UsJaVzUFkybxaYIdUPylh1mJAjMEEAEKAB0WIQSr -olKVmPZibEINM1ti9J50fZEbYAUCXBE1mgAKCRBi9J50fZEbYEcVEACOTG1qO0m/ -+8T2S8rskKDrgoXMi22x3n4SqdKIA5TwWdWp18nVyXIxUWvI1cS73WupHNtEKTLc -+yObvNo1N3syj/5c14RcRLUcWTFKs596TcUP5/xNH33j0nFplKplBP4MegnduXsB -HibxiEycpkTFVxc3xbW9KeWSzqEHxxOXE1okL0SDWTj/oNRToaDc4zdm26veZd25 -ycxqRkksZZCPuczqb2SB/mDqHx1jl4z2B6CzN3OUzMk40a77xwZXKNGTO4+fMEOJ -Flch8YQXh+gPbS1F/Q7qCrQOkhoV3nI/0CxNgWNcPrUd52xtGHzgxbdrgT7L0XMO -/KmIu1O8E+znjOxcSAklwh1xLsT01193vbVyW2pcmmtqo1ku0taLlw4T7VHQNb88 -uOKucXlA10L2lFFnqBWLOuZDcVpgywMjIrKTPoEpDcVPaBUDQCFBZE9ogA/Edhlo -mxGxhtzG/O6wwFcLoleMH1Lf6zMxhwOAIvkWVjsuQ312uVy1RNY7b3UFrxOw8/qq -UBy6AFE/dp9PF8BIQ37NHKeAlvCexEedwJi4RwH0hUQkBhxBeNrTOEE7cCaZ9Shz -IWhPKxSRKKblYY4fpDzl2uMBwdetk9jfZF2ofoSOKXTVh+YJ8PzncD6xJVesbMIW -0aPkERdmz8JeGBclBR0miED+zidofWCgD7kCDQRaBREYARAAqiqhYIA3ci/sJ7y3 -mJaQ/lsL2nsy+RgW52ETpLp3tIO2r3rxNn7CB/kJhPimDIo4OJSV2bl3Sr2llgwX -PrBQ+Z5bCUV70uc1U0vvJEW/r9tkyOu3YV7VXWXtaQWkCgxIqWgNJvU5A/9/6vz9 -u1RdMZwxpjy/4HuWvHYRXlJmeeca/BEoaYWMRlECuJjIBcAzuVJTlKBT7x7U4Ptc -qqZGbzr0+zU39y1kMXu/ayldlsF3k6DKYZYNaa8cKNqorV0FqBVm1JZSjiAAWqGp -tmYxUmv/riY6cP28tP3G6noH1XqzEvZ3fdYIsGM29YQ1Y1vrVrrBVju/aMzss498 -czxMtp8e0sudHt+ommUDkA2WBEPuqJPIcOj+7bvFiv6smyxcU8VmsyEapknq+Dq8 -wG0w3fGsRdy8puc5COz/3xuiFlHQ97wtnnmyWbmdQmx7EfZcGWFfnK6HwEXAbcjO -aaFwSISK8ROgqoKfTss6/8Go+vbmtKJQH2w1fQArnPHGu9qFM/sBNhZ+ieiZ6x1H -CdU3qvuycFZMSsMhk4ER2vJdeJ8tu2jUhMOIuA/VUgUblCJkAaBE9wXaiibCZ/XT -XBXVb81v+EpLsoc5G/wrg35D5U/Gqqc+KAABK2zHa4L7rIs6jb2daeRrUBytsWm2 -Exq5sE1Uf5mioHtZpbr6rKIGzT0AEQEAAYkCNgQYAQgAIBYhBGcsZXvga0swlpxK -V0YUScJeNrmOBQJaBREYAhsMAAoJEEYUScJeNrmOb2oQALYcLV3wFFR5v9zpEPdS -haOIpYyuFBkN0FoID+w7Hb7R3pyl7c6nLI9tyFEkJBM1faGke8vKj6HZSfcyX1Lo -2rBL+yW7Gu8z3uEbkTnPFew9LnutGFuFTnbpVdLcpsbm2lG5yhdmjvJBKI4CfX4Z -UFlhyGtwqsl+1lpUgvOuMI2HjyHcFbzkhiSRDQvtXCgJu6orjzEvqiKNM4MM7PMJ -AwU0Lf3NV/p1H2mFllfotmXVZ/TjXuGcOYH56gcf4XpkuD5Vb2Qhu7IbR6TneC5j -yPdC0yQYcXqrpYhNBmlbXIoEL1m0xXhrFVPxS3QeMfkhQOqjvhaxBGCt29YJaTfQ -ugN7I1YfEJIxTap8xzEdJ+80YL3iNCIzaWSsd/xUKpobHSsu4RU1cv//S+5qD3WZ -NfcUoBgmfPC7NXCoKrEVXk5QKh3efKnAkMQrxdWRiwSuenf4Yk4fWXcTyCXsMPVB -qjcZRuOpow7tU9AuBoMyJ1XrznHoubdnc29iGN51Hrhvp/uNxjsCgPgQtpL/8znk -dgfzXU5CYJDYHa6fubUTHVZfLKbzBEI2XY1nqVu+QEO86tkY9Ef4PFMknThTAJDC -ph3xIx/sBb5s3c/XH9JgWEiyO3rMEzZecgF34OJgwnc5gl63a4k1cF0cxzkCZYi3 -k6XI/RkkRzdN1CSdCapbDJDvuQINBFoFEeUBEAChZUqlI7FLQIY6GEo0bhJ4oMp2 -jQi22zb9ZmqqcmRbWfNKfCfm/cXNDabccqzPRTWezq6hVYYPz6cSnzXpxPBIQufZ -IoMVLKDbTS0RTFVwQsYu9qGdZ52J2bq6qMWK0I2n6lECNkbOB0bZ3aPxe3yw4McP -6u+SU+b0ArMvIGqq1cmKSpkAQB0kBK/gGzEj26d30jMSN393BZ/ESEs7PZyaie3O -CdT71Cmh6xNxv0IwmgbUo54diXL9hEYTrI3hPyCKFeAoiTjlpz9ah7DPoOHgd9lD -Rd4a6VdMrdz7m5aFWo/NVuoty9spGYLG0p9N7zSaUAdO/96mn+W18hbL7EkU7/Db -Ubt5ZP34YOI46aI8YRZKiTq6NI4WglZDxu9PFGoCx4lyvhgKOwcQHySverAyb0Y1 -qeNCL9uk6oBHB2bXlAhBBOORtL5rGD+ICCuCV4g1ZEoN7sJBMxNMXORzRZ1crdlr -10lld/Mg0udl2Hgatfx+i+Y0ae/W0Ibr417H5q7iHr85ivTQ6mRU3hMuzQSoWZK8 -vixjvOK401Gre22q5jq1IPinACcu6VUto9Wbo8C1msSsWgHrqLRFeqp18BoIVY5s -QCvcsGlyD7MdJQohpmJ7al/kNVOidhGf7TtcSolWF7gLZacMRYbGWhbDhpOIhIpl -jiWTg8oWRl9KPbwzBQARAQABiQRyBBgBCAAmFiEEZyxle+BrSzCWnEpXRhRJwl42 -uY4FAloFEeUCGwIFCQlmAYACQAkQRhRJwl42uY7BdCAEGQEIAB0WIQRxHyjVEOHg -vL1fa/6UNugL+6RpCQUCWgUR5QAKCRCUNugL+6RpCSgsD/40XzObgPRpbIRQaJL1 -FgynrXUh3dJHdqB5Yi/pYshFuI+nnjpAGTyYyk75WlfvUmzY4HgNmh9yCjWketc0 -SdulPkWQ093Y38bQ9WGVQ7NLnZ47AUTuImqEdKcR4wu9F3nGD+cyNWE5fao62tYd -hlzrP1rLz8kALtswc9PVYLEKnqNCBtlGoWdeW7K1lYVG4666/uYvHzOzsUQ0MqVT -HDjpvxEcVRA0EW47m2TVj6IYAsM+0J93aFRr4OKXf4bu1ejxRz4Pdx73QsjeZwlN -5F4FpnmegdUbNR3azeGcF0qiOjPCNu3xi5lDFPKCRZLnCAqMsvv92Z/GWryNAuDj -H9tsmbDUwYXc1QUbdsu+p2jVm79yPgJUIvcy/kwOd0/GYUDOme2NvhF252aOO6Mt -OnTCrQoX0mIY/IisIjwi+2LEpQVyNDu7AGu581LYFGhBDUqiy5CyQ2neHS+k9iq2 -06dVdqETpiybizUZm2aQ8FlRV0j6PVKrqAzi0cMYJC+Gh/fNvx61goJ1tEDdh+LK -Mw0Js7OCtH7Wu1D0U/qDl3137PIBSv10BZ3SkbZDqivV5YhyGhvEewiXsbamE6VZ -AHGZ5pfd/0tkqAW9UQqw1AdqYBsAtE4yeU63xPcz7B4VyyIdRNxnjQiEg+SEpDyy -Gl2kGtt+cIbEYZovTrrW2cM0FzGhD/4rRIDfd+IvhZ86BbYoIv4oreiZVjIhFAYI -7e0DfVliBXNOHFErghu3FisUrfTM5g7RHA0Snk8OGO/Yu2mSXYKVvygIlfi3i+7B -0eZxhZEOsHXgO3v4WtY5/67Q1XXF9J7MY9Ke9gqp0E8HRFsECfEoSCRdaaic5PIT -veUEkHs6q6W+J5ULNTqdWsmSdgNWQh3Zbhh0Ih9m9nioAlZHaKnEZXGt8GsUimr7 -ffRuYgxF+kuWT8UwQu0Tc47QrYgZIpxH4WI6Rc6qKAo/4DLK2Q3Y15kJFqi8He0t -U7fWXMtrdQxxkz94WTFokISVVRZxSfZ8VkGjVHAgk6NVBgp+2zjiwfwS16qbOUOY -ikR3WTCbyStdePLaXgAFxA7g/pl5/f0IF3/IoGdTGjWoRqnBZG7NfP7bYF1CKe4f -a87Z47LriyL70BFosJqBNMJUEorS9w8sBbnmMUdpGMyk7PH386W95ib7AEOtRttL -uzYetY4LljxgMsloRgYX+Kg5i6fkntG6rod8LNYg7jWObWaIqlPoTo1RNoujYAnE -qdCDQHoUOgtZ4v6+QaxI3WV1KPBsPb7SAjuphubIQVK/6qHse9OoWVwWAABXHFqX -2qV4dyq6mq87ohTcRrZqt64ekD8H3Qe4xkYSzsWZTc0qovhs+G+dSTJ709xuV2EP -+YMbPW0/IQ== -=g11H ------END PGP PUBLIC KEY BLOCK----- - -pub rsa4096 2019-05-15 [SC] - F1261BDE929012C8FF2E501D6EA5D7598529A53E -uid [ultimate] Martin Hickey -sig 3 6EA5D7598529A53E 2019-05-15 Martin Hickey -sub rsa4096 2019-05-15 [E] -sig 6EA5D7598529A53E 2019-05-15 Martin Hickey - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBFzcLlgBEACsmjtsbfMuKiKBl3yV5FsQBxvmNyhIwUJMtjgm5CMFcOLD+jDw -mExfsE8sM5fqfS5P7NFHn3V6NY/GyKNH3DZHGhYwDw/vG6JfHo1s9IzhjySuWEtL -7GUCJBKXk2cDfk4p0lHRgEtoYjG/sRMgk3y7WTR/W0McxllcrQQBB3RREbz8y7r7 -atJCeec36SSZgXqsyXAESx5dx7qRTdIwObPTCGxBdj2ZkgzT3D35EExdi9I8oM6L -bYOyUPy0aEj/FX6HVBOIWNGB0z8TYXjwY6/3gJG1JhaFZK1zvYogJ3p8jO07bTwo -/AzYAG4NoV4TqTyFPmb0d0+wE+lZOWA3FfF0YtYnNe3KPmPJZ/TXdTO6kle24UTy -Q9GK2s8QB3V9NA09/YoSF1qdjRfL5jo7XnRJztfFgIqW118I4EKSF+kz3hCMxH1Y -iCvHIHFQs+WX6g1bXHDI8JWe7VDiCVYwMxap8o/vtEKoETH9fjOEO/f/YF68hqpX -7eYTacDEV72qikHz/O0hNyeS1m/AnavPrd5RQi53vOT/KhwM+wC4a1bAywQUDZDW -KkSEkTqjzcSryj3DJR6EZ9y4F11Kt4TZoxHvh59UCcVyaTZPl/YdcRWom6eGo/5U -K1MFeF7fTK9ZVuJnvG6av2/W7Sbz9KaJxLHhUNAQ+ytdVkN9xfXrx1HP7QARAQAB -tChNYXJ0aW4gSGlja2V5IDxtYXJ0aW4uaGlja2V5QGllLmlibS5jb20+iQJOBBMB -CgA4FiEE8SYb3pKQEsj/LlAdbqXXWYUppT4FAlzcLlgCGwMFCwkIBwIGFQoJCAsC -BBYCAwECHgECF4AACgkQbqXXWYUppT5IFA//b64QqKN/ookqqeKEUMUOMoZUTi2t -4HPtzX/nqOXDb0zyIyaJaJlgxz+LuoN8CrSrwnmTY/ibKsFS7xkFRIeKYSb9b2no -NPb8F0SVtxYFQJ8d4WU1snAWFJd8aMe3+z8w15Mqz1Sd1lS/sN5s101rbh8jtFZD -NnAZqyfUgIhVq243XfhP4/mHPinpXjjF+APlMbdsOqnWgxzp8E9hpCd/YLb6KY0j -JbwryzH52ha9ZDMdMipH557+Xutcl4Wyn8RsJy38J0qBvy2p8AMZIYotw6pSCedi -7Iva+EitGSXXgRWbR6O68JvUgrFDOjcPKSQy7AlwhTase+b4OA9c3DgSxR5SMBR6 -OLYaIuDeVY2Zjr0ydFdxrfQzlHget7axRH0aaMimyCNfRa3HJea8ffF/Ssv2meUF -IPIhYLn7SBrVoTISu38S6WkhBBkDiHAW7nqV+mWR3cnVjIzIjW56bI06NZ4kqtvk -D9TX7b+KV20cSjjbSGI70023oHFoJSpLsj9+otvPwNrYC2oD0qTLBfNMkpcktnnw -I2uynQrPNbQVeA+cKrECJeyl2yAC4WXvP4ZefvFZX6RnL9HiiZ+pDyBt6Yq3A9AA -NhRd8zEAKNwH88tFmWMinTzCZz04bKvql+E7A3MAaR8WS3BG3JfLXMqOKiMfCHr5 -4Gn3rD4UGtFfxoy5Ag0EXNwuWAEQAKuxVJDOjG+xuaaO2Z/6BQfTaz6/zgzql/pR -UHInKSt5ts2LGdRhfvsNBzGBhoneLWZ8PivHRGSZFsFj5Nzy9/DIkopdHSZhP/zB -aqihHgFJTKxKBfrhP60bYQGBkHNMVwqbFuck24DUCzrMyJXG15f252aY7ByCIIem -SHbmPww5q6HPEPS+hHE4ka4N4s+vqL+oK8ktq7lnZCX+AZ4jIuMAoh/C851hLcr5 -EK+a6tXa2yRJtJfj44GX6+nBVm2w+3eHqOpD7JM7NqWmo41+qg3t2J3zHQf/0ejP -ej+OcVdEBD5zlJL+CNZ9PCMBUOrb+IbqY3ybmJieipOJtOCY8nwUyCueyTmq1tso -OwUsGB9hIsVY11wNgoNgrA6PhExGxcM5S/0Rt4+y/pwFjnqYLXBXyBSjXzzmpjhn -zERjmANlI8QLKHDdShgboDUt3Ynw+D/peTS9iJMIPuUTrcGcKgw4+6FNKACnJ5l7 -Wvz7apgD8QmxnSZMquul23bGihhbQMITWvdF5KEHE06Ah1bOzB3KXBEVx00Y0tO/ -hsY8XH4T/pEKv9FsIF6R4o2k/xm6jR9eZutABVIrizMHkZzjjo1ZC8b15olrZvLa -/DtNHzV5nPPSvGZPcey9BYk6b5GGCfT/EiWtJz8Nxm7/cCYRvuuZnGCxriH6XPww -v8kPNihfABEBAAGJAjYEGAEKACAWIQTxJhvekpASyP8uUB1upddZhSmlPgUCXNwu -WAIbDAAKCRBupddZhSmlPikmD/9UrspSeSjwaXSj2vCpO1pWm6ryVQc2ZzyMnXvq -j5HLwzaVsN8HM/YADK5FL6qqhxrROOZdSHjS92sxk2Rab23gGRKbwDUJmerheZ4B -ZXG40fDOPv45PZ8V0Kn9bzliNpPBFPjoaI8X1AKoIXyUqEy98Y/zhnLDhW/+yPrO -gznPfO5ds75+u4xOx9pTfGpdwt6qhfCdNHUoZWsAw/6pafqrCIvbHjGvmMJyYENS -dl6sPYBeiDkJkH67sGvJghjedhNznnXJ8+sm701eTqZkmpxzc0jvzwgnnYb0rAzS -uU3QNj9w5HcGQd/pk29Ui8A4VWLJOUcDCVa/CIQMQqQDPYJKxaj7XgE+dQ9MxQ3a -O0wgpEo2+4BaZ4I/qP8CgaE9q4IopMhNKPR1IeEFUmTsIzLVAktS/InshFWWUp5e -mEss8kiqxU9bAGZvWopllCaPJQTDZElQpW84Z0afyVLPp47CoKcXBSMsITFt3mRf -ZXAA6h8UlSgC7FV1YT4p6qsHqQ3cLERdTSrQFLmaCb2yRCR2V9d0RiMaIwUmnbld -g1jeR4weO3LLghuWpfZHruDrDU2ZvOAObQIQdHBFmCHejA/gilf0MUdJ1h2gApuJ -m3MUub704EDCTSqz9LJc+4/NbA2esZj7mExCtsMEqaoHW7BU4ws6BRHTyeHgi+Le -1qneNQ== -=oCPv ------END PGP PUBLIC KEY BLOCK----- - -pub rsa4096 2018-03-14 [SC] - 967F8AC5E2216F9F4FD270AD92AA783CBAAE8E3B -uid [ultimate] Matthew Fisher -sig 3 92AA783CBAAE8E3B 2018-03-14 Matthew Fisher -sub rsa4096 2018-03-14 [E] -sig 92AA783CBAAE8E3B 2018-03-14 Matthew Fisher - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBFqpgxYBEAC1+yf/KFw2AQlineurz7Oz8NyYMlx1JnxZvMOFrL6jbZGyyzyy -jBX5Ii++79Wq1T3BL+F/UFhgruQbbzL8SiAc8Q55Ec7z/BVxM7iQPLCnFRqztllx -Ia1D1dZ9aFIw4P92kQgOQGPOgIxFRwEPA0ZX5nbZfL/teNhphW7vHaauk9xEJddm -Pyy3l9xCRIKQVMwuCaLeH0ZZpBllddwuRV4ptlQ30MpOnaalQda9/j3VhNFEX8Nj -nu8GHn+f4Lzy6XmhHb++JB3AIo5ZfwaUS2xMrnObtvmGHR3+uP/kblh9MzZlmL4T -ldclyGaV7z9Z/xGwnX/+r7xna/fr3mey3GXm29BOP2sUBBQCba05X5nYUd2TjWsZ -OZtE6sLuzUzeOTLEDu28IJoiaYnLKDNzDmuVM26xAYVWXUdCGgn+1rAp0t5OGgHm -qTexvPmckgp3yw+tcPUkR6nh0ft7pmeoK53AQHMt6fk7plZCTuu5UvxZE/oDzt4X -w9+vSTD5GzsNGrTYLTYUSL0muK+iM/uuJtFNJUREOucXfmWxulUsxwOB0st7hnLs -4JmFSr3av1en1WqqdiXswOrdK2msTm4J2+fsOU1jnyF//RJmj+1KPpRDCBTzpAFS -SzE/rRaLZBVE8k2vT0L6yBXvGJ2ONK9TkGT5fnyXu8zDu1d2Koj0c+6m9wARAQAB -tCpNYXR0aGV3IEZpc2hlciA8bWF0dC5maXNoZXJAbWljcm9zb2Z0LmNvbT6JAk4E -EwEIADgWIQSWf4rF4iFvn0/ScK2Sqng8uq6OOwUCWqmDFgIbAwULCQgHAgYVCgkI -CwIEFgIDAQIeAQIXgAAKCRCSqng8uq6OOyTsD/979LDS7ONHIHNoRf7Uud40To0S -/domtZM0rXUCBdbe5R4/xah0HvM1u8aN4OC6U7i0LCXSmEOZxQLKxKBWfX4/d6k7 -lBwuQBSlcM6cM6nDfPInT0C3o8caP8lOGeNAdOkMxrqiEO4gHNP5BvWCV+jQSU5X -uvGhKNTMcpaf+DqZAFbR6zpdL7t5JCK0B0RRhFfaGWb19t3REukI5OF5M5SN7EtQ -XWK/1fyzsltrjTSXgMWuxtJjBchltjme/S3XpHeeoSCm1WWh3a140tCC662ydU1u -EZIlUrn8dfMpH0BY6bb0/4dhHvCJ3bw+zZoCzFJM/LksjP5i+Q4mUOD8PvFWh5aS -46F827YiMdqD/eDMr1QRe66fPw5EtWTHgnf3PX+NmN8lgn2o280AkRXqkrCgl580 -B+lFwZ6hfan2F8RIHXNbF+9Zvc7Nh8bG8s4I8s6uiufmsmOuFdp47J4//q1W0HcU -0fqajDnEhExtGkgwIsum1Ndwq2sWZT/ko7PYyC3J6mbr/MXTvd2TxtnMgG6kpyPv -p3HlDaBw1aO5vO5mji4RTsoZi12MITIyvPsFWh0WtXkJLNaJ30bFSEx5fiJILxu0 -bBoBK0LUhB1Q+8G3Kea3+q3MuOQFnFfjPlMH6q84jpU5Lv5BaW17IeZ2kIfVYrcG -vBvtZ5VHDzY4EhGmlbkCDQRaqYMWARAA3wYv6jbE1PjXwIUWSSO9zxQLBKg7Cn7d -g+wwKx+N5DHjSdQBous6DGwN/wEZfXJOn14S9Yg4p4owmiyJDn0oqJ0BLdsMELoO -imCIZ+zn3AjCWdk2b0oCOhyTwhaVhVgi8yMQruMSUG9/3lkVoFae/GMC32nmE2A0 -BOnj9fVIhIrDKt9OSeTXXRNVaRvNFo9ry8S1hDxgfQ2unD6J0mMPhLH2O7CRZDFW -FyH09E/rhrIDvI3Z7mZw2ufGKR0YEu7fJ0BBBSbIqUOMsUnQNWomb2j/QZyYmhTS -Hg9YRB807H3b+5GuZim+DSUk5DQV2IENEg9LDYvhDftE5COYB3tZUnvEpOvNybBl -URxD8Kgqlb3j93l2FcD1QrIGW5VCmkkuD612ZG+NjMq0ZXlQjv6gxAYir8GTKkWt -tS1OatDm6qe6xEFypT6nlvxOYFxLeFkVVGt4H4QW6+MXvnwMofL0G6fOhRvdlq3R -US9n3WqzTpCwfvJs2lhYi+c3/2nwCx5G42OT9Ix0UFkYwxhGk6PRleKOMsw28PFr -a8DVjyKGOVn+9auVhPXYQcN0sZqFl8LBDkUtaniiRD4WKH91aKYgmX1qo8sJZMhx -t/ZoHOfoHDEEa+kLqfsWu3htyTP1gleCAA8kDcRiy1v/G8v3+p2ioI6q1qegigbr -AqTHcWNOltcAEQEAAYkCNgQYAQgAIBYhBJZ/isXiIW+fT9JwrZKqeDy6ro47BQJa -qYMWAhsMAAoJEJKqeDy6ro47T7gP/j/3R9hPg+kJCErlEKPqxsEOxxlaHx+f4UGg -Zm+P6QK2SrqbrqcPhoKUXeHlbCMm2euxKTonIawgCIr44kCZvp3B8pCGUCR+M0mf -aXGO1O6EJ3MmtlbXJ+OyBAhxpklUWdM6favuzi62fAmvwEKQf1reG/9r+toJb5N4 -KwrrdZNUaLJWhb6D0fwB+1fWJbdRnDO1rozcA+YJGhhunpxF2b2nZ5OtqNuGmbqV -ofxL6/0lM4HqLNcUBlUyQihjk1+hzfWji95SlzIxP2EhH6gJh/e+/EDCaVVV00CM -0n/0dEB25nAuSMGgUx2utNmfCUP84IErGzSUlXdzN20aW5xiBFU3/uSWyz80IGuy -WeyRzksmphGdLwef+sWLKGrOJh+DkOxxpFMRaIqGEG2YViQCg3gyzjiJuI/XAdlK -AhqwVKfRke24vgifd1tN+zeFs+m28Hpw7989vky1hDvqdpK5/fiJfqIBsF0jir/H -AgtqmbiqemX9rUa3uDkBsvyu+Ou41l+wL6ahj9Pnu0+9hQnpeZERIyhq4LWn7gGb -xk5y63wrvGbeS5lev//012oSzWQfSdFWqQVzMTVtOojGFWgvwRCwZiWEPQkRIV5r -VNXtXPUdKiOEkWin01ZrwDPEyBjr3pcnu2mbgLeJETODnCRi79KA5kCtd65JbNF7 -Qknjx8fW -=jz9T ------END PGP PUBLIC KEY BLOCK----- - -pub rsa4096 2018-08-06 [SC] [expires: 2022-08-06] - 76939899B137D575D3274E756DCCB9D752D35BA8 -uid [ultimate] Taylor Thomas -sig 3 6DCCB9D752D35BA8 2018-08-06 Taylor Thomas -sub rsa4096 2018-08-06 [E] [expires: 2022-08-06] -sig 6DCCB9D752D35BA8 2018-08-06 Taylor Thomas - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBFto3pMBEADAO8mWocOAqBUHtiLBnht3+vLnjLv1LNs2GBdMCDRza51/SzFN -NN5pAETGbFl11zxpm9rBkyjI2xVO4OqI8TNIn6vYPTh2YVBs9UB+qRqjJt94fm9C -tWdQ3/27I4PPrCIw5CxjLKst/GO0BjS/J228wP1JtUeyf/QH9K8hDFeov0y94IMM -s7NFRkqZJ6tXjlDCJnDkPm3wERgY3S2I8bgr/BlGFEWCmjqD75PqHuJYjh4mmXhk -KTeYcJh42INPzCXd3bnvF0NwfmAE70fsSOZz7H3Ox14Gs+Tn+jDC8+Or4CCaqtyE -276d8yyyDXBlDN9IjwhjlJPfx/zMtvD+lAkGV89NwbZ+YnyUNenK2V6H86Efe36t -MxvFCH7rOKjCNjKUE0NUbxXfYig5u6xuZKcBcJmjXbmL2dFUIaMzm8jf1NlLuzjw -k7IVAw2Y9ZcVO1eNgeVxI+NdRsdz8qgBmDTvRhxh2n/ppc+5DDVhiffGqqlIZmYN -NJ2bUhW0x0R2OHgOedMyKnYDGgXPI3hnmY/t48ErDwxTqVNoo2tVU1YWS3eP63oa -8ZAiNsvYVWFWIAUi1Q+ADAhj1GdISg4VU5N97joFZA/POZrRtS43OBDIvCpaKjF/ -bu2EltTVBdGZj6fW8xndZIum9cIHMlMi+gyq/o9kipFyZ7zVEM0SB8aI6QARAQAB -tCtUYXlsb3IgVGhvbWFzIDx0YXlsb3IudGhvbWFzQG1pY3Jvc29mdC5jb20+iQJU -BBMBCAA+FiEEdpOYmbE31XXTJ051bcy511LTW6gFAlto3pMCGwMFCQeGH4AFCwkI -BwIGFQoJCAsCBBYCAwECHgECF4AACgkQbcy511LTW6gkqw/9E/DZMckYjml9gN6f -Z7jyZSzO9zP2pVKvcPvaXU+kcyKPR6r6seYt4uSOdosSsZs/xF7aSPoMezDyNli+ -W0t27DCXtnbk+LYptw6AaevkUF9+Cxe/gfXSQDxU6jtOV00KM4WkJtJ7Zty1dvk3 -PsnpPhbxUAWwULy0wF9Ab9RAXMyz/7TrgWP70EY1G/KkETUHTdSkxaoUPs67F9Y8 -c5qVQjgFVqSeN90h58w/4SF7KkS4EOy7RRyfzaBuyQPPi3fOtvsfAY/cSOVn2PBF -Pj1RPoTREKEa0nnp9TtrlwP7v+ooIvwDeemjL1c6tlTBW67T6UM+W5hcvjegQg1h -uLOdRtiN1HlTvOZngtegvbegGviwpdXahrNxN2mtYCAAYNELNyQOAWERGF9TUKeb -OC1HLbZwXdmPiUlUfPN3aAnMH46qe7eSMAZK203ciZlUxowFuE01X+M3WmLESdP3 -dxv3TACiC55mGBgZm/d/1CK83KBWMlzbgfmop65xbxi/tmpJbYdqoTeidYtUDo+L -IzJVjagvfED49o/U86C5DBr7u0mhZqnAxaWEWRBRgFi1Bnl7w3zSYYhdwGjiYTJ9 -/hejac8iqWc+RC9AJh4HW6itB3jPoEI90aVb1y8hm3UOBQTMEnI+dpvZEQPWSBnd -tWzzQS7et8Tlq0J4/wRVcEXAlmq5Ag0EW2jekwEQAK3KxoH8N7Qc0vSkMQmo/NfO -lEE89/KobYLDvyQMfXQJGF143eaaW2IHcE6OIT6E9IX9vnt00Lfzm0Jwdd3ur5xf -l3GJ9r0riYVNzQ/9kMx4JkoXJ+kgaL2kVTykKERkHUvgRcLkgqVZWMMGz2sUNqYE -XUBtEnYVsZmxQNE8X41NI26XP/1e9jctn/FgAPXMtkLrXRfRIlKzyQTz7zCX1WnK -xeMjFjHkdBD4dP2ohoEkk2y5lDI1hlPginVPZHPShawxd+TE0Vre5i0Wp6Y+lKWL -QtsZKefTbqBDDJlwXmUZc+eu5jEuB7qKDcwH1s8vtZhRVaKFr0kPHEZ3Ka+ODWmt -/v6Zhh/tFkxq0mgh5OHycN3GvwMKp/fEBkRl9pM4RuHrf41XB4+/Joi+CottP5WC -1So8ydbJnG2lYknHxPMrISvACDMLPwWp9IFpr1u4nPkkuAlCuzUkev9r3dJQNq3G -hsTqpQgkHQTd5+QBabE1543gWOCjz9ap80RnIlSfylskR5fWy8U2XnOo7kb5TtnC -hZtJfjpCxHW894VEx3oe8tHaUUdmwrcK3g1DSu5KrtWn976g9tOlcpmfTjqNYEJq -93jD1aCb8yx+LHk5wypQhLpn76AE6YCJebQVxmc0AsqFOlTTRU6yPQF46qYOZD/+ -spUs4bXbxmLghLTO/VmrABEBAAGJAjwEGAEIACYWIQR2k5iZsTfVddMnTnVtzLnX -UtNbqAUCW2jekwIbDAUJB4YfgAAKCRBtzLnXUtNbqE4lEAC+uIwA9vkHHpucTLBq -UiwI4agcY9D0iOGohO7qJQ44MitIsiIqG3Qn1Wps1sdGdoxmFtTE8W0tMhZ+XpTd -ZkL3G8EIhB9gyuel3H1L2vD/6YX3P9Vv4JlcpNDjc/c2i/U+/05kBMwtrgmjB/3T -W1368I9uzfAS3SPYDUsx6nNv6iHhDYDEGOOuBWv5VDvrnYcBbysxkevB6SDZrs5d -7fpmALMsUt8le/y9sn5TFH2CB3aKHGJHMv0RxV5iEXwq7jHPeRJCamzKTCx8/et+ -8wn8Wudk+FiqrSH72BaRb9j7n7KoBuBQB30IbbocRNwGHJHsmuyThGBBZh9Z37Qm -r1qoSNRl+ZJy6QoAO6DVPS6FERDDXYPwrHiC8EbomblcVMYjfI9/Ln+rSB30/OtC -4t+v83v1TPerc1FCXJc2lISs1KLlJnPh5Ykq6IffH9nUALmo5tK5FUDUUhOAxFhe -wCE4fJI+yNIcMHotk5XSxbeSUVFaXDb4Pue/9DjQjnF5iSQGnbveEmGUaXxncjf2 -cJgcNZjd9P4XKqb1hNKpFwgm47dr3TH1/KmkFlfeBK4S/GpVsipWiB9vX4RC28EB -QP4bc5To+ohqwuOLw6hRo0YLf15jTJknCDtfsgKQ6uiR7ai+z6fqoH3kycCCcsPc -Y2/8LdVLydI6o8cZJDEpEexPaA== -=vtJm ------END PGP PUBLIC KEY BLOCK----- - -pub rsa4096/0x1EF612347F8A9958 2016-07-25 [SC] - Key fingerprint = 49D0 9C86 C3DC 8DA3 F0A0 7622 1EF6 1234 7F8A 9958 -uid [ultimate] Adam Reese -sig 3 0x1EF612347F8A9958 2018-01-02 Adam Reese -sig 3 0x1EF612347F8A9958 2016-07-25 Adam Reese -sig 0x62F49E747D911B60 2018-12-12 Matt Butcher -sig 0x461449C25E36B98E 2018-12-12 Matthew Farina -sig 0x2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein -uid [ultimate] Adam Reese -sig 3 0x1EF612347F8A9958 2018-01-02 Adam Reese -sig 3 0x1EF612347F8A9958 2016-07-25 Adam Reese -sig 0x62F49E747D911B60 2018-12-12 Matt Butcher -sig 0x461449C25E36B98E 2018-12-12 Matthew Farina -sig 0x2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein -uid [ultimate] Adam Reese -sig 3 0x1EF612347F8A9958 2018-01-02 Adam Reese -sig 3 0x1EF612347F8A9958 2016-07-25 Adam Reese -sig 0x62F49E747D911B60 2018-12-12 Matt Butcher -sig 0x461449C25E36B98E 2018-12-12 Matthew Farina -sig 0x2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein -sub rsa2048/0x21DD8DC880EBB474 2016-07-25 [E] [expires: 2024-07-23] -sig 0x1EF612347F8A9958 2016-07-25 Adam Reese -sub rsa2048/0x06F35E60A7A18DD6 2016-07-25 [SA] [expires: 2024-07-23] -sig 0x1EF612347F8A9958 2016-07-25 Adam Reese -sub rsa4096/0x2970B7F911395FDE 2018-01-09 [A] -sig 0x1EF612347F8A9958 2018-01-09 Adam Reese - ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQINBFeWdukBEAC/j4xe/59W2CYAzXBgh0kuhdI4t9B/4CzYxWgpCqNqXN/IfBHn -JUSiTKdfwU9+cNfcviDdV/UjyxbWxyvX5Zm/4Ik6XhbK7y+Cl/35TBt6d1MVNr+n -DPeS/uJKNtb27/NwCdihGzWL8UQ0Aah3Y7EZfpy3KSTNfSfEY35XbJTHGlFMGarW -nVArY387C64XNIO+n41NJRnLDzZbFJMv/Eq/psXLumAaav5+PuOelrfaWGNpke9C -AgV7DoyFcK8mTRwISqIjrV9S6ENqzUFu+VcqeOw8bzNnYDwdNY0kgBQpvfiKpnzd -yhYjFeu+OdT+sM5sXUgmM9IdB4wAbpZ2dM8uWjGe7WPSj1B5t3Bp6DtcIHl2ICcv -lpjjrXXKwv1wdnhnUKjlS8NPjO/XGzTUnkqiO9fvbVrMEh9CRCrzn1OuZaH9RQZq -vFBIp2XfEaFaUdvPSDNyDE+Ax1V3+cCVX1+mIIYrS7lK8X3DoXhBZbuREnxvK2X1 -hzw5Ye4GlAw5WeNJNusHmGtKvhayLi7xYjqsTAN/kAcyHm7d3xXBHYsasTpX5Bc4 -MW1nnTjFZzX/r+cOZELWnwAmkponf5PmBVefWRGvhhUtsoF+aw91pme1PF4S3QZW -orre/udNUF3JEbMHhstGlATUMvtLyFtdR3WH7ol2IEVCIGJmI5L6Bj4ylQARAQAB -tBpBZGFtIFJlZXNlIDxhZGFtQHJlZXNlLmlvPokCUQQTAQoAOwIbAwIeAQIXgAIZ -ARYhBEnQnIbD3I2j8KB2Ih72EjR/iplYBQJaS/jbBQsJCAcDBRUKCQgLBRYCAwEA -AAoJEB72EjR/iplYja4P/2eJs1aaS72z5FbdTktxX1/Jj9fFaniBVWakcUTZigOH -pq2oJWUnziLmUOI5sE89WsEt5tmhGCF9b4105nIPG4BVaLAvuiPBF69n/7eNxMh/ -5DZnpooPLwaT3w5m6Fqkouaqs3nWBTJ92Ramph9G/j3rmrf3lPrD3xXF8fXlIk+w -r5n2mdoJvvoezwTIts6iUAFf/hCOecmtOF2yc0Tjzqb2lsu+9OHOgID960cQmzEq -xSJrDsXGdDPkOjTQx2faEmd6jMFzImaqkGj+Ry+rq8yzlHaQeor4aeIAGncZDjmM -hYUXnO0ZITqVfvfm6Gu/c3NyNe4+0SpTWwTKxLv/Od3jtFMvmf1pIjNhcfAdCH5/ -HY8jxl58TL5BcmDK2tzpz1Tc9aa3hICPl3hFbwRDRbFZ/bEOdCjhAhPUmaAl8ia4 -H/XimRzqsr748G5ZP9gkSC42/3nvGgGNZQVmwedw6rOaA9EdWqv3FPE+l1ssbosB -VAmMnaP3M7iXt+ijA6vLeRG478q4rWt63uDYDswJDJv1AXAKjEzBsB13B2JqfN0G -m8HY2vWkaAuEta4fHRgf5hLJtPaJLjHeZ0s/c44KFKqkew3PVyaOnUG6WCpUrfjD -FTh+j/LMKxnz0CLpIj/xbSsfnJgbeNism7YeeEQcvM9z76mRMvDL6G33X9n1Wdwv -iQI0BBMBCgAeBQJXlnbpAhsDAwsJBwMVCggCHgECF4ADFgIBAhkBAAoJEB72EjR/ -iplYejcP/2BgJMc+vugdd/WkDJJj4TVskbn/VWvEp0aO/2ztADMW0uKs8DeRZFVk -eWbueBobrzWP2Cg3HN282E3lsQHqPOI5VS9wvbVj1NSesH/OcOcc2ukimHZAjg7g -cLaECJkXbjuzvKtFDVHRtzWFyJMRPPdrXcY7fzPV8bcr76VeJSz2klK7SI6xCDJz -fbclnyE3ctLVWd5Jmm31xT76u5WgCX+RA6wH7mxET3rEHaSXI66TFzmL97tnM8Ke -jCl2qRpOJpoUbZhhIaYa5BE7nRmPrwQ77za6JvuF7gxV1WyFkuwOGgKGx9zziyOG -Grmp7qZnxpfWXmBSFXdhiWUvWD1PvWT75QZluGXN2hVJEb6f3HAaK7q8y/2QPBWP -1ttnJ2lGpDfEtZCA+RUv6CfuADPF2B2pMyyWC54jT7QPfokgl6tQPotlyiGmiLup -Kml6hd7afS6QKHFeyZYpVVk2CCWXsiFw6qk9OEGgP9eyNQcKtXZhnpql63YUjvxH -HbTt/7OLlgbyN6AmWLRtVpb9onLEskhWJ86yeaYIQoSEP4qNZNBoekMMg+NM6QeF -CEfRPtmvG9X9kSEbeLazyV4xzw2SGjNbmQCSExGr5e2pqKYiEjlHiAXQ+OaVHkcG -0c43snOrCiD4c/rU6UQdPy8QMwjutoHWa5pe5hk/S5HjncmBwHfhiQIzBBABCgAd -FiEEq6JSlZj2YmxCDTNbYvSedH2RG2AFAlwRPcIACgkQYvSedH2RG2Cyag//bZFS -TnCa2WuTB7hWWaatEFdFZx/OoWlzwVsjh+WjAOsJa0TMRGI6VTIPYyLapuEY7+Ii -xL72wsAdjinnhcsBTAydcyx7RJGGhMSiWYRMVP6a+rlUAQJ/YmC0dB3HREMP7aEa -/Qgu1r05RpcLEDpzLsmbMmj7qA6Ugh5tuV7tVvHyQye/7jYADCguDRWC0C09lfz6 -lmQYEnFNo3V1meSxyPFTwp/S4gCf/sc3UTWSGTd8DE8lsQW30m7R1+zH3bw5jsiG -GZMgkpszNpWtVB6zc14csv8okl2tBwFTklauayIVdOprXWvGSiOPGUH2QLWa86CZ -9Wh295EHkHfB+dzB4Qn0m2QBde+r7Hhuve/OVD98oVqVbGa0E3wyX+7EdZY6TUY7 -XIXkQBqlHsdYFHNRiXT9A/mpZh2lXOd4/0m7aT6/Z+J3aV61Pq+azTLsdTs9FFIe -bhAjAjE0gTX7No5+wRVeRDA5zR0ZCEqoBKPx5HKSzxt6rlqbeMsg87fDflDAphAq -52D4CIxEtWJ8YbnUYvPG+emoY9hNh4x+teCwmHL0LHksQrg7bvGJxsh4driKef7f -LItrNLgFC5we2u4KwmPYA3kXcCTtelkzkbNoYE7cHtLyfCeP5l03VNjyx5X/RRec -7SW1hdfx0xLujD+HfPx62sfd1ml+Qv2/Ib2NUdyJAjMEEAEIAB0WIQRnLGV74GtL -MJacSldGFEnCXja5jgUCXBE99gAKCRBGFEnCXja5jrqvEADQAzvSRqjrGeDmx2h3 -S/aF5lLrFC9LhyFaFO7WRh+6hyAIPRKIICCHH+Or3mAxaQ5mi+7tF4s9UrtRu5FT -1gBDSu8hqGCVo0spCmbilQ9gVx6dRMjSS1UykiMWcNxksHhrzDF4hLSlhVYGUwkJ -JQekDcgNXrpnXF11GUt1nr59MSTfvtGb/9vgMkLC+uQeyJtLlx8E9VvppKc3pNKV -xYv682woSHy0TjOyzgA0MIpDMPcozR+E7h72pLNU7z5KfmxVlnJCBU6w8HlZ1ftc -OT3TpA5q3OBhYpz1xpXUA0ZQuRsApOOssvKLFonpu03zeZCTAq1Zhsq0Q0N7zUly -uPNKIB2drq7CAJ1z7R8tJ2Ouc7R2yDbuVK8M/xjgWBlOsz84cYlwFpAzlvboOhTs -ORn0rS+i3Ng8ZaN2yG23oOmXKOpHeKBG39iigAa+vvsxxOCzo66cMfpse/spas5K -OLBtS2gh1n8uXetGqrslXd0puXOyg9T8nI3z61QmPT6zVk8I/Q3Otcui50PfpA8Y -I6B6Lb3vBC/weA5D1ryLA0qNW85z3lpzCZ2c+6rAP92cVA4KR6BB1znAzK/Cf+Bb -4iy2bt2Wl94zJ5YvyAjv0KAhMde4i7z0+AJrM4BQlULSjupn62NT7nAMuYZbATFg -jVzlIE0SLwOapcBsfblii9Zly4kCMwQQAQgAHRYhBFER2nPfEtjoEspGLyzbv7s3 -roIqBQJcEUHmAAoJECzbv7s3roIqbnEP/11fn6f5lF6zsNpB/JF6sbsPrAD/bL8+ -QxIFdPQK/acmP5SeExGh7xj9nZvnzKAm4XTSbHTyZ5WNwEq7Vgk3JY0v07suxrvf -udGgTStmdZl+d01k4NJ11BGYBj0SQ4DG75Egl/57FsrSH6i5vjz6eqR/DJHxMfCt -Ws2SCbKb4aQGlXTPiXFfgGtyFLWEo+iVmySEpEtrn8m5Gm4eeVtg7IUh1DU5KiFK -xRkVOkdC/kyWAY3ig+HzbsVM8Xn3Q3S1ES7qusf+iuJoK4VJQ/HisFdBK3fOxgjo -y8C3m/XFpX11wZ7nJfJz2mIauhoasz/EAvaaczRjVbvmg3Wpm1ogiaxmn7JnS6S2 -0GIeFj0pJudNrDngn591URE1G32kzPaAmOEYeUMP/myjNsSYjlElEemAWO37O5zV -WFmKIcwysdPHnXJ9NjiVDOnpO/t7Xv4ZesJ88+Q/4wY/ESgkZDeA0yHMa33eSCte -SyDv+s1psYbOI7LcTo7ONbf47C1YNEA/Qil/WTcFTCiys9WibDe0KP/aoW0okWxn -hOxQUZV6ZNwUQ64pIpmkWWMV6jYJPotcc6NozQtqkBr/ukMx9KMGozJPfo3Rt51a -xc/oChdnNbhXnOSbdKy1xRo1BzTUR3uELJngLnBbanvA6y0koq3Q2vc23f/oFtv2 -gXwudV/k3ZqltBxBZGFtIFJlZXNlIDxhcmVlc2VAZGVpcy5jb20+iQJOBBMBCgA4 -AhsDAh4BAheAFiEESdCchsPcjaPwoHYiHvYSNH+KmVgFAlpL+NsFCwkIBwMFFQoJ -CAsFFgIDAQAACgkQHvYSNH+KmVip6g/+Px4J3cY58C+XXpnseL8cySMmDBD++pkD -gxaB1OdR09L03Iy27gCXDYBsGUu4x4iPvhEAq064uMKjYp6L/nhbHhvtoziBWL5m -Gd+RJVEzIaW2a2HDlIZ8fuzLiFjWbHz3URKYqjbT9TP3lMTHkBacx3HZ8M+9yUdI -ppsqhPu1xgD4jDXXioLeojca/vMlTo3dkZ9zSjAhqEQRDMzN7Xp1ZzPs+uCjEJHG -09y6/CvPH4gCEIl7Fo+m+vcLhMpRQypWTkyXVPghbrOvVkWpO2zCRFkmQeeDDldL -x5LfxHhMxQ6nMpvX+ecEWa427Jq6stRplNU3MXCFrQ40nP7ZTniNPJw8BfpBgRi9 -FubFpa308y3gYdluYDV3H61SL9hF/3XzguwB//kK4ULCF6Aa8cyFYjqB/cosrLs2 -U+325fY9eZOjCzykRIpINyexh727AAIqPto2J7jnjIhIywiYj0ivfvg84aoYYUqu -kUAPIBHAH/Em+vOYoGwsVMwrhG8U/rBr/VsLIGDC0qCh/AhVt2pvgZT7OQc3CHz2 -wb4NwmShF7ySaSqBJQ3FdRfC7bun5NSZX3hKNXMhppWxtRJU5PjDtg2Y1syb3+IY -5S2gtEAlGFLjnEfYeIBF8RTmdty5ovQLu120JYmu/tCN0EY8HniuIqkI6aqG2V58 -bFuOtoQVXEeJAjEEEwEKABsFAleWdukCGwMDCwkHAxUKCAIeAQIXgAMWAgEACgkQ -HvYSNH+KmVhLSA//a6F3PD6IzElQMTPwGG5RoeRhmAb6dee6xEJe12MBeZlHOvBF -DE5PAfUPoIWVvoaSLPwVIMoEJDpzQ9MyHpne1I+Zy1o9S4dUZ/c3W7rlH4a6e9lK -zcATK++k6FpWWIZ1Ff5ta9uGpxjQu9ojTixojzM4V46MCn7JxfvFiKGvGeXDHHYl -InZKSEmzYOODZzxcYT/U9C6mWADEmMx4M2xgv3UFMAotecXAqIW5/uRZ1h8Xh/eR -ULBGp9MSvnfxD665BqCJHNLh9/G+xr9Vi0ic239nqRUia+zI9tvO2JE4PxC82btk -m7kRNCo67dDg6flCYv/37IWc11RwYo9sKp2S3mZqQoFi5L2JKwZy7tzI9B4/Hrwl -NG0GlYwsiLpRMOWEwMixuLEp6vXRDriu63xKTNbhgtJrGS1FkmCeDuP1f1eRdCIH -PIwy/zQxfVqVWNnWzDs6esxi59L+nUv1soRDGstfAWfzN9wzpwQbmy6EqetgMuBl -F65+6f3LGcZ0Dp7tNM6M82/vvk78JOORJU8WigSQZAX4AB+NB8Z+MwpTmX/bG+Gb -FtcCYxd/zHVyxWUJfembfd8fGJtY39oI8vCdYNe2Jq94wohYmxE6Koan+4CkoAwx -7/67+VWSZwMOJcJsaMZP4qOMLnkrmImlF66AYe2Wm0oKsLibhEFBpSP38CGJAjME -EAEKAB0WIQSrolKVmPZibEINM1ti9J50fZEbYAUCXBE9wgAKCRBi9J50fZEbYBQn -D/403RTgmZMx2pkGFHdVrQFmqXoIXOScO5x8XS8OjFugjycYT6aNeHjQwVllKHLf -+Ig5saTkXoiKS03T61GUXuwPwLVTzkeeDME96dPqo+k83H+D4MifEtDxF1dZQi70 -sZPw+ITlzZOmpyQxDJa+rTbewvM+ULoXs6GNl+jxPpMlKcCpu2OwQn98SOibfDmj -HNCYiF+Gj1VM+xg5MB+ROkLzDFDpUux2M8fJZv+fgiTfcnWL93lWxaKhBlg4ZFC4 -KkdnB4wVyazEucsQQgpsJamjK7y7jslfkVZUwOJvpuqKYLDt5yITUXx5PyqrAwJe -0824AudafjAptcNYRtv51tSIeCw1mxAsiNBwIJmEW9JYwDED0SOH8OQJfWgMEIZc -4Zepa51s3kKYgdh1fkguIrsSERnaUOq0qHlhywOad1rElduWmPWti1mumCDna4gc -1ZH/YR+HyUp7ELF7sJvGQH3bNB5jziHtmqz6+nmH2XxRTY3Mhvkhp9ow5pWxyD6Q -xkXXdPw6a5ZNGTEmmDbi2FEykJyFTXvTsFPvSKAGSXeaUqGX514hK0ZamTfLLyH1 -xdEg3BUNI2jtgFfB6BlneCDlUppNzulfhAR3AgJmIiGImTG1l79nqEgk7O0g3uMC -JL61Vrjn4pFuBU/SG7Rx4WcnMXBfUo+caUQQbnP1QUGK7IkCMwQQAQgAHRYhBGcs -ZXvga0swlpxKV0YUScJeNrmOBQJcET32AAoJEEYUScJeNrmOZRkQALfTx1/VoQDN -MIIwxDW0vku9DLD8AciUs6V3B+IA2ISwbraHHji6kUEoVUMSCnTEIHJVr8L0oeMF -/87o7yUvYgUtWOt416icqGlpA1dGtQLLffyUNv3eCjW9db8+snLZUHsGkeLCowBI -eb8fcPMkNmNNW4YQxIs/di6spV6rCqR+PpuqsiyAHYHOl1z2RpSUA1wUt5oVcVrf -36so+m2gXwtnzx9Z331AlXhSrULPwD+lvd35+gEWpXw2SwD6Rd427URvAUWI7ai6 -eP78OWi2PlAAFbqEuMrwNvYC79hxwB29vEJgO2V3f95UA3d2bJU17pvK+/nYpPWA -uuqLDN3Ydqkoa++5HoqIdeh9uW4oiiGnMAkvH24012GpiXN37T45cH8LnNj0XooN -tYuiY/ZoCTw4gawtMwSWSl/htDrkLQKiUSiKdZqBLVXO2wRSjLjFPJEkq+eIBBcc -hvzj2C4a0sZhk4W1gHESeeB8D6IblgMm4oLa8Fn+4YhiwvbK9Dgja/7iiICP8bbO -3/9smCWOnnsixho5Jkd5IXWF0+tcfHVR/l+M8bCf+c02IGE/mD7RMFcxv3jdFYU7 -/HsbwU9fCDSCAgXszwM+232kALGeur0riRJ42X1RNOzh82cF1wzYyxHR8JZMtcIt -x2MWc/n2wJO3swXnItKI1Vy4fjRu2PcJiQIzBBABCAAdFiEEURHac98S2OgSykYv -LNu/uzeugioFAlwRQeYACgkQLNu/uzeugiotag//YwAVNMHNLmOeAzSOzEf6Z6yK -2WDgEhsUt9Ykhy2pSc2vUD7jIXSAPTJYI7yY1flDmOe3kEVXXeVcPYAli9Ii5Eq4 -DYJBC0FGboMbzdwh8P8RZGnhusB9MSlXYi2DnWH+oKGS9dQFnhpzn/lm0nl8tpL3 -FrnwhlshNpgYYqIa29yO1EHskiFVLD6pL1W/DM6lMFlmTMjRb+y8eyZtbpCIdrY7 -uhDRVwvJPYegj34KR+8OMo1iDvbckee5AR1Dc8L44KmB3Nm9AgW9o+bEz/kYz5pz -Eyv9ibthtagBpxU8kyfSuwH1Z3X3qzgpq8QVNVH0Y/5+sWAOEi2NkCHR2z4W757f -HO16Xz5SZQ5jBrqNFm6u7CLy0X96Sr5FRcefZl/gjqlgNqNTt/iJP48nfKvrEkn2 -1OlFKRFitEC3QOWDTeI4uDFgS/OIUQq7AgqAaxERR3/kbAaVh2R71AsYuXyD57dW -Eo9PJkle9gSMvWUqRC8/0eSR4zgrwirkmXNQVEj55l2Z5y1kWv95NB1kozJb4aJW -TboHAHzklAMK1Gw8AMkjsA5PZyXQGkM+kXzUvE4TLC3qnsr5w05yJT2teOaWGK3s -3ZOtu2WuvuOXo0qd7oBzkF9850LQ83wEuwIfc7XcbaB0pyb6B3EtyZ+pMlRrzREg -EiX+bB98Q3qhNopjIDy0I0FkYW0gUmVlc2UgPGFyZWVzZWRlc2lnbkBnbWFpbC5j -b20+iQJOBBMBCgA4AhsDAh4BAheAFiEESdCchsPcjaPwoHYiHvYSNH+KmVgFAlpL -+NsFCwkIBwMFFQoJCAsFFgIDAQAACgkQHvYSNH+KmVh/AA//YVA5eJBbCQQKp1IA -VWf1vqLdE13hxlw4MZOf4+2119l8RHKwS/mio9ZfmtoTHLqgiDFPEARQZQf5fjmr -Vl4QqZbOzlhbU1bFCE0i2I4Lypj5TAY2j6WRKwc11mKYmWM7gayMjvKvPrL9s+nH -sFC8foAkYC3nBeHR26AooLUjOi+jKD554vLKWRxgHMwS54s/U+n3OejxTF87Wdi9 -fB/65tlTw0vt2lrAf6LUaKjKj4sef771TMgXYuJijkbvzP4ShrezBPAdWabf13du -EK+O1FVURkTZYSpOB0etDyDV6DXD5amz9NNO/N+bfV0/2dNY0Ez3cjko8WuhQRu2 -Q2PrJ5CLRN88KLgrDu8lFLmrQY14OrnWaQb3zVA8LMPhg1jUtXGB71zbhmM92BPU -rRE+zO8cWOq1ERCw7GesQ6LwsKTI+ceXBmWqH0woxbBQlE0A2RhUam2LS7/etBuw -VLBqXM3/rGYeIWL8j1fx7yF7xnrly+7BBR46B1rdGPupmRG8U+xu6H5qnHgl5VQK -j63XHRyP8jew4ZkUSU1+4ueoruWDLTxa85DDpYKux/+8NQyhlfBxw2BJOegzBEF9 -0ddcRg5jkH/rdFXz7lFoZe9oE/wVzPMmzLqroKvwDI3krNuTL1QB+j5fAWl6+N5W -y3afspmmDA0ql1+6Cmr6UhLh6C+JAjEEEwEKABsFAleWdukCGwMDCwkHAxUKCAIe -AQIXgAMWAgEACgkQHvYSNH+KmVhpXRAAlauaug8H776O0qOVX7njKwyHUoJS4Ddj -PUA0XzmFjrLrC4CylxQ5zVnQWi2QAh1FEDVrTWX889kkbPPo+9RK82bkdwMP8+GN -Bv+Vu2SnJX4haDXooyT1BsmKvN5ypm/G4Xc0oWFwCXFJDxYtEhKKq25PRtP/KS89 -HOqvsD2SDfK2xpufXR6zyvCeXRwQX3iiyq8tR566aXpUg1mDcCtJpb1HGk4M/LO9 -9Ph1aOoHaqSAB85MK61rnYFNqRGZB3Ge91j3Xp188YZW6WFmC+YzdAB0+qGfWHLO -mT8HmI1X2mPuHdRtYk3AYYVgSSLJDwMdpvYoethPUiOGLraDQSufdEkAcMwuU2n+ -NuRbejtssInsdJuI9ug6hvbkDkj8gD+khPnvg/epSuOGGWckM6SOwkel5lYRH4pk -+Qu3zGj0k0mcOBucvQMpzGJfSac4bhj5TNOAyAMMjYGCQpRaJh3ZhI7mUfix2ex2 -+d7xru/amMjTZ7WQ5kpz1EQN7aeXOgtNRZQy9G93dw+cZ7WBJT1MQ0KwhITs0KGG -b078Z+nwuKVeTDPGxNaYNcYPFDjmfEEZ1khLrD0hT62qOjkO8KdfNcgfEn/xwb/q -eQoHvT1y5iIyu66DGiuFU0Kwbtq46/5rgT4EgZKX9D/j5oywMTtkOCalnM14bSkg -vD2WUX+zMbCJAjMEEAEKAB0WIQSrolKVmPZibEINM1ti9J50fZEbYAUCXBE9wgAK -CRBi9J50fZEbYBVcEACat/K5p4dxhimNvLfUNRMz6t5mW1P0nMeLPQ9R0thp7FAX -NIHRGyaoT7Kn4EISw0j2Y1icsAgg0G4tx00jIrwnFh3olK1bbUXeIgq9v3OR6rv1 -rW68C9KMMtsg+IPrv310MWqhxh1+yfiQFUFbLLTMUaZBXUCRYYt02vIbM06NNf1z -mXaBef98KB8PGpYZ9QAhF2yDHVPgSyIJs2cUamiyEyeJhuXuullbrV5m5XhdxY7N -bgseDGuQcmx7gPmaVJmlYUurFy8N1amodSnAthWyfINUGu42SszDqDagz2XF9R/I -Eq+4/noOdktyHq9bPGzSwTcdFoEpji9ufiT69TXYSZG+oH2kBCkhIX+Pt5w68phD -D7uK04N9CdNLUhEUQdZHXm+NWv5GGbXjEzkpZ4raXVe/i4hBDRL4ayVmDkbfVWtx -FBSyMV3LX7Rcr2rSFKBv9Yo2yBQMx/V/tYMeE9i369Z7jhslEsJc/4tFtLtCp8ck -il9j/Sj5KfYVYxzzl2g1OWDGTcpX9AO8W+T72iSbF2d12lSxa6XQJIumCZk9A0MF -WbNTVK3rbmreFwo9q/1xIcu6QakiICqUSnBkU6yM3V4AR6v4Dco9xtJ4f8DHZ0c9 -+MrS1LOw212EQo4TR/fflBg7hPVhAd+4LwZvjaa/Pn8Om+eUyZjucSiWuVroBokC -MwQQAQgAHRYhBGcsZXvga0swlpxKV0YUScJeNrmOBQJcET32AAoJEEYUScJeNrmO -M38QALMXs9/RAJwnZbwqyZBPI18Zmih+k/2OiryfOCfC9J5kE7dHx+MeSr4AVi0Q -rACXG1kLuvieXSq+kVw85NRqGWufEEXyK4730YNFFaBUH3KIBUc/zyZcIBLUlnkg -Gj/lzI0ZKxysEp4gMjPsXPVSAl3aRcUPofbjoNz7HQP4E3Lhy7XzOj+up9bhqquL -i1QKoOYddhrTKnXyONtM0VmJpYMgefVqR2CExJ/8XsNEknYpHbpynU7KpziJ1OYG -xacP44r5T1B1YeEQFfrtumMNPbsdKU9RnMo8AUcUnYE6DlrMNb+FWefuuRNg0qie -hfBO7eIqNHWwaEmlAw80FVa2HoHk7EALDpo5Lp78V/0CHRwdNgIoxDHM46AMvLqq -iFQ9wMsTHqVqWHLFAfDxgfjM9pWuxXk8R5+8KHyHQ+dY/tYrNBrqu0QV90pGN1k6 -sk7UI8B10SgqOwzOiddthiq62wmUuKWGNq3mepgAldPVJAfpFN2tEBx6/H/UUwBE -nH6t8NQcHjZw4zh3g2BRq6Ze7vk2YLlCRTKTOBWpfv8qu5DXz66V0/GcQVGC4LIF -Wtjh0mckHdSRME1JJQdMcSO3+qlE0EOOhPpB/aIVERyju2lXQbXXh8uRMkaDBlJo -HPgqpRwQ3ThHbiL3WkpGzCjod6lBxUZLauYZ21pl4X302sz7iQIzBBABCAAdFiEE -URHac98S2OgSykYvLNu/uzeugioFAlwRQeYACgkQLNu/uzeugioEHQ//XPCaFz0K -N5TJXF0/3s+2ufTYFXeHc9G7EEBfMk1kv/pObFgXx3H7V85XUyMUrj/BBEG96y6R -aKcsbkySGhL+l5meymPSrRGY5xMw7hYGrvzpNq99VT3msH+j/Mqz3in4EmgXev/b -7ZBrEVN74M46294//QiWSRaTO8bfKpS3kEixShJQcy4gRDkvjl+FgMxevjWsH9Bf -0y7pY3A4TFgMDqCd5R4Ptf+D8wrY9Tc4Hc+BM6DPfg8b11QeXFlAdBqW2tlwmnuW -U/joLeFXwwsQa0Dlg/vveGVfO4KoBMcsfFxQ3XleKIRH/mcSuQFf016MDhI5bZYP -T7SvkPK0sVkmJt3wGJmuJiTM6HEvMyjGSXYfAHJxePNetQS6oI5A9bw24NPTTHm8 -sPrEd5hIPLZ9kx9y3MwsTjx+/AZ67u4/BrPsFzNdyDp31aKT+g8vP3YTgESs92cy -vzNGNgJp5grvtDHc/lqe7rQWJYCO6uf9SnuWYQpAW7jnI6rMXctFFDCLwVFH5VGM -cbq7CjBbQ/fY9fREiWl+TeKQSBr7DV+ssqRxUfzZSYWRnZaDajRQS041qCFDyUhj -A26P04hT2n1x641ytvO1wvFa8of76Dos1USMeUFV3eQicY98C4p4sxEBCUmIBaOk -rTgaEDezUt63yR66Uc3p7PsjDaFwjsALKny5AQ0EV5Z26QEIAL1rcALBlQxGsY5Q -RhIvi351MeZsK0A4hrDQp7pFFjbqlA52UUkkDuyl8/1zES8ITe+l48F3NiDDGS5s -q6A9ubHCMCjz/NIHL9bTsb/7wyQNRBO+nuqBBvZg80LsWT8b/jg2fLXghIbWrg+w -r2UcxAV+ObOkVC+rnkxWrbHCnss+e3oEsgkO+8VWpROoRFMsGTf7lqOwgTaYYxe8 -VGo5y8OiMIPJdFDysp3VHu8lnGJZbix2awsJUqyEd+OKqYNKqfY43PCFpVW2m7pp -A85UvwdGVEDSy1iymjjZKHyWXb7emKweBhWFKbL7kpNSkwqV8qutGLfdO/jf6+4r -xRtwBkkAEQEAAYkDRAQYAQoADwUCV5Z26QUJDwmcAAIbDAEpCRAe9hI0f4qZWMBd -IAQZAQoABgUCV5Z26QAKCRAh3Y3IgOu0dN9iCACXC+h3mueHUFTmkNUG0c4OqemT -RCmaXIbt46kBnzYXx0AsHeoZEYXWW62Sl8auHfaL8zPpOEFwBCY0HCVDQ+joWPJo -EnHvPZs5DusNnVNkCfy/T7ClkTW8py95tIUfz1aJxcM8q6cXCQuCR1DciK/t1hi2 -c5NOIVHmQGZ4k/o49iEdgq3lZB7EumKxMYItQk6WMl3kX/7Nr9B1oc4SZ/7hhEn4 -rWA33Qvld1qeZmm7lUZGZP9y9U9I6AoJARHwvF3hvFjOvI0O7L4LxU75ee3W3vJJ -1ZkPzwwLBY3T6m9CIaqOOtxeQg0dlfRBX6DVpOB9ogNnFYwwmc1HX55FKc5J5bMQ -AJKy+Gs61XNZalag+l9huvilhiUxffg3nijjLcF0Gj9p7JJrqlG2MODTpLBABYul -+yckitJOU8MaIznVOIBTH7IfBtqzS8RxNiAZnpEWi8KhXV6U8nqhz7r62iPGTa8X -8DpHWLcIJyS79CagsN8XkJRKG7d8R4wBHvv4oumvyTk6C44Uxg/+pX10hV39Ct/r -BEnt6aiIdbkxfDSdEub703l8SBOjaPeXnpAAPcvY/f3h6f/pGfYFqCdr+vvRBf0k -Z+DpWXRAYwbl4G7sexffwlYpC3cxLM7ZyntD2srC1XXGY5fGfSQNhDb3PsHCbbOb -jhM0vksTgCE3D+4JUx3FciNSuZMcL5oGP7TxehjJGJOQT4ehUQg8B00KAeYKdase -p1AwECB7G0SvEMUqjPkFpWSjArZ57BDui8I8ZvpGNTVfZWGgzMeh/E6611yhxfus -dki8YND/u9WfjAQ2scMUCi3/7DpzDLP68cp2UGuGXRMs+I5cvwYKdlWbz1r1Rydm -2eShFsZE7SnUwlEeaypm4IZGUcbmLJYK/qX4lFsJ4oa6VdfSUPx7dUUGUbjqBgyc -q2gdHSkDsnY9xRmIThE7UarDVeA5GqM/QVXB+xxG8tjabUV7HV4YLURdVKDa4Gdp -1+bpKSEHugsBBXfgpTl/UnloW9VbhyvjYWtUTsWm0tgBuQENBFeWdukBCAC4LXGN -UKmFNwyk612coxLXln38Ezqr9BkD4SWPeD0uFEKyBlrTndQUlfGq+2eEmvxGzeY/ -ElPSgm9+xQSiWEaPRxFfJ6J5gzbVJAOJZJ45KLkfKokoj/Ao0wLA1GwqJx86kmUL -akR8zSZAv2XgT5Y0gE6i5sKmUBPTanJu+QBxi0L7/9W644PdbZmcxoiNszQ3zSVF -WcoZOB7p8r9QxgW3EeDyfzfi+zvXRgI2hCkGvrxOzkgQurgs+EEypVkBcLwYUHWM -woYzI+J+ny95jQpEhSYo9MW/uwGua0PjMpcMDA0ddqaqsc1pSUYOMsaq+Ddfv/EF -+/Fwn4KjdT73XazXABEBAAGJA0QEGAEKAA8FAleWdukFCQ8JnAACGyIBKQkQHvYS -NH+KmVjAXSAEGQEKAAYFAleWdukACgkQBvNeYKehjdZeOwgAtixW73UK6gyyBsvC -PNW2n7HjRc02049cUcHz+s0D+wMa2xpYIN1EPQBTrcpL7mZZeKmxKzYA9vj3RuaW -ocoChTBAmQzinTFT173kV1MpQgbSP0sgS+6/p2tSJ+HxmzzNsV5UMwV61IN8xbFB -N4t+GzWyIh4etBkpUiDjzZg9w9E1pAD9UaNAmNGfv3bt3+A8w5H3KHqMWxfl6/+Q -Urw4j3v86ShJknPeQ3WHsO9J53QottQuWidswvZ3QG7bAZUjbUPwSCcbjllooIKL -M4ZPc//4dEnvFl2FLQeIxWm8B61wNA/BZJAAWd1r6tkztulKgkL60NuvkwiodR7p -pQ1t9rDjEACwzg4ijOl0zN/TE1XxgRaf9avhvQ0mVcqU8Hp2OKFjesdYMsgroXtd -0KN4S00QJJhTpdgT7MMRCZATzPw5jzdnqjxJoJuwYzaszMTqKGPnFJdBnPQutyiX -T4gp56u0wH6CmrPFwYHKq6NNGr3bPuYG/d+pCwt18Zt13KmgEWaEdgDmfylTrnQk -hWzmhAHgCzwn/aJw6sN1GkCfQD0cxdUrAm3Ttt582ImLpBB4tDhPlroHtxw/KTPN -SMCM0pSQ9jompssPvFjYRMExqLsLZAVWrpK0uvrWom0pkWzvjBqXC4EczxpjLepX -1AIi+hHYDzW2MizcTEe5jYUpwAr0N44Cnw80RwIHJM1O3XLQpaVGW91hgLjWp81A -5FmqWPO7Qo+EQtg/zAa7F9ukHGsl+Xa/+Lx6PuoRwOV2sKfFfJ+7xolvwFLta7Lf -Hu55PURVcw24CMCCyQPcOOoqZEhiAOwNtDq22c1T8x1GyvI9WsZRLT2XGCntDavA -pXkYs9ZKW6OQ5KWKhkw7ocvTF4Aq5fBrv7noWtN9mp4mfMBaOsZRsuaqQoKRqwvK -RJDZ4+wzc/Chy/N3fSa22n7QLxHyFDqBSARBGy4hoXgaf3Zqk3SglTvZK1wkIpyB -hqHZQIYxbE5/KRJuiqcZ//UtmNp/q7FFu/Ytx22lsE8wWHGzZJdavLkCDQRaVQcB -ARAAzA+ZDFUZ739XOAiZGunhUyQ3g68sN19x4M+Qay95ZPFwl3HLgV46WBDY3x87 -DMpvYYJqLOF/tKlzRymm+7QpyLtIWKX5f8TKGKrV0+8vY+h7SyKaRVNbu5HqPDU8 -ViXzMleQxgy6T39HIuHdAPo9ceEOGM+XB0ESpA1eRjeRJGF6dC1Ric8nUZRMnmTw -y8xGugv0n7ET47v22cW8TVs2k/ociPVLCF/Qws1FeJRp0CDbg7YFcbqoD4cV1On5 -SypMRnSmhjm9GI3hw2JNM73XLH1lSuHKKIMtUifaKkpUL0RP+Nq+QYAzu8ruUuwX -pEy/WyiuP+qj67rzQOsqRDUUMAVtAr2FH27kECAHDxHlFAB/ukp0/WAh0oT7tX25 -+nM+XcWCoNFRMDhPAAYhlWDyn+iPuCFPdzR5Jgx3hyvgKPDRmrIwhs3VmWEF/dPT -XpCyIbgSSCEF8JOv0h8m3K69tWWTxv5j8j1gVlZ0mVdv55lnqQtybxPoVnFrAznr -g/30+vsyoh5dH3cc9MteUh0qYRqDH8Q5wc0benZFRwxH9E3tV0P7NhO7h1H9l0Cq -wwyrOPEdnySUD0xdBupC2zoqdjCB8l4RQidryWcPcItSs0J6p79NLqdHStBJZogf -EzfPqL4J0y2Dv4EFQs1LCPlxaLS7TMrjZKdecrsmRHJwofkAEQEAAYkCNgQYAQoA -IBYhBEnQnIbD3I2j8KB2Ih72EjR/iplYBQJaVQcBAhsgAAoJEB72EjR/iplY7AwP -/2APBujg1Q/pXeDxLgxs8eGYV6DpTtAJkOYF15A7cQ/2WcmSJ8GywCpjkVgItqLf -UT/mI01vuJMaQM/aOFQiRHmlfdS7KEYzc2W5zLb/PA6XK8OjELGP2ZgMsTSy8MOm -ILtxxhPlGRaQWI7zEA3YDYfRg+uP10z8KpFlOg4tNdbXaA7RLdz+x/zP75Hv7C1D -9wJMLO0I4fmK4sepGq+Zk/pFpuXRMwjO0eZXLSE6sO5P0YF6HrU8TReXAE7gHuzB -gcKIYF9oNertp4LhplYhrHkN/rg5b/CbRW7+C4jbwszYzQL2S2Hx03TFasp/jTgb -oX5X3ISY0zDw53aE4mcI1nhYPosiY4BQ647C8SkwLZEixb7mS0pW8HdELRIBJPDQ -llursCS2hDZsBPS1PcvZsAkrTscsUADvdryZHqo+TkizO+HO+oRBRqltAPHTiBSl -13Hjd1Bv+wX/hexVe+Ru1i5i6e495nsvFx3S3b/iCpPpmRYXiWBoW2taR1WQz8/r -0OChc/OrJIg6HZ+sTAnoIGFFlc7p0hrf5jKaO6p+LQCHc6IAcKXvYBLxMOK0i6BR -BlA4kJPTfla4LmKRg/T/xow/naen/aM9mQCs7k2UAoeqNZ6IfQ6G5BZ81H9JNvHC -beriLZDBuRy1LJRjBmZEz+UDBgZoR9oz5DOLh8dGVpkt -=HZO9 ------END PGP PUBLIC KEY BLOCK----- -pub rsa4096 2014-05-13 [SCEA] - ABA2529598F6626C420D335B62F49E747D911B60 -uid [ unknown] Matt Butcher -sig 3 62F49E747D911B60 2017-08-11 Matt Butcher -sig 461449C25E36B98E 2018-12-12 Matthew Farina -sig 1EF612347F8A9958 2018-12-12 Adam Reese -sig 2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein -uid [ unknown] technosophos (keybase.io/technosophos) -sig 3 62F49E747D911B60 2016-10-24 Matt Butcher -sig 461449C25E36B98E 2018-12-12 Matthew Farina -sig 1EF612347F8A9958 2018-12-12 Adam Reese -sig 2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein -uid [ unknown] keybase.io/technosophos -sig 3 62F49E747D911B60 2014-05-13 Matt Butcher -sig 461449C25E36B98E 2018-12-12 Matthew Farina -sig 1EF612347F8A9958 2018-12-12 Adam Reese -sig 2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein -sub rsa2048 2014-05-13 [S] [expires: 2022-05-11] -sig 62F49E747D911B60 2014-05-13 Matt Butcher -sub rsa2048 2014-05-13 [E] [expires: 2022-05-11] -sig 62F49E747D911B60 2014-05-13 Matt Butcher - ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: GPGTools - https://gpgtools.org - -mQINBFNyROIBEADL6FVlqQPC2DAZS8RGYs9Kiqpu486QI6070Nq1l950XxUdudkm -dM8TH0FluDkq/RtQQmVHIwBdL4n/pH7EfKTUy4ggYIs9v2VPhMp7DVlRVKIXKoHl -qQu9I2VI3UNM8j+cQkisFgVrzHi93SHxRKRfJM/qPkQYmzsnBRH/2YAodSOmWybf -TZJToPtkRXqPMm+ZAAtfyhwvwPiXfSnB3/0t5K4WCdhQP601l3fifyaZVVF9GX3Z -n54i080HXYhdxr32n8xPi+EDPv7Sh3XuQZ+zmYmSTxZ12mBIZgzwCJH9Uy9XzmE5 -LrZhf/s4mus5VO7ZxqOr/pZ2edzu3Hae9SwVa96kntHK4Oc5Ja6AYK17dibRG7m6 -1AInGbpJ5oJMvm3MwQbxLXtonZuMr3F+ivdBqrnwjpGHiTfeeuBGasx3WIJwBvzv -94rldvEERAc92eMNEW4G+9tK0w2R8SYP4njWZUKM7ngXRPxA5/vRYj8pzpr/uiFi -YkkzOTo5beueqdAyqaV7GOG2bmzt2Lc5PSGXK/Ew8sLAiOV4ug2QysNMw2MdjF7v -ek6Hco8U+Ir5YQnt4B+t9piDg3w45WGdNfAe5roPZtB9yYox6Iy34fo1GmX4qUf7 -3i99UrZ/B+wgCRjHxsqborquMZnX9S0BeQFm2RV/0S2l5A5NT6yHB4B0hwARAQAB -tD90ZWNobm9zb3Bob3MgKGtleWJhc2UuaW8vdGVjaG5vc29waG9zKSA8dGVjaG5v -c29waG9zQGdtYWlsLmNvbT6JAjcEEwEKACEFAlgOZHsCGy8FCwkIBwMFFQoJCAsF -FgIDAQACHgECF4AACgkQYvSedH2RG2DUXhAAtZGIlCFk8GkhxoUFZgcR+AfgKG39 -bcEdDVulGX0r7LpVO3pF0V7KrY/Hz55fVrQjF6UMS6TF4dB/j4U4ylIdv9UUyQUJ -O9bPJwcYLbSLURqA75NeA7XVSHwvbm6hTCcdnwPxvxkfisd/YUN75mlDNeZEEXs/ -/n+2AxlPX8eQt6n/3RlYYGrekle7EUO8IJcqS8jfSloxkUBO201BubU8lg9bmE4W -uiav6Dgqs1V4q6jheGz+c6BD/PYOysQiet+1Ot0GscvIKgW0w60q7ilzzviOK3eg -fiq57W0Oc3GI4ihrfH3ppC3kcJFyAe/zle25QxWHZWfnNZ12ZElK7vIQnl1JzwVb -sICj+y3j2MUkczHtf4KJZe/7lr1G+No1mFYmDu+GZqT/eSmADOF+IrkoCZgR/jMr -O9Kgfh5U9B7spbHGkA7eZvH68FHRxqvXnUgBFS5hE9oQyTR2EmBF+hpx8T4K2uIo -NSCoq5pTD3HNEZqBZ+E1NQCGv1a8YiyLjeI32vljH52pjtfbW06Nfb4rI+/BrMU2 -82gzVHxiN9O5Ba8YmBLbkZYYTW0+9rF5w0brTxRS4IfokNNeanIJ+w7CuUhEyf0O -yOa0DRxUEvHIq6UFibJWzei2dzBIyHovdIQelkmFr2Oq9LDqsBtSZH5quGzeJ2N/ -atK1HR1GK5CNwRaJAjMEEAEIAB0WIQRnLGV74GtLMJacSldGFEnCXja5jgUCXBE3 -0gAKCRBGFEnCXja5jrJlEACarEjVOWmmZlNkHqajs2rEUJzM+qKThr0QMOd8UvYh -ZpC0+IPOXLjwXpKiZ+7sPw8YaGHw5NK36jWPy+cPdoDppZfRYHp+/0cmK4GI3DH4 -9x/jW3yG9g8ckYCKYscrhev3AeD1UwjjiiQhS5m15/TTOLPGtu4kcWyeTcdgFMo+ -sdiD1w81XA2/zCTJptsDw8AIxJEk+rqBP46qy7kPpawCsO+x1f17tleZ+5pZPYCu -G3vuaC9ggcKIp9K2oifH/Qn1YE4G8Dz9KqDsS3Ucg50PR2tpd2nXQCoWatezNxED -tyNblmx29JJFjSMs9nKNdddDmwWHM8+CNBS9mXRs1BttxtWAPmz2Y/9U4wvQ6V0H -NxZd3JUItOqxkoxVavdMQrbRDLgI8qVXA9LXABJaJ9SccOJfAK+zJVSVcOqrGoVK -7jQyBoHsMbbGl4P2EJtFNIUOiAvo+y0cA6oboAYnqgBr3ghOXWa7uiLB2zFhREro -0VoGlqCjbH6JdvjDcC4Vf9mxtVP42605phBmd6OCDXjTmgn+KToRLKd2i8b/eafZ -5djwipOpyHHxIQ5N+qSI1jxh+58P8x7502kMTHzCoAdxnWk2CT67Imggby3xh8IM -jJmvah5NM5a0eFIGZs8HNuhkbtJBuF6WzVoieBbin+O6/7zvNaS3x0ZcYJPZUpWa -dokCMwQQAQoAHRYhBEnQnIbD3I2j8KB2Ih72EjR/iplYBQJcET/bAAoJEB72EjR/ -iplYOrAP/1b4FsE7QxzYgU2ulBkqDGe9eSWPwuqWORwqpYNPy9UNYKrDn7LO4mfT -GKvVozfR2e8YjDNsP9PfqPjk7OenqiWkzDgwAZFKoFxbu0RFtxA+aMNVOG6ks6g/ -LJH3uvKxqaK0oUTntB9YusdS5B7JOcSzDo9uw+2mRyavxs7aitJmcMmrU6GySmGu -t5Nutsr0j1k5vB7lFNu7PYmc/rQyF7UK45+Q5RSzW7lsvudR6VM7qjE+eHfOOB+t -9Kym2siSrCcwsBsrqGtumXksG3KUFubDr6VG7nUX1y0CkZ8FdtdWnsyssuJy/cUz -sGhoZIXhnP8LAvVS2/0g5U+94K3TfFPrlhq8Dgt4EWOr8icL13QY1ZhlQNW861KP -HEOTtUoNJPg7DafrkB377cfwANk8K3iJAMrWK11TR1obr5brMPFvRqeb1OsDeTmf -PGJkm3DePTKydUNvLwd9FwdG9wsoZVGrn7aRQ59OUn5IdAnuZ5Q9eWnEJf03pTNp -sJ/6cH4XCLy7bM1iim6oknLKpUFWRFxOgMKVeFNQO1h1D96u21bYDXnbKyc2vlIw -sBZbKkHsxWr8AzmCOrWb1DTJO5sYTpsBQkQANQt/IUpNbg5eMC7zdyHUpLEyqQ2E -kfrWOoqoTowXv6xZ7Wdd5/OJHwn4PnsKac4ah3tOMzhQYOAgel+/iQIzBBABCAAd -FiEEURHac98S2OgSykYvLNu/uzeugioFAlwRQU4ACgkQLNu/uzeugiqARA//YEMd -eLItDPOCtLlEYJZ9N3VheUA78IER84cena7RDI38Rra7sh5M+msNJJTYH+mXK1B/ -2Y8tIHo870I300vQLLDXXjGDFWuQRDIXgNkVpk8M0msNqtvTps1Pmf7fxpSeI24a -dGwlyz3oCUELp8bXuyY7LTrNMa8LjNSbS5TdCF0xteuMZdDyD03jDO/fz44Oabtr -fdaIrzDRbw42AxnzR8wrhlR55+EFxWizWERqPLxhXYYhcGk0PyGdZUzcP9YJmjiV -h0605ct+ykiIm0RQ5/YGWkKRC8LIRDYW7NB9Hwv62kjw2pSKcOWm9kaGHOjfieCd -XnBvAPv3sAdcfgx5bP44a2Sh7Bsh8BIqrbsAAG+9b07h7IMM6MCFFxd2smNsp74n -gPR8k4GF7vfVvZherYCB3EhPLoudoxf/u0Ock2Ssa31XStZ+jHb6a/keEPFGnygg -opNDfw5BlUsys7wSEDOSTE3cdiE7B0hWxC5Xw80r3SxONk3jPczraSG/EVmKndX9 -quFboecUIXGBbsx79tUolKTMOQrVP7KIM9ltbpvQShy6RYpWa0dKTRuUgMijqiB4 -A1SR5gvVgKs/xzy2Bw9TAH2ayGc/r+mTpwpa6eOhu3NOYhqqkENlZ/IsKnX+dX55 -UvSVexlttUIjxCKjvH61Pmdi8meNhEesjVYnsbS0MWtleWJhc2UuaW8vdGVjaG5v -c29waG9zIDx0ZWNobm9zb3Bob3NAa2V5YmFzZS5pbz6JAi0EEwEKABcFAlNyROIC -Gy8DCwkHAxUKCAIeAQIXgAAKCRBi9J50fZEbYCnkD/0WpXKEaTdXwqy7fm87An1H -H6HcHDR95+Ldu8XgmSZq4nbkDc0wjDdBD5Tp25QSUznzJ4pKO/Wd7l6C4fhqTZn/ -vldDpRXl23bqvRHmWVkXH/EKZxh1y9TnID7Ysy9H9qRVdFm/yjM9EqrD++/vowYW -Sq6ekosXdjTZWuXVBnirnM/MwSZ/3w1tyK+zfbzA5XR/pscPbTO/UuKdmUbwz4yt -QjSQg+awJ2iRko0USvDG1t7PyMdDNfF+gbzp6qdI/NUo+XicRzCtmxfKR88vD5yE -FD0DY/6xl9172XpB3h5aI1jg2LTDLr0IIlO2KHRkqs9piqJuHL8uA870ZMvLJN9g -JryUny7b5PJlmaYDJPc3TmiMUUHTkrcmJq4Knlh7WtrDX8avbc6T8lWOCakn3cNC -X3O7RW37k953fF3GSgv8otDlySANW20fG5bPN2gvElfHi4LFP9hAXESZUDYuOasu -dRUHMkqc9BAMqqgrgrrY9Qmk1aE+udVTcVICRoUoZyBFVzDsRQL+c1zVBk/kJ8oL -e9cqcdpZbkvVDLtPEyA+b4icX41woqiTRfK28BbKCSwSXkqi+vo9pk9Uwy++S7OS -D3EOjZhox+Zi2Ijcpzb++B2mxX5yroRrPWvHrxIsAKs8ogO9undz+rJbqgZr1PoF -rV+wpMe0ckRECvGqEz0BiYkCMwQQAQgAHRYhBGcsZXvga0swlpxKV0YUScJeNrmO -BQJcETfSAAoJEEYUScJeNrmO1T4P/jAUMiKYNqUlYpCV+mvzVwUQWIyPYdgzqO9R -AmvI1ELCDT1BGB9pLeeUwFXQX/+8+7lGAVLynL7FPPVkkatblVIQKFgvL7XmU6gb -o73DpslX6hn+clYeYXUs37XToffVIFVwIQkWusZ+X9BkM2TeV5fgoJ4mhCh8ys5g -RHKuXYnqCIHfPj033GIhSn1DZRecKPWeb07zYZI4SHsBYEM7xfN4eUEXOjIlRXea -O6hS6N3vBTinn7LnHkRDD9mUemTruBtab2F9Nk3+njzpafMb4IprD5+GGdRacGOq -VNWFlZDYyy+3Qv5A7mXBYGCaTtH5Jlz4oEibFXvvVzD3IgwFCvmU1S+UD+9l+u+z -Nk3F4l07BuulhX6Ek55CoI3kbMCovFjPFrXWghT+/XQy6GaEhQmQ12rhUDBBjS2s -29NImvHyBGX/FHY0udt4fF/h5O0eRw7zqmGeen4yOu60cEi9MVesRz+GZcbdXupe -RghrhXhfE6NHcp7ciyK0+Y8f3dpeXVw3na4EOraR4w4ae+SJUt56Sbudqn7S6Kxj -UCKql68VWHfhh1ibdbv1wl8bAHqtSt0FQlG5mUAcN6R/COO6uK07H2rJWtmIiDAG -nhcSyLY+5SjD7LtRYvZr+SP2EWQ8wHopjkkGfvG1gXl5NQ8gaVRzErkuc4S3yHMC -47j+0GsBiQIzBBABCgAdFiEESdCchsPcjaPwoHYiHvYSNH+KmVgFAlwRP9sACgkQ -HvYSNH+KmVhSoRAArTJp7zUs6pp/+JTFfJsRHbqUBP13KAoZCtaV6auJf+MA6mFD -TD0DpVKdKBGjKna+W/qFn/8lpIjxL6YtQ3/W1j+d+uhd2OPb44atpXNuxArpCqoZ -zAyx0ELmgP1YbZ/DIRKv0v0nFsmP4jd14pcclFKGLqh/tK66n3+mOH7zSqltljV0 -9A4evtkI/29/Jj2I31j2rthk+gJmAYiksXVIZb3Hoj4VjFaW0D3/d7Bc5LaUCY2Z -6GXa208UjBfumKRtSWGXDaz4LmxoS3+H3xfnm3APQIryaSc8daBY0BjDwORa2gUB -9rddEtSWbVZvJoIdAa7shLvR+eYubMCOjmHc5cV3rG0AF+5pymOv+Z9pAIj8Uzfs -kXtmIkoXPRfubeb6rNx66fKakgjXqtcGfe0VdYg/VJiheVedPmqBvePFvuUGvROa -TzDdKxKqi+AR3+JALfcue42xbNCTqWW+iercuKz6gpNukfwuDciNMrH+Ggg8K/FL -x/NEQbVTA+IFZyuBtiRv37gDNf+gRK1buA1OJg6rS1US8CE/brOWEhSDXN1wJ9wM -JHtM6xj/Td/L8v7BYOXbq1ffuuXeX7OOa2NF86yTthS+Hx07y6ivaBRIWhf0DA6I -lQkoKtJJ8dgWtzRgH/Dl18nhgjdqhyaQXnBclxv0B8M3tbpeoJYBRTM/7haJAjME -EAEIAB0WIQRREdpz3xLY6BLKRi8s27+7N66CKgUCXBFBTQAKCRAs27+7N66CKqz6 -EACB4UuPAH70NzoHo9utcD9bzMj0PRi3GKh6MMm0CsumM360HfN9RftOrB+Y3mjq -Oyl4onqz4hWKWWQayUsI3T0YiDwtV3zeGkvyKGMB2gZN/duZplHiSj95Jv7HPQRL -kVo8rrEPboI+EdCCOypZIu8K9vfs/fTrsx14dEy85cOqv4J2is28zOapFoR+79gN -pErktx0ftcv7e2fxXQB5sUAa8k64bRNuVoFXz1HH6T+7641DwQutGAEFWug/Ythj -vytNBlcq0bxpzVwC2RAbPrnJdRu8f1XM4jBx9mJz6NfHGvSjEtlAuc53Y9DvJEcZ -uKwrN2NmtJ0dkO81NaU6B6oT9dwTaJ/6hwHq0WNvPeDcoUZxrh0XXyuhjR/p5MoU -/0TeiwA6HByO+/wQRL5ZODUag8xlsnXHMxwz6F/mqo6OirJzflJdJkm9kL4UKjCB -r/jzOmf6WVQEfjWTFmv2empmxT3Z4ahR60DLRCGPlc6v7N7QshbH74b/NfbP7CPt -SNqQwiPzSTiNjr1SZhMFJ/Zu7HS+/ysXyPw6Ku0s+8zQtkstV9+Oo/mpfm27yDih -scWIZTmc3RiZmL6eURA9tijdB7ZNuXxTyKkClUfnkiba9zdBCZT+n52zXY9aR4OE -KEYFXe/x2A2hON02AP/lzgjEpg/3vaSfLrzk0wMeh+yYjLQpTWF0dCBCdXRjaGVy -IDxtYXR0LmJ1dGNoZXJAbWljcm9zb2Z0LmNvbT6JAk4EEwEKADgWIQSrolKVmPZi -bEINM1ti9J50fZEbYAUCWY4aMQIbLwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAK -CRBi9J50fZEbYCtzD/9WqoGpj/rKKoqoToj3hInc3Nv/Nxj9quJc2Z4gxmnwYlB+ -KhZeDlfCytkFFYXgl4bB6KcpnI/OW+hynxR8jT/wIvD5E3wIUCRVJfbdmKBiSha5 -KMDgLGmVpJbVG83s7mN6BlgYPxUa7dFXI43mRBkt9hCnH7U4vwx5rtLlR5FEU6EL -sjYiWc/zyjqZFLHbnlJ8tt09zKTVDF4SfdJz1vpDCD1exY7LZtsaL1SpE+fuTq+5 -/Z6MvMQk4bJcEbXzrIF1U7C7xIoTv/npv+eb0xdiPto1UKs6C1o2rIdvxbrDc8zz -pWMRSPjBaOuey01rFKrkpSlxuX1h6HQSDyN2Q7WmeezLh3RgoTwrEnmy/Qi5Ze21 -pi2ygMtUadxTzZRi/IC77s4FOlrnqx27AonEzRQHTtKXhLKrrXD6HQTerf7W9v/l -24O/QIAdX/JlhYWHQGPHAWe/3o30XkeM/Bhlt29SAnxeWhTo3oa1EudXrAe745eM -rA/pdAHWgqIBi5KZP0j9nQRtxXN/ZP3ASKjs1CKw4OnwpIWotUkK0XgMemJTgBYR -FmNUPpUCiLTiZxfJbcQt3khDOfQ53iR3xSLP788MHO5/zGqKcOgnjFlyc8QLdLSb -db52l79ZaeXamakEEfaZRnR3wtZjWvLk+JnC9nWEVflICnLwKpLoph+ceVe8j4kC -MwQQAQgAHRYhBGcsZXvga0swlpxKV0YUScJeNrmOBQJcETezAAoJEEYUScJeNrmO -ZJgP/2yhVoDQbp6T1ngsl079C3ZwyDY//TfKXUwAJJgHo84IdrLWhYYTCo1/2nm5 -rAqmDlq3OJsUMucwj8opocEIBM2HWcRcwFJgwC3Caq6w0vLzmt9Qm5eGIwSPGH/Q -7w1YQj+6x++xyYuVdmChVIIgQy5TP2cIuM+c+T2Zq2vTGKV6VNKQpVH/o0ymB7zx -5ZSJdsQGuNWDvZbwsVrYsbbgEy72iO7fVvc3aYUVXL1gvJjAh4GUKApsLWhJGG/G -HoYvl0PSTpb++HOGwtwbG+GG4ELbISfyrs4JfMUvRA2hd7MZD5BTvO9hzWeFAOJR -ze5gsDkUCMeJ2D1yoVONHhmaZ8k9xy88p/NOC3iYamoixO6vVkOsCbOhzHRVlj1a -VV4Zs1RZ6kMgm0HBgGFjj4IjWZy39G+JWfjJuLpRAVOwPv3Km2ertTtJJjkSaTA8 -TRG+/ZjgEse+Gio40aeBhm/2LvM4A1oHe/gzZXQZrHKIl7sy9ijQowj1wuqs+oqz -gdjjBp/DcU3qbTo0vJ6ACvRjQVcIhIdOypkn31uhUSA1CtHT3TNau4/D+B7YNtWZ -egeVXWvzFZukXkYzvbDjMn9t3PYHMPKaPYynBGuFPO0fMBXouh4qOfXywFemB+zu -+ccZol3zPzBhdxInOhWi6wjMBSY1zae0S3Co34CSylZ2lu+kiQIzBBABCgAdFiEE -SdCchsPcjaPwoHYiHvYSNH+KmVgFAlwRP9sACgkQHvYSNH+KmViCHw//dGg9ochq -Wuh344h8SSqq7G5d+Hch7EIMCykPlDCkmO0/FsKEmMQ2nMySpkm1zM59pHvADlbu -tbhtLIk8kAjsfyZF2alpTCn1gxRVe/aXBsgmAf/Op2jf92zPkov8bXw7x1oFZ3ew -fWFR8bwG0OEK8kr9jpkCs2lRv7kG6g60ptsCWDkJGiXpEyovUF0W3ZpCU3RBVUIC -D8xMTBJXOiCYtux7uDpGJ9iYBGD0eUWxg5OUZs6Gmid2sr+rV4WIoBJEgGUMq37f -d+loYNwm36GQmU3ytWx3ZduCruNRf5XSdws+nJU0rPb51CiacPp/g9PR/9f9i9/a -yzao/7pA9/0mfiAXHveK39iNqFH4V0B4hOUzRWWWJJNvZw195LridOcmmgOLTWQJ -iWErD0VvzZRrf5vf8sdsRoXx1gHrgb2ana3ThfRl+7gE9jgkrEZxGZBh6eaxyxpc -eTJBtGjcAgATlKSZSrm9ZLI3Jyz+1R+uEj1LPY6rc05c1XU2l6ucoMGRvu/Vs2m6 -XBiyJeqX5yARdVbiTMbmGI39SxoZ4//KJoFjs71+FxT+sZ3syfQyQJjaS8++qB/e -zmhF4Ab2wh987um42q3Bzl3NXnERFO0Rq5R3ksgBb2ns93Sc6WQPV62pUFxzeHX5 -BcW6vVjL8jkrJuMipKetoZGm5Aimf3oydJ+JAjMEEAEIAB0WIQRREdpz3xLY6BLK -Ri8s27+7N66CKgUCXBFBTQAKCRAs27+7N66CKrU0D/9tCR/N64xwcY1eq5tEjFb0 -9T0L/aP39hcCOWeMwD2AcA5qSM1Fr/gqCs18db9JqZOcTEISrStzQ/ciGj3Dsnlz -7LjYVicQjwNK39YxedfAuU1kPAd2k4KujpE8o9b0Q25nsTOth1ZZyXJIIsrywwVS -CG0shyi2GASuZOXIZOdkYI/SIPojZdQKG8Czd37Lo+2mPDqG5lTL+LUX0UoqEFR0 -AJfsBkZhUodUAJSxzT5sn9ZyBOabAbdFhiwjMHTyvFOdFzIfc5pS+/OqQqdhwxcw -/FyCFyN5WgC6nRxEZqvW2jbB7xphLPWOWxWbogwD4QACQO6ih6pyUAvkcPGRTJRS -j9AuJ7cX1iul0hwxjFifoJGnDyNB0oDo7qyfcOQ5lKlYE6BWQiQHcE8UX4pn0Tk8 -z18pSjh79LxFUgKH9Rvv2eIjE0V/GYFh/RiPxopBRGcqnpo4F5mOvLbeNMN/lX2s -3g8rkpVLa/QWf/d+goak/zVWqLmC/5OMTFnEWrcTu6dVycoEiyfKgf8LRZ4YrSbv -jkeHNuYwM5KVgv+umLOw/p18mUueYmEvpsmQ55Ri2UxxhNWizm3xEtLo1jLGfBUu -7RwuZ3AuR8HMBeOHsWe1/MSpZgmbL0V31mfC0M25CrBs0qBUB1QxpCLv+cSa3Acy -CMfQS9ln8Ydzm2sHPsLZA7kBDQRTckTiAQgA12JICQ4oNax8PaljKomTwuFTCrm4 -6j7Z7HsBM579lqkQmsNaBu8euQF6C5WJUE4aflBIa4Q8vqinZirkdUNvkj2jGdKW -XG+KwGluvbd8IhCvD9ITV52/Sj0V1PqZMcKktRpEczn1KY5BjILXKbtlp1eVa7Ha -VMHHge01c2TH6jttOtasUFBkT0jD/Zd4fO6l1e9cN3e7hhIO6HGqcrhNIaHD1ikG -6VjJU/ndP5qkzwErqlWF2H+TThWaY/PO0zXp5pXQ8geBWPfnw4B6ZvKzoHM54vd+ -aotgoDrNpWMkksm16oAvctXkg/WSt3mzNIHQHQZSYN/uorXek9R8664MmQARAQAB -iQNEBBgBCgAPBQJTckTiBQkPCZwAAhsCASkJEGL0nnR9kRtgwF0gBBkBCgAGBQJT -ckTiAAoJENzV9eXvMsNFID4H/j9fGdHyPQLDvH363lsGx62l5zlX1vL8rjleZMTR -D+JRQJ3MjSgEIdEE8gYLyRmetPsrbQKpOu/uGVs+Ef/SDFj89VhK+661DfHwcahN -XHPTjcNi6OUlE2Z0DXdxgb4czMZkDf79ga/sf72S1uJNQb83GfYN1QfLq+MXsBmF -LfYU5RkoF7obgVQFAs5HYf3RqCribdNEhGEZPPG6wNcp5DC1UvrpotldqwHZltFS -dPPPUT5S/kpcRtqL/bilPc4Pb7qKQR1Huacy1ca1DAEP+TvhvgMmm6ExVAYiV1TA -ZBfOUYC+Czn7ZOGJ8Q4AN0yno4IOsmwBrxaUx9+38I3rb04nLhAAyUUZZGp4Zfj1 -bJ/pOxZ2H2BqX3fstN9tVvZu47D2DoeF6T6x02HIV7oVQ1/haMnjP7rtsWNjrl32 -RkMkbvwqsnQwcZJrylQTxYuzy4IGXak/tlEcesspsG6O34pvPoZ1c+q92jofPOzl -W2xnSTtKlt0Fu/m2WNg6s8tfec7emi69J6Pl+XMAmQkihXF+j4QuXYzSV4G97W2t -AMxo5d3GIQ4UzcxhEvTH5s/S12iGT0xfy6G3yEqTzFgByA94BWN/plzgaaV0bNDs -uK16Sp6c8gldHs5o5uI9wtJa468dj5Ll9zJZOdC3UN3kGDY1T5jnctnfgLpU081c -tfz7tr1URFiq2LYlxpEUC/OUFyilHuM17RacLLAM6+9s2bYFD2uAOfQyUJaUD2z2 -/9I7WRaDbL0DMhn/QZPdhLZSMuuoaBEu99NWBGHMfVpDmmPQeBLTS5l4Q7lbrf6f -zLdb+3Vuhifl6w4UTPC7Wb4qowjtIiaqdtqpsqm2LE62xsvd230wWT+ipGhBx/B4 -soOh2lVXkEGL+nEPTljBxkumkZOTxJl/EC3cFEtVKGCw9Rid8nUmc/v9LcKaJQDO -AZ0oAMc8eSyxKGaW0pePlHn8k5cds1w2ZsSCXuDGNYHATp4Gm3izEGLRsex1KYq+ -+dysRCx2EZMDsaCAUbXNrt12FNhgzh+5AQ0EU3JE4gEIALyimTnOi0q1WouENJKQ -RlpBsZ25Cxp+kc3Ttws65cFYV+3682KMRelDvZ073JRlyMbEmAsxCitrmsKfI8+9 -3TVg9XS5R9RynMpRiyi1m6sHLbeXG6LaWaT3gyzu9VC6EGoadf+l8/emQD2WeDJl -fHJr+QivlGM7hdMvjewj7Wp4+x0JclhNsgjYEUkF4ajy/q+A98YyGFybpOwqRoLv -U5WXQGxuh0LiUjvpLyrIEEFcvASCNOAJgpN92G7nsNDsXpWjwmUDYwM9uJipbM+M -kyWJykiaii9tYg/AzsFN9Cr0+w07IEX8IfWmY9iGjOC3eISwX/vx/jtrI9Mj8Ei5 -RKUAEQEAAYkDRAQYAQoADwUCU3JE4gUJDwmcAAIbDAEpCRBi9J50fZEbYMBdIAQZ -AQoABgUCU3JE4gAKCRAZcP/x+neS2tEuB/4tvkwlS/aJJles7+n9gzlcWHeRHECG -0zTrmQr99uTvBaYewB6gSJK1YrM/ocOH2k6e2EAfYw+bgBXcOpb3NQePZ4vLCAkl -6J40ktwyWBOs8uCAdBX5Ngkxhiz5oNaxQqnBU+xfovsbQJrxj0S28DBXGDR6npI1 -vqjrsYBoPeo4YZu6pUAp6wW+7eC4eHVK/NIogw0XxA3VRzwvzLK+aUI5RbzyWwYY -PDfzXrQRqeUqCF2bnnsXjDHxfqjfoWrnK+ATGFZgjbF2wHhPDRRHqAx/ggn8K/R4 -rvhEKFIqxQHrfZgQgsWregiv46Ph8DBEGcniqeIS5kRQi5y71IB7ndb9cB4QAJIb -BQaB5GhsjVw5bQTsZWMDLoweaR4kqP2eXgx6HuhRw1XcP0ZNNv5//L7tv6tmeXgb -RO17JzCw8+g2ZFq8Wbd6v9MFKefh+FT75Vvb9jV6h2NtQlteKQ9mpXVpcxZ0pKDb -hPzrjcI3Xo/zjYHFjTk2VAxWVPtamBN2eCGc3ggWifYnmuCctxHlTZNyDyrfPwJ+ -Vj8VuTsjd/7b8VVLd2lpzF2m9M25z4zNgxzldAAr4F+bIqjPJUVY29pZFyKKqcBG -zCYBTlB+yiVqjXOyyYQKwE3nkG7UrlsQdQEI/wjqBJtDpQ/w7NLPKwx2633dVQAT -omPKujL3klFlIdof/5+JUDzmg2mC9ATCJ4sgTAIodo6hHACQT2OuKmAHuCI1oqBs -7A3H1pPk3HZVKy7LbdQTy7QTzpBiUHklOKWlWj+ugWeABTZZK5U9cm9vq2mT+rcB -Wu94GriSlDo3vobC78nMDZc68eV18onQpWTlzRsTVVfOjll/8ddtruVkCVhtfRxE -ANQIfZg7P8oNxVDAX+jIsTDxjh8r+S1wsUQcTNop6JMicDbxrBRB13vYIY0Jg4+Z -9WUiKCaM69kbgcJ7tTp0skcJ+rYcjVkTz2/P33/FA8BMDUwCR2FovRnmq9pVjAAP -hS0eN8yqaR533ire0Ur5Vif6+z4A0ifVTZ2hY96B -=nEJu ------END PGP PUBLIC KEY BLOCK----- diff --git a/Makefile b/Makefile deleted file mode 100644 index b956e19..0000000 --- a/Makefile +++ /dev/null @@ -1,181 +0,0 @@ -BINDIR := $(CURDIR)/bin -DIST_DIRS := find * -type d -exec -TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64 -TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-386.tar.gz linux-386.tar.gz.sha256 linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-s390x.tar.gz linux-s390x.tar.gz.sha256 windows-amd64.zip windows-amd64.zip.sha256 -BINNAME ?= helm3 - -GOPATH = $(shell go env GOPATH) -DEP = $(GOPATH)/bin/dep -GOX = $(GOPATH)/bin/gox -GOIMPORTS = $(GOPATH)/bin/goimports -ARCH = $(shell uname -p) - -ACCEPTANCE_DIR:=../acceptance-testing -# To specify the subset of acceptance tests to run. '.' means all tests -ACCEPTANCE_RUN_TESTS=. - -# go option -PKG := ./... -TAGS := -TESTS := . -TESTFLAGS := -LDFLAGS := -w -s -GOFLAGS := -SRC := $(shell find . -type f -name '*.go' -print) - -# Required for globs to work correctly -SHELL = /bin/bash - -GIT_COMMIT = $(shell git rev-parse HEAD) -GIT_SHA = $(shell git rev-parse --short HEAD) -GIT_TAG = $(shell git describe --tags --abbrev=0 --exact-match 2>/dev/null) -GIT_DIRTY = $(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean") - -ifdef VERSION - BINARY_VERSION = $(VERSION) -endif -BINARY_VERSION ?= ${GIT_TAG} - -# Only set Version if building a tag or VERSION is set -ifneq ($(BINARY_VERSION),) - LDFLAGS += -X helm.sh/helm/v3/internal/version.version=${BINARY_VERSION} -endif - -VERSION_METADATA = unreleased -# Clear the "unreleased" string in BuildMetadata -ifneq ($(GIT_TAG),) - VERSION_METADATA = -endif - -LDFLAGS += -X helm.sh/helm/v3/internal/version.metadata=${VERSION_METADATA} -LDFLAGS += -X helm.sh/helm/v3/internal/version.gitCommit=${GIT_COMMIT} -LDFLAGS += -X helm.sh/helm/v3/internal/version.gitTreeState=${GIT_DIRTY} - -.PHONY: all -all: build - -# ------------------------------------------------------------------------------ -# build - -.PHONY: build -build: $(BINDIR)/$(BINNAME) $(BINDIR)/$(BINNAME)-server - -$(BINDIR)/$(BINNAME): $(SRC) - GO111MODULE=on go build -o $(BINDIR)/$(BINNAME) ./cmd/helm -$(BINDIR)/$(BINNAME)-server: $(SRC) - GO111MODULE=on go build -o $(BINDIR)/helm3-server ./cmd/service -# ------------------------------------------------------------------------------ -# test - -.PHONY: test -test: build -ifeq ($(ARCH),s390x) -test: TESTFLAGS += -v -else -test: TESTFLAGS += -race -v -endif -test: test-style -test: test-unit - -.PHONY: test-unit -test-unit: - @echo - @echo "==> Running unit tests <==" - GO111MODULE=on go test $(GOFLAGS) -run $(TESTS) $(PKG) $(TESTFLAGS) - -.PHONY: test-coverage -test-coverage: - @echo - @echo "==> Running unit tests with coverage <==" - @ ./scripts/coverage.sh - -.PHONY: test-style -test-style: - GO111MODULE=on golangci-lint run - -.PHONY: test-acceptance -test-acceptance: TARGETS = linux/amd64 -test-acceptance: build build-cross - @if [ -d "${ACCEPTANCE_DIR}" ]; then \ - cd ${ACCEPTANCE_DIR} && \ - ROBOT_RUN_TESTS=$(ACCEPTANCE_RUN_TESTS) ROBOT_HELM_PATH=$(BINDIR) make acceptance; \ - else \ - echo "You must clone the acceptance_testing repo under $(ACCEPTANCE_DIR)"; \ - echo "You can find the acceptance_testing repo at https://github.com/helm/acceptance-testing"; \ - fi - -.PHONY: test-acceptance-completion -test-acceptance-completion: ACCEPTANCE_RUN_TESTS = shells.robot -test-acceptance-completion: test-acceptance - -.PHONY: coverage -coverage: - @scripts/coverage.sh - -.PHONY: format -format: $(GOIMPORTS) - GO111MODULE=on go list -f '{{.Dir}}' ./... | xargs $(GOIMPORTS) -w -local helm.sh/helm - -# ------------------------------------------------------------------------------ -# dependencies - -# If go get is run from inside the project directory it will add the dependencies -# to the go.mod file. To avoid that we change to a directory without a go.mod file -# when downloading the following dependencies - -$(GOX): - (cd /; GO111MODULE=on go get -u github.com/mitchellh/gox) - -$(GOIMPORTS): - (cd /; GO111MODULE=on go get -u golang.org/x/tools/cmd/goimports) - -# ------------------------------------------------------------------------------ -# release - -.PHONY: build-cross -build-cross: LDFLAGS += -extldflags "-static" -build-cross: $(GOX) - GO111MODULE=on CGO_ENABLED=0 $(GOX) -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/$(BINNAME)" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./cmd/helm - -.PHONY: dist -dist: - ( \ - cd _dist && \ - $(DIST_DIRS) cp ../LICENSE {} \; && \ - $(DIST_DIRS) cp ../README.md {} \; && \ - $(DIST_DIRS) tar -zcf helm-${VERSION}-{}.tar.gz {} \; && \ - $(DIST_DIRS) zip -r helm-${VERSION}-{}.zip {} \; \ - ) - -.PHONY: fetch-dist -fetch-dist: - mkdir -p _dist - cd _dist && \ - for obj in ${TARGET_OBJS} ; do \ - curl -sSL -o helm-${VERSION}-$${obj} https://get.helm.sh/helm-${VERSION}-$${obj} ; \ - done - -.PHONY: sign -sign: - for f in _dist/*.{gz,zip,sha256} ; do \ - gpg --armor --detach-sign $${f} ; \ - done - -.PHONY: checksum -checksum: - for f in _dist/*.{gz,zip} ; do \ - shasum -a 256 "$${f}" | awk '{print $$1}' > "$${f}.sha256" ; \ - done - -# ------------------------------------------------------------------------------ - -.PHONY: clean -clean: - @rm -rf $(BINDIR) ./_dist - -.PHONY: info -info: - @echo "Version: ${VERSION}" - @echo "Git Tag: ${GIT_TAG}" - @echo "Git Commit: ${GIT_COMMIT}" - @echo "Git Tree State: ${GIT_DIRTY}" diff --git a/OWNERS b/OWNERS deleted file mode 100644 index d7dac55..0000000 --- a/OWNERS +++ /dev/null @@ -1,21 +0,0 @@ -maintainers: - - adamreese - - bacongobbler - - fibonacci1729 - - hickeyma - - jdolitsky - - marckhouzam - - mattfarina - - michelleN - - prydonius - - SlickNik - - technosophos - - thomastaylor312 - - viglesiasce -emeritus: - - jascott1 - - migmartri - - nebril - - seh - - vaikas-google - - rimusz diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index c84a6f8..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,3 +0,0 @@ -# Helm Security Reporting and Policy - -The Helm project has [a common process and policy that can be found here](https://github.com/helm/community/blob/master/SECURITY.md). \ No newline at end of file diff --git a/cmd/helm/chart.go b/cmd/helm/chart.go deleted file mode 100644 index adc874c..0000000 --- a/cmd/helm/chart.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/pkg/action" -) - -const chartHelp = ` -This command consists of multiple subcommands to work with the chart cache. - -The subcommands can be used to push, pull, tag, list, or remove Helm charts. -` - -func newChartCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "chart", - Short: "push, pull, tag, or remove Helm charts", - Long: chartHelp, - Hidden: !FeatureGateOCI.IsEnabled(), - PersistentPreRunE: checkOCIFeatureGate(), - } - cmd.AddCommand( - newChartListCmd(cfg, out), - newChartExportCmd(cfg, out), - newChartPullCmd(cfg, out), - newChartPushCmd(cfg, out), - newChartRemoveCmd(cfg, out), - newChartSaveCmd(cfg, out), - ) - return cmd -} diff --git a/cmd/helm/chart_export.go b/cmd/helm/chart_export.go deleted file mode 100644 index 67caf08..0000000 --- a/cmd/helm/chart_export.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" -) - -const chartExportDesc = ` -Export a chart stored in local registry cache. - -This will create a new directory with the name of -the chart, in a format that developers can modify -and check into source control if desired. -` - -func newChartExportCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewChartExport(cfg) - - cmd := &cobra.Command{ - Use: "export [ref]", - Short: "export a chart to directory", - Long: chartExportDesc, - Args: require.MinimumNArgs(1), - Hidden: !FeatureGateOCI.IsEnabled(), - RunE: func(cmd *cobra.Command, args []string) error { - ref := args[0] - return client.Run(out, ref) - }, - } - - f := cmd.Flags() - f.StringVarP(&client.Destination, "destination", "d", ".", "location to write the chart.") - - return cmd -} diff --git a/cmd/helm/chart_list.go b/cmd/helm/chart_list.go deleted file mode 100644 index a9d01c9..0000000 --- a/cmd/helm/chart_list.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/pkg/action" -) - -const chartListDesc = ` -List all charts in the local registry cache. - -Charts are sorted by ref name, alphabetically. -` - -func newChartListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - return &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "list all saved charts", - Long: chartListDesc, - Hidden: !FeatureGateOCI.IsEnabled(), - RunE: func(cmd *cobra.Command, args []string) error { - return action.NewChartList(cfg).Run(out) - }, - } -} diff --git a/cmd/helm/chart_pull.go b/cmd/helm/chart_pull.go deleted file mode 100644 index 760ff3e..0000000 --- a/cmd/helm/chart_pull.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" -) - -const chartPullDesc = ` -Download a chart from a remote registry. - -This will store the chart in the local registry cache to be used later. -` - -func newChartPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - return &cobra.Command{ - Use: "pull [ref]", - Short: "pull a chart from remote", - Long: chartPullDesc, - Args: require.MinimumNArgs(1), - Hidden: !FeatureGateOCI.IsEnabled(), - RunE: func(cmd *cobra.Command, args []string) error { - ref := args[0] - return action.NewChartPull(cfg).Run(out, ref) - }, - } -} diff --git a/cmd/helm/chart_push.go b/cmd/helm/chart_push.go deleted file mode 100644 index ff34632..0000000 --- a/cmd/helm/chart_push.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" -) - -const chartPushDesc = ` -Upload a chart to a remote registry. - -Note: the ref must already exist in the local registry cache. - -Must first run "helm chart save" or "helm chart pull". -` - -func newChartPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - return &cobra.Command{ - Use: "push [ref]", - Short: "push a chart to remote", - Long: chartPushDesc, - Args: require.MinimumNArgs(1), - Hidden: !FeatureGateOCI.IsEnabled(), - RunE: func(cmd *cobra.Command, args []string) error { - ref := args[0] - return action.NewChartPush(cfg).Run(out, ref) - }, - } -} diff --git a/cmd/helm/chart_remove.go b/cmd/helm/chart_remove.go deleted file mode 100644 index d952951..0000000 --- a/cmd/helm/chart_remove.go +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" -) - -const chartRemoveDesc = ` -Remove a chart from the local registry cache. - -Note: the chart content will still exist in the cache, -but it will no longer appear in "helm chart list". - -To remove all unlinked content, please run "helm chart prune". (TODO) -` - -func newChartRemoveCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - return &cobra.Command{ - Use: "remove [ref]", - Aliases: []string{"rm"}, - Short: "remove a chart", - Long: chartRemoveDesc, - Args: require.MinimumNArgs(1), - Hidden: !FeatureGateOCI.IsEnabled(), - RunE: func(cmd *cobra.Command, args []string) error { - ref := args[0] - return action.NewChartRemove(cfg).Run(out, ref) - }, - } -} diff --git a/cmd/helm/chart_save.go b/cmd/helm/chart_save.go deleted file mode 100644 index 35b72cd..0000000 --- a/cmd/helm/chart_save.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "path/filepath" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart/loader" -) - -const chartSaveDesc = ` -Store a copy of chart in local registry cache. - -Note: modifying the chart after this operation will -not change the item as it exists in the cache. -` - -func newChartSaveCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - return &cobra.Command{ - Use: "save [path] [ref]", - Short: "save a chart directory", - Long: chartSaveDesc, - Args: require.MinimumNArgs(2), - Hidden: !FeatureGateOCI.IsEnabled(), - RunE: func(cmd *cobra.Command, args []string) error { - path := args[0] - ref := args[1] - - path, err := filepath.Abs(path) - if err != nil { - return err - } - - ch, err := loader.Load(path) - if err != nil { - return err - } - - return action.NewChartSave(cfg).Run(out, ch, ref) - }, - } -} diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go deleted file mode 100644 index 1601cb4..0000000 --- a/cmd/helm/completion.go +++ /dev/null @@ -1,246 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "os" - "path/filepath" - - "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -const completionDesc = ` -Generate autocompletions script for Helm for the specified shell (bash or zsh). - -This command can generate shell autocompletions. e.g. - - $ helm completion bash - -Can be sourced as such - - $ source <(helm completion bash) -` - -var ( - completionShells = map[string]func(out io.Writer, cmd *cobra.Command) error{ - "bash": runCompletionBash, - "zsh": runCompletionZsh, - } -) - -func newCompletionCmd(out io.Writer) *cobra.Command { - shells := []string{} - for s := range completionShells { - shells = append(shells, s) - } - - cmd := &cobra.Command{ - Use: "completion SHELL", - Short: "Generate autocompletions script for the specified shell (bash or zsh)", - Long: completionDesc, - RunE: func(cmd *cobra.Command, args []string) error { - return runCompletion(out, cmd, args) - }, - ValidArgs: shells, - } - - return cmd -} - -func runCompletion(out io.Writer, cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errors.New("shell not specified") - } - if len(args) > 1 { - return errors.New("too many arguments, expected only the shell type") - } - run, found := completionShells[args[0]] - if !found { - return errors.Errorf("unsupported shell type %q", args[0]) - } - - return run(out, cmd) -} - -func runCompletionBash(out io.Writer, cmd *cobra.Command) error { - err := cmd.Root().GenBashCompletion(out) - - // In case the user renamed the helm binary (e.g., to be able to run - // both helm2 and helm3), we hook the new binary name to the completion function - if binary := filepath.Base(os.Args[0]); binary != "helm" { - renamedBinaryHook := ` -# Hook the command used to generate the completion script -# to the helm completion function to handle the case where -# the user renamed the helm binary -if [[ $(type -t compopt) = "builtin" ]]; then - complete -o default -F __start_helm %[1]s -else - complete -o default -o nospace -F __start_helm %[1]s -fi -` - fmt.Fprintf(out, renamedBinaryHook, binary) - } - - return err -} - -func runCompletionZsh(out io.Writer, cmd *cobra.Command) error { - zshInitialization := `#compdef helm - -__helm_bash_source() { - alias shopt=':' - alias _expand=_bash_expand - alias _complete=_bash_comp - emulate -L sh - setopt kshglob noshglob braceexpand - source "$@" -} -__helm_type() { - # -t is not supported by zsh - if [ "$1" == "-t" ]; then - shift - # fake Bash 4 to disable "complete -o nospace". Instead - # "compopt +-o nospace" is used in the code to toggle trailing - # spaces. We don't support that, but leave trailing spaces on - # all the time - if [ "$1" = "__helm_compopt" ]; then - echo builtin - return 0 - fi - fi - type "$@" -} -__helm_compgen() { - local completions w - completions=( $(compgen "$@") ) || return $? - # filter by given word as prefix - while [[ "$1" = -* && "$1" != -- ]]; do - shift - shift - done - if [[ "$1" == -- ]]; then - shift - fi - for w in "${completions[@]}"; do - if [[ "${w}" = "$1"* ]]; then - # Use printf instead of echo beause it is possible that - # the value to print is -n, which would be interpreted - # as a flag to echo - printf "%s\n" "${w}" - fi - done -} -__helm_compopt() { - true # don't do anything. Not supported by bashcompinit in zsh -} -__helm_ltrim_colon_completions() -{ - if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then - # Remove colon-word prefix from COMPREPLY items - local colon_word=${1%${1##*:}} - local i=${#COMPREPLY[*]} - while [[ $((--i)) -ge 0 ]]; do - COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} - done - fi -} -__helm_get_comp_words_by_ref() { - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[${COMP_CWORD}-1]}" - words=("${COMP_WORDS[@]}") - cword=("${COMP_CWORD[@]}") -} -__helm_filedir() { - local RET OLD_IFS w qw - __debug "_filedir $@ cur=$cur" - if [[ "$1" = \~* ]]; then - # somehow does not work. Maybe, zsh does not call this at all - eval echo "$1" - return 0 - fi - OLD_IFS="$IFS" - IFS=$'\n' - if [ "$1" = "-d" ]; then - shift - RET=( $(compgen -d) ) - else - RET=( $(compgen -f) ) - fi - IFS="$OLD_IFS" - IFS="," __debug "RET=${RET[@]} len=${#RET[@]}" - for w in ${RET[@]}; do - if [[ ! "${w}" = "${cur}"* ]]; then - continue - fi - if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then - qw="$(__helm_quote "${w}")" - if [ -d "${w}" ]; then - COMPREPLY+=("${qw}/") - else - COMPREPLY+=("${qw}") - fi - fi - done -} -__helm_quote() { - if [[ $1 == \'* || $1 == \"* ]]; then - # Leave out first character - printf %q "${1:1}" - else - printf %q "$1" - fi -} -autoload -U +X bashcompinit && bashcompinit -# use word boundary patterns for BSD or GNU sed -LWORD='[[:<:]]' -RWORD='[[:>:]]' -if sed --help 2>&1 | grep -q 'GNU\|BusyBox'; then - LWORD='\<' - RWORD='\>' -fi -__helm_convert_bash_to_zsh() { - sed \ - -e 's/declare -F/whence -w/' \ - -e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \ - -e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \ - -e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \ - -e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \ - -e "s/${LWORD}_filedir${RWORD}/__helm_filedir/g" \ - -e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__helm_get_comp_words_by_ref/g" \ - -e "s/${LWORD}__ltrim_colon_completions${RWORD}/__helm_ltrim_colon_completions/g" \ - -e "s/${LWORD}compgen${RWORD}/__helm_compgen/g" \ - -e "s/${LWORD}compopt${RWORD}/__helm_compopt/g" \ - -e "s/${LWORD}declare${RWORD}/builtin declare/g" \ - -e "s/\\\$(type${RWORD}/\$(__helm_type/g" \ - -e 's/aliashash\["\(.\{1,\}\)"\]/aliashash[\1]/g' \ - -e 's/FUNCNAME/funcstack/g' \ - <<'BASH_COMPLETION_EOF' -` - out.Write([]byte(zshInitialization)) - - runCompletionBash(out, cmd) - - zshTail := ` -BASH_COMPLETION_EOF -} -__helm_bash_source <(__helm_convert_bash_to_zsh) -` - out.Write([]byte(zshTail)) - return nil -} diff --git a/cmd/helm/create.go b/cmd/helm/create.go deleted file mode 100644 index e8ff757..0000000 --- a/cmd/helm/create.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "path/filepath" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/helmpath" -) - -const createDesc = ` -This command creates a chart directory along with the common files and -directories used in a chart. - -For example, 'helm create foo' will create a directory structure that looks -something like this: - - foo/ - ├── .helmignore # Contains patterns to ignore when packaging Helm charts. - ├── Chart.yaml # Information about your chart - ├── values.yaml # The default values for your templates - ├── charts/ # Charts that this chart depends on - └── templates/ # The template files - └── tests/ # The test files - -'helm create' takes a path for an argument. If directories in the given path -do not exist, Helm will attempt to create them as it goes. If the given -destination exists and there are files in that directory, conflicting files -will be overwritten, but other files will be left alone. -` - -type createOptions struct { - starter string // --starter - name string - starterDir string -} - -func newCreateCmd(out io.Writer) *cobra.Command { - o := &createOptions{} - - cmd := &cobra.Command{ - Use: "create NAME", - Short: "create a new chart with the given name", - Long: createDesc, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - o.name = args[0] - o.starterDir = helmpath.DataPath("starters") - return o.run(out) - }, - } - - cmd.Flags().StringVarP(&o.starter, "starter", "p", "", "The name or absolute path to Helm starter scaffold") - return cmd -} - -func (o *createOptions) run(out io.Writer) error { - fmt.Fprintf(out, "Creating %s\n", o.name) - - chartname := filepath.Base(o.name) - cfile := &chart.Metadata{ - Name: chartname, - Description: "A Helm chart for Kubernetes", - Type: "application", - Version: "0.1.0", - AppVersion: "0.1.0", - APIVersion: chart.APIVersionV2, - } - - if o.starter != "" { - // Create from the starter - lstarter := filepath.Join(o.starterDir, o.starter) - // If path is absolute, we don't want to prefix it with helm starters folder - if filepath.IsAbs(o.starter) { - lstarter = o.starter - } - return chartutil.CreateFrom(cfile, filepath.Dir(o.name), lstarter) - } - - _, err := chartutil.Create(chartname, filepath.Dir(o.name)) - return err -} diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go deleted file mode 100644 index 0a9b7b9..0000000 --- a/cmd/helm/create_test.go +++ /dev/null @@ -1,194 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "testing" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/helmpath" -) - -func TestCreateCmd(t *testing.T) { - defer ensure.HelmHome(t)() - cname := "testchart" - dir := ensure.TempDir(t) - defer testChdir(t, dir)() - - // Run a create - if _, _, err := executeActionCommand("create " + cname); err != nil { - t.Fatalf("Failed to run create: %s", err) - } - - // Test that the chart is there - if fi, err := os.Stat(cname); err != nil { - t.Fatalf("no chart directory: %s", err) - } else if !fi.IsDir() { - t.Fatalf("chart is not directory") - } - - c, err := loader.LoadDir(cname) - if err != nil { - t.Fatal(err) - } - - if c.Name() != cname { - t.Errorf("Expected %q name, got %q", cname, c.Name()) - } - if c.Metadata.APIVersion != chart.APIVersionV2 { - t.Errorf("Wrong API version: %q", c.Metadata.APIVersion) - } -} - -func TestCreateStarterCmd(t *testing.T) { - defer ensure.HelmHome(t)() - cname := "testchart" - defer resetEnv()() - os.MkdirAll(helmpath.CachePath(), 0755) - defer testChdir(t, helmpath.CachePath())() - - // Create a starter. - starterchart := helmpath.DataPath("starters") - os.MkdirAll(starterchart, 0755) - if dest, err := chartutil.Create("starterchart", starterchart); err != nil { - t.Fatalf("Could not create chart: %s", err) - } else { - t.Logf("Created %s", dest) - } - tplpath := filepath.Join(starterchart, "starterchart", "templates", "foo.tpl") - if err := ioutil.WriteFile(tplpath, []byte("test"), 0644); err != nil { - t.Fatalf("Could not write template: %s", err) - } - - // Run a create - if _, _, err := executeActionCommand(fmt.Sprintf("create --starter=starterchart %s", cname)); err != nil { - t.Errorf("Failed to run create: %s", err) - return - } - - // Test that the chart is there - if fi, err := os.Stat(cname); err != nil { - t.Fatalf("no chart directory: %s", err) - } else if !fi.IsDir() { - t.Fatalf("chart is not directory") - } - - c, err := loader.LoadDir(cname) - if err != nil { - t.Fatal(err) - } - - if c.Name() != cname { - t.Errorf("Expected %q name, got %q", cname, c.Name()) - } - if c.Metadata.APIVersion != chart.APIVersionV2 { - t.Errorf("Wrong API version: %q", c.Metadata.APIVersion) - } - - expectedNumberOfTemplates := 8 - if l := len(c.Templates); l != expectedNumberOfTemplates { - t.Errorf("Expected %d templates, got %d", expectedNumberOfTemplates, l) - } - - found := false - for _, tpl := range c.Templates { - if tpl.Name == "templates/foo.tpl" { - found = true - if data := string(tpl.Data); data != "test" { - t.Errorf("Expected template 'test', got %q", data) - } - } - } - if !found { - t.Error("Did not find foo.tpl") - } - -} - -func TestCreateStarterAbsoluteCmd(t *testing.T) { - defer resetEnv()() - defer ensure.HelmHome(t)() - cname := "testchart" - - // Create a starter. - starterchart := helmpath.DataPath("starters") - os.MkdirAll(starterchart, 0755) - if dest, err := chartutil.Create("starterchart", starterchart); err != nil { - t.Fatalf("Could not create chart: %s", err) - } else { - t.Logf("Created %s", dest) - } - tplpath := filepath.Join(starterchart, "starterchart", "templates", "foo.tpl") - if err := ioutil.WriteFile(tplpath, []byte("test"), 0644); err != nil { - t.Fatalf("Could not write template: %s", err) - } - - os.MkdirAll(helmpath.CachePath(), 0755) - defer testChdir(t, helmpath.CachePath())() - - starterChartPath := filepath.Join(starterchart, "starterchart") - - // Run a create - if _, _, err := executeActionCommand(fmt.Sprintf("create --starter=%s %s", starterChartPath, cname)); err != nil { - t.Errorf("Failed to run create: %s", err) - return - } - - // Test that the chart is there - if fi, err := os.Stat(cname); err != nil { - t.Fatalf("no chart directory: %s", err) - } else if !fi.IsDir() { - t.Fatalf("chart is not directory") - } - - c, err := loader.LoadDir(cname) - if err != nil { - t.Fatal(err) - } - - if c.Name() != cname { - t.Errorf("Expected %q name, got %q", cname, c.Name()) - } - if c.Metadata.APIVersion != chart.APIVersionV2 { - t.Errorf("Wrong API version: %q", c.Metadata.APIVersion) - } - - expectedNumberOfTemplates := 8 - if l := len(c.Templates); l != expectedNumberOfTemplates { - t.Errorf("Expected %d templates, got %d", expectedNumberOfTemplates, l) - } - - found := false - for _, tpl := range c.Templates { - if tpl.Name == "templates/foo.tpl" { - found = true - if data := string(tpl.Data); data != "test" { - t.Errorf("Expected template 'test', got %q", data) - } - } - } - if !found { - t.Error("Did not find foo.tpl") - } -} diff --git a/cmd/helm/dependency.go b/cmd/helm/dependency.go deleted file mode 100644 index 2cc4c50..0000000 --- a/cmd/helm/dependency.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "path/filepath" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" -) - -const dependencyDesc = ` -Manage the dependencies of a chart. - -Helm charts store their dependencies in 'charts/'. For chart developers, it is -often easier to manage dependencies in 'Chart.yaml' which declares all -dependencies. - -The dependency commands operate on that file, making it easy to synchronize -between the desired dependencies and the actual dependencies stored in the -'charts/' directory. - -For example, this Chart.yaml declares two dependencies: - - # Chart.yaml - dependencies: - - name: nginx - version: "1.2.3" - repository: "https://example.com/charts" - - name: memcached - version: "3.2.1" - repository: "https://another.example.com/charts" - - -The 'name' should be the name of a chart, where that name must match the name -in that chart's 'Chart.yaml' file. - -The 'version' field should contain a semantic version or version range. - -The 'repository' URL should point to a Chart Repository. Helm expects that by -appending '/index.yaml' to the URL, it should be able to retrieve the chart -repository's index. Note: 'repository' can be an alias. The alias must start -with 'alias:' or '@'. - -Starting from 2.2.0, repository can be defined as the path to the directory of -the dependency charts stored locally. The path should start with a prefix of -"file://". For example, - - # Chart.yaml - dependencies: - - name: nginx - version: "1.2.3" - repository: "file://../dependency_chart/nginx" - -If the dependency chart is retrieved locally, it is not required to have the -repository added to helm by "helm add repo". Version matching is also supported -for this case. -` - -const dependencyListDesc = ` -List all of the dependencies declared in a chart. - -This can take chart archives and chart directories as input. It will not alter -the contents of a chart. - -This will produce an error if the chart cannot be loaded. -` - -func newDependencyCmd(out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "dependency update|build|list", - Aliases: []string{"dep", "dependencies"}, - Short: "manage a chart's dependencies", - Long: dependencyDesc, - Args: require.NoArgs, - } - - cmd.AddCommand(newDependencyListCmd(out)) - cmd.AddCommand(newDependencyUpdateCmd(out)) - cmd.AddCommand(newDependencyBuildCmd(out)) - - return cmd -} - -func newDependencyListCmd(out io.Writer) *cobra.Command { - client := action.NewDependency() - - cmd := &cobra.Command{ - Use: "list CHART", - Aliases: []string{"ls"}, - Short: "list the dependencies for the given chart", - Long: dependencyListDesc, - Args: require.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - chartpath := "." - if len(args) > 0 { - chartpath = filepath.Clean(args[0]) - } - return client.List(chartpath, out) - }, - } - return cmd -} diff --git a/cmd/helm/dependency_build.go b/cmd/helm/dependency_build.go deleted file mode 100644 index 478b494..0000000 --- a/cmd/helm/dependency_build.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "os" - "path/filepath" - - "github.com/spf13/cobra" - "k8s.io/client-go/util/homedir" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/getter" -) - -const dependencyBuildDesc = ` -Build out the charts/ directory from the Chart.lock file. - -Build is used to reconstruct a chart's dependencies to the state specified in -the lock file. This will not re-negotiate dependencies, as 'helm dependency update' -does. - -If no lock file is found, 'helm dependency build' will mirror the behavior -of 'helm dependency update'. -` - -func newDependencyBuildCmd(out io.Writer) *cobra.Command { - client := action.NewDependency() - - cmd := &cobra.Command{ - Use: "build CHART", - Short: "rebuild the charts/ directory based on the Chart.lock file", - Long: dependencyBuildDesc, - Args: require.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - chartpath := "." - if len(args) > 0 { - chartpath = filepath.Clean(args[0]) - } - man := &downloader.Manager{ - Out: out, - ChartPath: chartpath, - Keyring: client.Keyring, - Getters: getter.All(settings), - RepositoryConfig: settings.RepositoryConfig, - RepositoryCache: settings.RepositoryCache, - Debug: settings.Debug, - } - if client.Verify { - man.Verify = downloader.VerifyIfPossible - } - return man.Build() - }, - } - - f := cmd.Flags() - f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures") - f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys") - - return cmd -} - -// defaultKeyring returns the expanded path to the default keyring. -func defaultKeyring() string { - if v, ok := os.LookupEnv("GNUPGHOME"); ok { - return filepath.Join(v, "pubring.gpg") - } - return filepath.Join(homedir.HomeDir(), ".gnupg", "pubring.gpg") -} diff --git a/cmd/helm/dependency_build_test.go b/cmd/helm/dependency_build_test.go deleted file mode 100644 index 58ef3d3..0000000 --- a/cmd/helm/dependency_build_test.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "testing" - - "helm.sh/helm/v3/pkg/provenance" - "helm.sh/helm/v3/pkg/repo" - "helm.sh/helm/v3/pkg/repo/repotest" -) - -func TestDependencyBuildCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") - defer srv.Stop() - if err != nil { - t.Fatal(err) - } - - rootDir := srv.Root() - srv.LinkIndices() - - chartname := "depbuild" - createTestingChart(t, rootDir, chartname, srv.URL()) - repoFile := filepath.Join(rootDir, "repositories.yaml") - - cmd := fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s", filepath.Join(rootDir, chartname), repoFile, rootDir) - _, out, err := executeActionCommand(cmd) - - // In the first pass, we basically want the same results as an update. - if err != nil { - t.Logf("Output: %s", out) - t.Fatal(err) - } - - if !strings.Contains(out, `update from the "test" chart repository`) { - t.Errorf("Repo did not get updated\n%s", out) - } - - // Make sure the actual file got downloaded. - expect := filepath.Join(rootDir, chartname, "charts/reqtest-0.1.0.tgz") - if _, err := os.Stat(expect); err != nil { - t.Fatal(err) - } - - // In the second pass, we want to remove the chart's request dependency, - // then see if it restores from the lock. - lockfile := filepath.Join(rootDir, chartname, "Chart.lock") - if _, err := os.Stat(lockfile); err != nil { - t.Fatal(err) - } - if err := os.RemoveAll(expect); err != nil { - t.Fatal(err) - } - - _, out, err = executeActionCommand(cmd) - if err != nil { - t.Logf("Output: %s", out) - t.Fatal(err) - } - - // Now repeat the test that the dependency exists. - if _, err := os.Stat(expect); err != nil { - t.Fatal(err) - } - - // Make sure that build is also fetching the correct version. - hash, err := provenance.DigestFile(expect) - if err != nil { - t.Fatal(err) - } - - i, err := repo.LoadIndexFile(filepath.Join(rootDir, "index.yaml")) - if err != nil { - t.Fatal(err) - } - - reqver := i.Entries["reqtest"][0] - if h := reqver.Digest; h != hash { - t.Errorf("Failed hash match: expected %s, got %s", hash, h) - } - if v := reqver.Version; v != "0.1.0" { - t.Errorf("mismatched versions. Expected %q, got %q", "0.1.0", v) - } -} diff --git a/cmd/helm/dependency_test.go b/cmd/helm/dependency_test.go deleted file mode 100644 index 80b357a..0000000 --- a/cmd/helm/dependency_test.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "runtime" - "testing" -) - -func TestDependencyListCmd(t *testing.T) { - noSuchChart := cmdTestCase{ - name: "No such chart", - cmd: "dependency list /no/such/chart", - golden: "output/dependency-list-no-chart-linux.txt", - wantError: true, - } - - noDependencies := cmdTestCase{ - name: "No dependencies", - cmd: "dependency list testdata/testcharts/alpine", - golden: "output/dependency-list-no-requirements-linux.txt", - } - - if runtime.GOOS == "windows" { - noSuchChart.golden = "output/dependency-list-no-chart-windows.txt" - noDependencies.golden = "output/dependency-list-no-requirements-windows.txt" - } - - tests := []cmdTestCase{noSuchChart, - noDependencies, { - name: "Dependencies in chart dir", - cmd: "dependency list testdata/testcharts/reqtest", - golden: "output/dependency-list.txt", - }, { - name: "Dependencies in chart archive", - cmd: "dependency list testdata/testcharts/reqtest-0.1.0.tgz", - golden: "output/dependency-list-archive.txt", - }} - runTestCmd(t, tests) -} diff --git a/cmd/helm/dependency_update.go b/cmd/helm/dependency_update.go deleted file mode 100644 index 9855afb..0000000 --- a/cmd/helm/dependency_update.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "path/filepath" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/getter" -) - -const dependencyUpDesc = ` -Update the on-disk dependencies to mirror Chart.yaml. - -This command verifies that the required charts, as expressed in 'Chart.yaml', -are present in 'charts/' and are at an acceptable version. It will pull down -the latest charts that satisfy the dependencies, and clean up old dependencies. - -On successful update, this will generate a lock file that can be used to -rebuild the dependencies to an exact version. - -Dependencies are not required to be represented in 'Chart.yaml'. For that -reason, an update command will not remove charts unless they are (a) present -in the Chart.yaml file, but (b) at the wrong version. -` - -// newDependencyUpdateCmd creates a new dependency update command. -func newDependencyUpdateCmd(out io.Writer) *cobra.Command { - client := action.NewDependency() - - cmd := &cobra.Command{ - Use: "update CHART", - Aliases: []string{"up"}, - Short: "update charts/ based on the contents of Chart.yaml", - Long: dependencyUpDesc, - Args: require.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - chartpath := "." - if len(args) > 0 { - chartpath = filepath.Clean(args[0]) - } - man := &downloader.Manager{ - Out: out, - ChartPath: chartpath, - Keyring: client.Keyring, - SkipUpdate: client.SkipRefresh, - Getters: getter.All(settings), - RepositoryConfig: settings.RepositoryConfig, - RepositoryCache: settings.RepositoryCache, - Debug: settings.Debug, - } - if client.Verify { - man.Verify = downloader.VerifyAlways - } - return man.Update() - }, - } - - f := cmd.Flags() - f.BoolVar(&client.Verify, "verify", false, "verify the packages against signatures") - f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys") - f.BoolVar(&client.SkipRefresh, "skip-refresh", false, "do not refresh the local repository cache") - - return cmd -} diff --git a/cmd/helm/dependency_update_test.go b/cmd/helm/dependency_update_test.go deleted file mode 100644 index 1f9d558..0000000 --- a/cmd/helm/dependency_update_test.go +++ /dev/null @@ -1,205 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - "testing" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/provenance" - "helm.sh/helm/v3/pkg/repo" - "helm.sh/helm/v3/pkg/repo/repotest" -) - -func TestDependencyUpdateCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") - if err != nil { - t.Fatal(err) - } - defer srv.Stop() - t.Logf("Listening on directory %s", srv.Root()) - - if err := srv.LinkIndices(); err != nil { - t.Fatal(err) - } - - dir := func(p ...string) string { - return filepath.Join(append([]string{srv.Root()}, p...)...) - } - - chartname := "depup" - ch := createTestingMetadata(chartname, srv.URL()) - md := ch.Metadata - if err := chartutil.SaveDir(ch, dir()); err != nil { - t.Fatal(err) - } - - _, out, err := executeActionCommand( - fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()), - ) - if err != nil { - t.Logf("Output: %s", out) - t.Fatal(err) - } - - // This is written directly to stdout, so we have to capture as is. - if !strings.Contains(out, `update from the "test" chart repository`) { - t.Errorf("Repo did not get updated\n%s", out) - } - - // Make sure the actual file got downloaded. - expect := dir(chartname, "charts/reqtest-0.1.0.tgz") - if _, err := os.Stat(expect); err != nil { - t.Fatal(err) - } - - hash, err := provenance.DigestFile(expect) - if err != nil { - t.Fatal(err) - } - - i, err := repo.LoadIndexFile(dir(helmpath.CacheIndexFile("test"))) - if err != nil { - t.Fatal(err) - } - - reqver := i.Entries["reqtest"][0] - if h := reqver.Digest; h != hash { - t.Errorf("Failed hash match: expected %s, got %s", hash, h) - } - - // Now change the dependencies and update. This verifies that on update, - // old dependencies are cleansed and new dependencies are added. - md.Dependencies = []*chart.Dependency{ - {Name: "reqtest", Version: "0.1.0", Repository: srv.URL()}, - {Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()}, - } - if err := chartutil.SaveChartfile(dir(chartname, "Chart.yaml"), md); err != nil { - t.Fatal(err) - } - - _, out, err = executeActionCommand(fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir())) - if err != nil { - t.Logf("Output: %s", out) - t.Fatal(err) - } - - // In this second run, we should see compressedchart-0.3.0.tgz, and not - // the 0.1.0 version. - expect = dir(chartname, "charts/compressedchart-0.3.0.tgz") - if _, err := os.Stat(expect); err != nil { - t.Fatalf("Expected %q: %s", expect, err) - } - unexpected := dir(chartname, "charts/compressedchart-0.1.0.tgz") - if _, err := os.Stat(unexpected); err == nil { - t.Fatalf("Unexpected %q", unexpected) - } -} - -func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { - defer resetEnv()() - defer ensure.HelmHome(t)() - - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") - if err != nil { - t.Fatal(err) - } - defer srv.Stop() - t.Logf("Listening on directory %s", srv.Root()) - - if err := srv.LinkIndices(); err != nil { - t.Fatal(err) - } - - chartname := "depupdelete" - - dir := func(p ...string) string { - return filepath.Join(append([]string{srv.Root()}, p...)...) - } - createTestingChart(t, dir(), chartname, srv.URL()) - - _, output, err := executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir())) - if err != nil { - t.Logf("Output: %s", output) - t.Fatal(err) - } - - // Chart repo is down - srv.Stop() - - _, output, err = executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir())) - if err == nil { - t.Logf("Output: %s", output) - t.Fatal("Expected error, got nil") - } - - // Make sure charts dir still has dependencies - files, err := ioutil.ReadDir(filepath.Join(dir(chartname), "charts")) - if err != nil { - t.Fatal(err) - } - dependencies := []string{"compressedchart-0.1.0.tgz", "reqtest-0.1.0.tgz"} - - if len(dependencies) != len(files) { - t.Fatalf("Expected %d chart dependencies, got %d", len(dependencies), len(files)) - } - for index, file := range files { - if dependencies[index] != file.Name() { - t.Fatalf("Chart dependency %s not matching %s", dependencies[index], file.Name()) - } - } - - // Make sure tmpcharts is deleted - if _, err := os.Stat(filepath.Join(dir(chartname), "tmpcharts")); !os.IsNotExist(err) { - t.Fatalf("tmpcharts dir still exists") - } -} - -// createTestingMetadata creates a basic chart that depends on reqtest-0.1.0 -// -// The baseURL can be used to point to a particular repository server. -func createTestingMetadata(name, baseURL string) *chart.Chart { - return &chart.Chart{ - Metadata: &chart.Metadata{ - APIVersion: chart.APIVersionV2, - Name: name, - Version: "1.2.3", - Dependencies: []*chart.Dependency{ - {Name: "reqtest", Version: "0.1.0", Repository: baseURL}, - {Name: "compressedchart", Version: "0.1.0", Repository: baseURL}, - }, - }, - } -} - -// createTestingChart creates a basic chart that depends on reqtest-0.1.0 -// -// The baseURL can be used to point to a particular repository server. -func createTestingChart(t *testing.T, dest, name, baseURL string) { - t.Helper() - cfile := createTestingMetadata(name, baseURL) - if err := chartutil.SaveDir(cfile, dest); err != nil { - t.Fatal(err) - } -} diff --git a/cmd/helm/docs.go b/cmd/helm/docs.go deleted file mode 100644 index 2c9020f..0000000 --- a/cmd/helm/docs.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "path/filepath" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/cobra/doc" - - "helm.sh/helm/v3/cmd/helm/require" -) - -const docsDesc = ` -Generate documentation files for Helm. - -This command can generate documentation for Helm in the following formats: - -- Markdown -- Man pages - -It can also generate bash autocompletions. -` - -type docsOptions struct { - dest string - docTypeString string - topCmd *cobra.Command -} - -func newDocsCmd(out io.Writer) *cobra.Command { - o := &docsOptions{} - - cmd := &cobra.Command{ - Use: "docs", - Short: "Generate documentation as markdown or man pages", - Long: docsDesc, - Hidden: true, - Args: require.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - o.topCmd = cmd.Root() - return o.run(out) - }, - } - - f := cmd.Flags() - f.StringVar(&o.dest, "dir", "./", "directory to which documentation is written") - f.StringVar(&o.docTypeString, "type", "markdown", "the type of documentation to generate (markdown, man, bash)") - - return cmd -} - -func (o *docsOptions) run(out io.Writer) error { - switch o.docTypeString { - case "markdown", "mdown", "md": - return doc.GenMarkdownTree(o.topCmd, o.dest) - case "man": - manHdr := &doc.GenManHeader{Title: "HELM", Section: "1"} - return doc.GenManTree(o.topCmd, manHdr, o.dest) - case "bash": - return o.topCmd.GenBashCompletionFile(filepath.Join(o.dest, "completions.bash")) - default: - return errors.Errorf("unknown doc type %q. Try 'markdown' or 'man'", o.docTypeString) - } -} diff --git a/cmd/helm/env.go b/cmd/helm/env.go deleted file mode 100644 index 2687272..0000000 --- a/cmd/helm/env.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "sort" - - "helm.sh/helm/v3/pkg/cli" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" -) - -var ( - envHelp = ` -Env prints out all the environment information in use by Helm. -` -) - -func newEnvCmd(out io.Writer) *cobra.Command { - o := &envOptions{} - o.settings = cli.New() - - cmd := &cobra.Command{ - Use: "env", - Short: "Helm client environment information", - Long: envHelp, - Args: require.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out) - }, - } - - return cmd -} - -type envOptions struct { - settings *cli.EnvSettings -} - -func (o *envOptions) run(out io.Writer) error { - envVars := o.settings.EnvVars() - - // Sort the variables by alphabetical order. - // This allows for a constant output across calls to 'helm env'. - var keys []string - for k := range envVars { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - fmt.Printf("%s=\"%s\"\n", k, envVars[k]) - } - return nil -} diff --git a/cmd/helm/flags.go b/cmd/helm/flags.go deleted file mode 100644 index 65575a5..0000000 --- a/cmd/helm/flags.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli/output" - "helm.sh/helm/v3/pkg/cli/values" -) - -const outputFlag = "output" - -func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) { - f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL (can specify multiple)") - f.StringArrayVar(&v.Values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") - f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") - f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)") -} - -func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) { - f.StringVar(&c.Version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed") - f.BoolVar(&c.Verify, "verify", false, "verify the package before installing it") - f.StringVar(&c.Keyring, "keyring", defaultKeyring(), "location of public keys used for verification") - f.StringVar(&c.RepoURL, "repo", "", "chart repository url where to locate the requested chart") - f.StringVar(&c.Username, "username", "", "chart repository username where to locate the requested chart") - f.StringVar(&c.Password, "password", "", "chart repository password where to locate the requested chart") - f.StringVar(&c.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") - f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file") - f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") -} - -// bindOutputFlag will add the output flag to the given command and bind the -// value to the given format pointer -func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) { - f := cmd.Flags() - flag := f.VarPF(newOutputValue(output.Table, varRef), outputFlag, "o", - fmt.Sprintf("prints the output in the specified format. Allowed values: %s", strings.Join(output.Formats(), ", "))) - - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - var formatNames []string - for _, format := range output.Formats() { - if strings.HasPrefix(format, toComplete) { - formatNames = append(formatNames, format) - } - } - return formatNames, completion.BashCompDirectiveDefault - }) -} - -type outputValue output.Format - -func newOutputValue(defaultValue output.Format, p *output.Format) *outputValue { - *p = defaultValue - return (*outputValue)(p) -} - -func (o *outputValue) String() string { - // It is much cleaner looking (and technically less allocations) to just - // convert to a string rather than type asserting to the underlying - // output.Format - return string(*o) -} - -func (o *outputValue) Type() string { - return "format" -} - -func (o *outputValue) Set(s string) error { - outfmt, err := output.ParseFormat(s) - if err != nil { - return err - } - *o = outputValue(outfmt) - return nil -} diff --git a/cmd/helm/flags_test.go b/cmd/helm/flags_test.go deleted file mode 100644 index d5576fe..0000000 --- a/cmd/helm/flags_test.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "testing" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/release" - helmtime "helm.sh/helm/v3/pkg/time" -) - -func outputFlagCompletionTest(t *testing.T, cmdName string) { - releasesMockWithStatus := func(info *release.Info, hooks ...*release.Hook) []*release.Release { - info.LastDeployed = helmtime.Unix(1452902400, 0).UTC() - return []*release.Release{{ - Name: "athos", - Namespace: "default", - Info: info, - Chart: &chart.Chart{}, - Hooks: hooks, - }, { - Name: "porthos", - Namespace: "default", - Info: info, - Chart: &chart.Chart{}, - Hooks: hooks, - }, { - Name: "aramis", - Namespace: "default", - Info: info, - Chart: &chart.Chart{}, - Hooks: hooks, - }, { - Name: "dartagnan", - Namespace: "gascony", - Info: info, - Chart: &chart.Chart{}, - Hooks: hooks, - }} - } - - tests := []cmdTestCase{{ - name: "completion for output flag long and before arg", - cmd: fmt.Sprintf("__complete %s --output ''", cmdName), - golden: "output/output-comp.txt", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - }), - }, { - name: "completion for output flag long and after arg", - cmd: fmt.Sprintf("__complete %s aramis --output ''", cmdName), - golden: "output/output-comp.txt", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - }), - }, { - name: "completion for output flag short and before arg", - cmd: fmt.Sprintf("__complete %s -o ''", cmdName), - golden: "output/output-comp.txt", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - }), - }, { - name: "completion for output flag short and after arg", - cmd: fmt.Sprintf("__complete %s aramis -o ''", cmdName), - golden: "output/output-comp.txt", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - }), - }} - runTestCmd(t, tests) -} diff --git a/cmd/helm/get.go b/cmd/helm/get.go deleted file mode 100644 index 7c4854b..0000000 --- a/cmd/helm/get.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" -) - -var getHelp = ` -This command consists of multiple subcommands which can be used to -get extended information about the release, including: - -- The values used to generate the release -- The generated manifest file -- The notes provided by the chart of the release -- The hooks associated with the release -` - -func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "get", - Short: "download extended information of a named release", - Long: getHelp, - Args: require.NoArgs, - } - - cmd.AddCommand(newGetAllCmd(cfg, out)) - cmd.AddCommand(newGetValuesCmd(cfg, out)) - cmd.AddCommand(newGetManifestCmd(cfg, out)) - cmd.AddCommand(newGetHooksCmd(cfg, out)) - cmd.AddCommand(newGetNotesCmd(cfg, out)) - - return cmd -} diff --git a/cmd/helm/get_all.go b/cmd/helm/get_all.go deleted file mode 100644 index 7d893d7..0000000 --- a/cmd/helm/get_all.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli/output" -) - -var getAllHelp = ` -This command prints a human readable collection of information about the -notes, hooks, supplied values, and generated manifest file of the given release. -` - -func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - var template string - client := action.NewGet(cfg) - - cmd := &cobra.Command{ - Use: "all RELEASE_NAME", - Short: "download all information for a named release", - Long: getAllHelp, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - res, err := client.Run(args[0]) - if err != nil { - return err - } - if template != "" { - data := map[string]interface{}{ - "Release": res, - } - return tpl(template, data, out) - } - - return output.Table.Write(out, &statusPrinter{res, true}) - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) == 1 { - return compListRevisions(cfg, args[0]) - } - return nil, completion.BashCompDirectiveNoFileComp - }) - - f.StringVar(&template, "template", "", "go template for formatting the output, eg: {{.Release.Name}}") - - return cmd -} diff --git a/cmd/helm/get_all_test.go b/cmd/helm/get_all_test.go deleted file mode 100644 index a213ac5..0000000 --- a/cmd/helm/get_all_test.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" - - "helm.sh/helm/v3/pkg/release" -) - -func TestGetCmd(t *testing.T) { - tests := []cmdTestCase{{ - name: "get all with a release", - cmd: "get all thomas-guide", - golden: "output/get-release.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})}, - }, { - name: "get all with a formatted release", - cmd: "get all elevated-turkey --template {{.Release.Chart.Metadata.Version}}", - golden: "output/get-release-template.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "elevated-turkey"})}, - }, { - name: "get all requires release name arg", - cmd: "get all", - golden: "output/get-all-no-args.txt", - wantError: true, - }} - runTestCmd(t, tests) -} - -func TestGetAllRevisionCompletion(t *testing.T) { - revisionFlagCompletionTest(t, "get all") -} diff --git a/cmd/helm/get_hooks.go b/cmd/helm/get_hooks.go deleted file mode 100644 index c2087b1..0000000 --- a/cmd/helm/get_hooks.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" -) - -const getHooksHelp = ` -This command downloads hooks for a given release. - -Hooks are formatted in YAML and separated by the YAML '---\n' separator. -` - -func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewGet(cfg) - - cmd := &cobra.Command{ - Use: "hooks RELEASE_NAME", - Short: "download all hooks for a named release", - Long: getHooksHelp, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - res, err := client.Run(args[0]) - if err != nil { - return err - } - for _, hook := range res.Hooks { - fmt.Fprintf(out, "---\n# Source: %s\n%s\n", hook.Path, hook.Manifest) - } - return nil - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) == 1 { - return compListRevisions(cfg, args[0]) - } - return nil, completion.BashCompDirectiveNoFileComp - }) - - return cmd -} diff --git a/cmd/helm/get_hooks_test.go b/cmd/helm/get_hooks_test.go deleted file mode 100644 index 7b9142d..0000000 --- a/cmd/helm/get_hooks_test.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" - - "helm.sh/helm/v3/pkg/release" -) - -func TestGetHooks(t *testing.T) { - tests := []cmdTestCase{{ - name: "get hooks with release", - cmd: "get hooks aeneas", - golden: "output/get-hooks.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})}, - }, { - name: "get hooks without args", - cmd: "get hooks", - golden: "output/get-hooks-no-args.txt", - wantError: true, - }} - runTestCmd(t, tests) -} - -func TestGetHooksRevisionCompletion(t *testing.T) { - revisionFlagCompletionTest(t, "get hooks") -} diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go deleted file mode 100644 index f332bef..0000000 --- a/cmd/helm/get_manifest.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" -) - -var getManifestHelp = ` -This command fetches the generated manifest for a given release. - -A manifest is a YAML-encoded representation of the Kubernetes resources that -were generated from this release's chart(s). If a chart is dependent on other -charts, those resources will also be included in the manifest. -` - -func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewGet(cfg) - - cmd := &cobra.Command{ - Use: "manifest RELEASE_NAME", - Short: "download the manifest for a named release", - Long: getManifestHelp, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - res, err := client.Run(args[0]) - if err != nil { - return err - } - fmt.Fprintln(out, res.Manifest) - return nil - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) == 1 { - return compListRevisions(cfg, args[0]) - } - return nil, completion.BashCompDirectiveNoFileComp - }) - - return cmd -} diff --git a/cmd/helm/get_manifest_test.go b/cmd/helm/get_manifest_test.go deleted file mode 100644 index bd0ffc5..0000000 --- a/cmd/helm/get_manifest_test.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" - - "helm.sh/helm/v3/pkg/release" -) - -func TestGetManifest(t *testing.T) { - tests := []cmdTestCase{{ - name: "get manifest with release", - cmd: "get manifest juno", - golden: "output/get-manifest.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "juno"})}, - }, { - name: "get manifest without args", - cmd: "get manifest", - golden: "output/get-manifest-no-args.txt", - wantError: true, - }} - runTestCmd(t, tests) -} - -func TestGetManifestRevisionCompletion(t *testing.T) { - revisionFlagCompletionTest(t, "get manifest") -} diff --git a/cmd/helm/get_notes.go b/cmd/helm/get_notes.go deleted file mode 100644 index 4491bd9..0000000 --- a/cmd/helm/get_notes.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" -) - -var getNotesHelp = ` -This command shows notes provided by the chart of a named release. -` - -func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewGet(cfg) - - cmd := &cobra.Command{ - Use: "notes RELEASE_NAME", - Short: "download the notes for a named release", - Long: getNotesHelp, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - res, err := client.Run(args[0]) - if err != nil { - return err - } - if len(res.Info.Notes) > 0 { - fmt.Fprintf(out, "NOTES:\n%s\n", res.Info.Notes) - } - return nil - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) == 1 { - return compListRevisions(cfg, args[0]) - } - return nil, completion.BashCompDirectiveNoFileComp - }) - - return cmd -} diff --git a/cmd/helm/get_notes_test.go b/cmd/helm/get_notes_test.go deleted file mode 100644 index a59120b..0000000 --- a/cmd/helm/get_notes_test.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" - - "helm.sh/helm/v3/pkg/release" -) - -func TestGetNotesCmd(t *testing.T) { - tests := []cmdTestCase{{ - name: "get notes of a deployed release", - cmd: "get notes the-limerick", - golden: "output/get-notes.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "the-limerick"})}, - }, { - name: "get notes without args", - cmd: "get notes", - golden: "output/get-notes-no-args.txt", - wantError: true, - }} - runTestCmd(t, tests) -} - -func TestGetNotesRevisionCompletion(t *testing.T) { - revisionFlagCompletionTest(t, "get notes") -} diff --git a/cmd/helm/get_values.go b/cmd/helm/get_values.go deleted file mode 100644 index a8c5acc..0000000 --- a/cmd/helm/get_values.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli/output" -) - -var getValuesHelp = ` -This command downloads a values file for a given release. -` - -type valuesWriter struct { - vals map[string]interface{} - allValues bool -} - -func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - var outfmt output.Format - client := action.NewGetValues(cfg) - - cmd := &cobra.Command{ - Use: "values RELEASE_NAME", - Short: "download the values file for a named release", - Long: getValuesHelp, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - vals, err := client.Run(args[0]) - if err != nil { - return err - } - return outfmt.Write(out, &valuesWriter{vals, client.AllValues}) - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.IntVar(&client.Version, "revision", 0, "get the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) == 1 { - return compListRevisions(cfg, args[0]) - } - return nil, completion.BashCompDirectiveNoFileComp - }) - f.BoolVarP(&client.AllValues, "all", "a", false, "dump all (computed) values") - bindOutputFlag(cmd, &outfmt) - - return cmd -} - -func (v valuesWriter) WriteTable(out io.Writer) error { - if v.allValues { - fmt.Fprintln(out, "COMPUTED VALUES:") - } else { - fmt.Fprintln(out, "USER-SUPPLIED VALUES:") - } - return output.EncodeYAML(out, v.vals) -} - -func (v valuesWriter) WriteJSON(out io.Writer) error { - return output.EncodeJSON(out, v.vals) -} - -func (v valuesWriter) WriteYAML(out io.Writer) error { - return output.EncodeYAML(out, v.vals) -} diff --git a/cmd/helm/get_values_test.go b/cmd/helm/get_values_test.go deleted file mode 100644 index ecd92d3..0000000 --- a/cmd/helm/get_values_test.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" - - "helm.sh/helm/v3/pkg/release" -) - -func TestGetValuesCmd(t *testing.T) { - tests := []cmdTestCase{{ - name: "get values with a release", - cmd: "get values thomas-guide", - golden: "output/get-values.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})}, - }, { - name: "get values requires release name arg", - cmd: "get values", - golden: "output/get-values-args.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})}, - wantError: true, - }, { - name: "get values thomas-guide (all)", - cmd: "get values thomas-guide --all", - golden: "output/get-values-all.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})}, - }, { - name: "get values to json", - cmd: "get values thomas-guide --output json", - golden: "output/values.json", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})}, - }, { - name: "get values to yaml", - cmd: "get values thomas-guide --output yaml", - golden: "output/values.yaml", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})}, - }} - runTestCmd(t, tests) -} - -func TestGetValuesRevisionCompletion(t *testing.T) { - revisionFlagCompletionTest(t, "get values") -} - -func TestGetValuesOutputCompletion(t *testing.T) { - outputFlagCompletionTest(t, "get values") -} diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go deleted file mode 100644 index 112d512..0000000 --- a/cmd/helm/helm.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main // import "helm.sh/helm/v3/cmd/helm" - -import ( - "flag" - "fmt" - "log" - "os" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "k8s.io/klog" - - // Import to initialize client auth plugins. - _ "k8s.io/client-go/plugin/pkg/client/auth" - - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/gates" -) - -// FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work -const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI") - -var ( - settings = cli.New() -) - -func init() { - log.SetFlags(log.Lshortfile) -} - -func debug(format string, v ...interface{}) { - if settings.Debug { - format = fmt.Sprintf("[debug] %s\n", format) - log.Output(2, fmt.Sprintf(format, v...)) - } -} - -func initKubeLogs() { - pflag.CommandLine.SetNormalizeFunc(wordSepNormalizeFunc) - gofs := flag.NewFlagSet("klog", flag.ExitOnError) - klog.InitFlags(gofs) - pflag.CommandLine.AddGoFlagSet(gofs) - pflag.CommandLine.Set("logtostderr", "true") -} - -func main() { - initKubeLogs() - - actionConfig := new(action.Configuration) - cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:]) - - if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), os.Getenv("HELM_DRIVER"), debug); err != nil { - log.Fatal(err) - } - - if err := cmd.Execute(); err != nil { - debug("%+v", err) - switch e := err.(type) { - case pluginError: - os.Exit(e.code) - default: - os.Exit(1) - } - } -} - -// wordSepNormalizeFunc changes all flags that contain "_" separators -func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { - return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-")) -} - -func checkOCIFeatureGate() func(_ *cobra.Command, _ []string) error { - return func(_ *cobra.Command, _ []string) error { - if !FeatureGateOCI.IsEnabled() { - return FeatureGateOCI.Error() - } - return nil - } -} diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go deleted file mode 100644 index 5f9d80a..0000000 --- a/cmd/helm/helm_test.go +++ /dev/null @@ -1,246 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "os/exec" - "runtime" - "strings" - "testing" - - shellwords "github.com/mattn/go-shellwords" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/internal/test" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/cli" - kubefake "helm.sh/helm/v3/pkg/kube/fake" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/storage" - "helm.sh/helm/v3/pkg/storage/driver" - "helm.sh/helm/v3/pkg/time" -) - -func testTimestamper() time.Time { return time.Unix(242085845, 0).UTC() } - -func init() { - action.Timestamper = testTimestamper -} - -func runTestCmd(t *testing.T, tests []cmdTestCase) { - t.Helper() - for _, tt := range tests { - for i := 0; i <= tt.repeat; i++ { - t.Run(tt.name, func(t *testing.T) { - defer resetEnv()() - - storage := storageFixture() - for _, rel := range tt.rels { - if err := storage.Create(rel); err != nil { - t.Fatal(err) - } - } - t.Logf("running cmd (attempt %d): %s", i+1, tt.cmd) - _, out, err := executeActionCommandC(storage, tt.cmd) - if (err != nil) != tt.wantError { - t.Errorf("expected error, got '%v'", err) - } - if tt.golden != "" { - test.AssertGoldenString(t, out, tt.golden) - } - }) - } - } -} - -func runTestActionCmd(t *testing.T, tests []cmdTestCase) { - t.Helper() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer resetEnv()() - - store := storageFixture() - for _, rel := range tt.rels { - store.Create(rel) - } - _, out, err := executeActionCommandC(store, tt.cmd) - if (err != nil) != tt.wantError { - t.Errorf("expected error, got '%v'", err) - } - if tt.golden != "" { - test.AssertGoldenString(t, out, tt.golden) - } - }) - } -} - -func storageFixture() *storage.Storage { - return storage.Init(driver.NewMemory()) -} - -// go-shellwords does not handle empty arguments properly -// https://github.com/mattn/go-shellwords/issues/5#issuecomment-573431458 -// -// This method checks if the last argument was an empty one, -// and if go-shellwords missed it, we add it ourselves. -// -// This is important for completion tests as completion often -// uses an empty last parameter. -func checkLastEmpty(in string, out []string) []string { - lastIndex := len(in) - 1 - - if lastIndex >= 1 && (in[lastIndex] == '"' && in[lastIndex-1] == '"' || - in[lastIndex] == '\'' && in[lastIndex-1] == '\'') { - // The last parameter of 'in' was empty ("" or ''), let's make sure it was detected. - if len(out) > 0 && out[len(out)-1] != "" { - // Bug from go-shellwords: - // 'out' does not have the empty parameter. We add it ourselves as a workaround. - out = append(out, "") - } else { - fmt.Println("WARNING: go-shellwords seems to have been fixed. This workaround can be removed.") - } - } - return out -} - -func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) { - args, err := shellwords.Parse(cmd) - if err != nil { - return nil, "", err - } - // Workaround the bug in shellwords - args = checkLastEmpty(cmd, args) - - buf := new(bytes.Buffer) - - actionConfig := &action.Configuration{ - Releases: store, - KubeClient: &kubefake.PrintingKubeClient{Out: ioutil.Discard}, - Capabilities: chartutil.DefaultCapabilities, - Log: func(format string, v ...interface{}) {}, - } - - root := newRootCmd(actionConfig, buf, args) - root.SetOutput(buf) - root.SetArgs(args) - - c, err := root.ExecuteC() - - return c, buf.String(), err -} - -// cmdTestCase describes a test case that works with releases. -type cmdTestCase struct { - name string - cmd string - golden string - wantError bool - // Rels are the available releases at the start of the test. - rels []*release.Release - // Number of repeats (in case a feature was previously flaky and the test checks - // it's now stably producing identical results). 0 means test is run exactly once. - repeat int -} - -func executeActionCommand(cmd string) (*cobra.Command, string, error) { - return executeActionCommandC(storageFixture(), cmd) -} - -func resetEnv() func() { - origEnv := os.Environ() - return func() { - os.Clearenv() - for _, pair := range origEnv { - kv := strings.SplitN(pair, "=", 2) - os.Setenv(kv[0], kv[1]) - } - settings = cli.New() - } -} - -func testChdir(t *testing.T, dir string) func() { - t.Helper() - old, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - if err := os.Chdir(dir); err != nil { - t.Fatal(err) - } - return func() { os.Chdir(old) } -} - -func TestPluginExitCode(t *testing.T) { - if os.Getenv("RUN_MAIN_FOR_TESTING") == "1" { - os.Args = []string{"helm", "exitwith", "2"} - - // We DO call helm's main() here. So this looks like a normal `helm` process. - main() - - // As main calls os.Exit, we never reach this line. - // But the test called this block of code catches and verifies the exit code. - return - } - - // Currently, plugins assume a Linux subsystem. Skip the execution - // tests until this is fixed - if runtime.GOOS != "windows" { - // Do a second run of this specific test(TestPluginExitCode) with RUN_MAIN_FOR_TESTING=1 set, - // So that the second run is able to run main() and this first run can verify the exit status returned by that. - // - // This technique originates from https://talks.golang.org/2014/testing.slide#23. - cmd := exec.Command(os.Args[0], "-test.run=TestPluginExitCode") - cmd.Env = append( - os.Environ(), - "RUN_MAIN_FOR_TESTING=1", - // See pkg/cli/environment.go for which envvars can be used for configuring these passes - // and also see plugin_test.go for how a plugin env can be set up. - // We just does the same setup as plugin_test.go via envvars - "HELM_PLUGINS=testdata/helmhome/helm/plugins", - "HELM_REPOSITORY_CONFIG=testdata/helmhome/helm/repositories.yaml", - "HELM_REPOSITORY_CACHE=testdata/helmhome/helm/repository", - ) - stdout := &bytes.Buffer{} - stderr := &bytes.Buffer{} - cmd.Stdout = stdout - cmd.Stderr = stderr - err := cmd.Run() - exiterr, ok := err.(*exec.ExitError) - - if !ok { - t.Fatalf("Unexpected error returned by os.Exit: %T", err) - } - - if stdout.String() != "" { - t.Errorf("Expected no write to stdout: Got %q", stdout.String()) - } - - expectedStderr := "Error: plugin \"exitwith\" exited with error\n" - if stderr.String() != expectedStderr { - t.Errorf("Expected %q written to stderr: Got %q", expectedStderr, stderr.String()) - } - - if exiterr.ExitCode() != 2 { - t.Errorf("Expected exit code 2: Got %d", exiterr.ExitCode()) - } - } -} diff --git a/cmd/helm/history.go b/cmd/helm/history.go deleted file mode 100644 index 3ef542e..0000000 --- a/cmd/helm/history.go +++ /dev/null @@ -1,201 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "strconv" - "time" - - "github.com/gosuri/uitable" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/cli/output" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/releaseutil" - helmtime "helm.sh/helm/v3/pkg/time" -) - -var historyHelp = ` -History prints historical revisions for a given release. - -A default maximum of 256 revisions will be returned. Setting '--max' -configures the maximum length of the revision list returned. - -The historical release set is printed as a formatted table, e.g: - - $ helm history angry-bird - REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION - 1 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Initial install - 2 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Upgraded successfully - 3 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Rolled back to 2 - 4 Mon Oct 3 10:15:13 2016 deployed alpine-0.1.0 1.0 Upgraded successfully -` - -func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewHistory(cfg) - var outfmt output.Format - - cmd := &cobra.Command{ - Use: "history RELEASE_NAME", - Long: historyHelp, - Short: "fetch release history", - Aliases: []string{"hist"}, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - history, err := getHistory(client, args[0]) - if err != nil { - return err - } - - return outfmt.Write(out, history) - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.IntVar(&client.Max, "max", 256, "maximum number of revision to include in history") - bindOutputFlag(cmd, &outfmt) - - return cmd -} - -type releaseInfo struct { - Revision int `json:"revision"` - Updated helmtime.Time `json:"updated"` - Status string `json:"status"` - Chart string `json:"chart"` - AppVersion string `json:"app_version"` - Description string `json:"description"` -} - -type releaseHistory []releaseInfo - -func (r releaseHistory) WriteJSON(out io.Writer) error { - return output.EncodeJSON(out, r) -} - -func (r releaseHistory) WriteYAML(out io.Writer) error { - return output.EncodeYAML(out, r) -} - -func (r releaseHistory) WriteTable(out io.Writer) error { - tbl := uitable.New() - tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "DESCRIPTION") - for _, item := range r { - tbl.AddRow(item.Revision, item.Updated.Format(time.ANSIC), item.Status, item.Chart, item.AppVersion, item.Description) - } - return output.EncodeTable(out, tbl) -} - -func getHistory(client *action.History, name string) (releaseHistory, error) { - hist, err := client.Run(name) - if err != nil { - return nil, err - } - - releaseutil.Reverse(hist, releaseutil.SortByRevision) - - var rels []*release.Release - for i := 0; i < min(len(hist), client.Max); i++ { - rels = append(rels, hist[i]) - } - - if len(rels) == 0 { - return releaseHistory{}, nil - } - - releaseHistory := getReleaseHistory(rels) - - return releaseHistory, nil -} - -func getReleaseHistory(rls []*release.Release) (history releaseHistory) { - for i := len(rls) - 1; i >= 0; i-- { - r := rls[i] - c := formatChartname(r.Chart) - s := r.Info.Status.String() - v := r.Version - d := r.Info.Description - a := formatAppVersion(r.Chart) - - rInfo := releaseInfo{ - Revision: v, - Status: s, - Chart: c, - AppVersion: a, - Description: d, - } - if !r.Info.LastDeployed.IsZero() { - rInfo.Updated = r.Info.LastDeployed - - } - history = append(history, rInfo) - } - - return history -} - -func formatChartname(c *chart.Chart) string { - if c == nil || c.Metadata == nil { - // This is an edge case that has happened in prod, though we don't - // know how: https://github.com/helm/helm/issues/1347 - return "MISSING" - } - return fmt.Sprintf("%s-%s", c.Name(), c.Metadata.Version) -} - -func formatAppVersion(c *chart.Chart) string { - if c == nil || c.Metadata == nil { - // This is an edge case that has happened in prod, though we don't - // know how: https://github.com/helm/helm/issues/1347 - return "MISSING" - } - return c.AppVersion() -} - -func min(x, y int) int { - if x < y { - return x - } - return y -} - -func compListRevisions(cfg *action.Configuration, releaseName string) ([]string, completion.BashCompDirective) { - client := action.NewHistory(cfg) - - var revisions []string - if hist, err := client.Run(releaseName); err == nil { - for _, release := range hist { - revisions = append(revisions, strconv.Itoa(release.Version)) - } - return revisions, completion.BashCompDirectiveDefault - } - return nil, completion.BashCompDirectiveError -} diff --git a/cmd/helm/history_test.go b/cmd/helm/history_test.go deleted file mode 100644 index c9bfb64..0000000 --- a/cmd/helm/history_test.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "testing" - - "helm.sh/helm/v3/pkg/release" -) - -func TestHistoryCmd(t *testing.T) { - mk := func(name string, vers int, status release.Status) *release.Release { - return release.Mock(&release.MockReleaseOptions{ - Name: name, - Version: vers, - Status: status, - }) - } - - tests := []cmdTestCase{{ - name: "get history for release", - cmd: "history angry-bird", - rels: []*release.Release{ - mk("angry-bird", 4, release.StatusDeployed), - mk("angry-bird", 3, release.StatusSuperseded), - mk("angry-bird", 2, release.StatusSuperseded), - mk("angry-bird", 1, release.StatusSuperseded), - }, - golden: "output/history.txt", - }, { - name: "get history with max limit set", - cmd: "history angry-bird --max 2", - rels: []*release.Release{ - mk("angry-bird", 4, release.StatusDeployed), - mk("angry-bird", 3, release.StatusSuperseded), - }, - golden: "output/history-limit.txt", - }, { - name: "get history with yaml output format", - cmd: "history angry-bird --output yaml", - rels: []*release.Release{ - mk("angry-bird", 4, release.StatusDeployed), - mk("angry-bird", 3, release.StatusSuperseded), - }, - golden: "output/history.yaml", - }, { - name: "get history with json output format", - cmd: "history angry-bird --output json", - rels: []*release.Release{ - mk("angry-bird", 4, release.StatusDeployed), - mk("angry-bird", 3, release.StatusSuperseded), - }, - golden: "output/history.json", - }} - runTestCmd(t, tests) -} - -func TestHistoryOutputCompletion(t *testing.T) { - outputFlagCompletionTest(t, "history") -} - -func revisionFlagCompletionTest(t *testing.T, cmdName string) { - mk := func(name string, vers int, status release.Status) *release.Release { - return release.Mock(&release.MockReleaseOptions{ - Name: name, - Version: vers, - Status: status, - }) - } - - releases := []*release.Release{ - mk("musketeers", 11, release.StatusDeployed), - mk("musketeers", 10, release.StatusSuperseded), - mk("musketeers", 9, release.StatusSuperseded), - mk("musketeers", 8, release.StatusSuperseded), - } - - tests := []cmdTestCase{{ - name: "completion for revision flag", - cmd: fmt.Sprintf("__complete %s musketeers --revision ''", cmdName), - rels: releases, - golden: "output/revision-comp.txt", - }, { - name: "completion for revision flag with too few args", - cmd: fmt.Sprintf("__complete %s --revision ''", cmdName), - rels: releases, - golden: "output/revision-wrong-args-comp.txt", - }, { - name: "completion for revision flag with too many args", - cmd: fmt.Sprintf("__complete %s three musketeers --revision ''", cmdName), - rels: releases, - golden: "output/revision-wrong-args-comp.txt", - }} - runTestCmd(t, tests) -} diff --git a/cmd/helm/install.go b/cmd/helm/install.go deleted file mode 100644 index 0f548c9..0000000 --- a/cmd/helm/install.go +++ /dev/null @@ -1,245 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "time" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/cli/output" - "helm.sh/helm/v3/pkg/cli/values" - "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/release" -) - -const installDesc = ` -This command installs a chart archive. - -The install argument must be a chart reference, a path to a packaged chart, -a path to an unpacked chart directory or a URL. - -To override values in a chart, use either the '--values' flag and pass in a file -or use the '--set' flag and pass configuration from the command line, to force -a string value use '--set-string'. In case a value is large and therefore -you want not to use neither '--values' nor '--set', use '--set-file' to read the -single large value from file. - - $ helm install -f myvalues.yaml myredis ./redis - -or - - $ helm install --set name=prod myredis ./redis - -or - - $ helm install --set-string long_int=1234567890 myredis ./redis - -or - $ helm install --set-file my_script=dothings.sh myredis ./redis - -You can specify the '--values'/'-f' flag multiple times. The priority will be given to the -last (right-most) file specified. For example, if both myvalues.yaml and override.yaml -contained a key called 'Test', the value set in override.yaml would take precedence: - - $ helm install -f myvalues.yaml -f override.yaml myredis ./redis - -You can specify the '--set' flag multiple times. The priority will be given to the -last (right-most) set specified. For example, if both 'bar' and 'newbar' values are -set for a key called 'foo', the 'newbar' value would take precedence: - - $ helm install --set foo=bar --set foo=newbar myredis ./redis - - -To check the generated manifests of a release without installing the chart, -the '--debug' and '--dry-run' flags can be combined. - -If --verify is set, the chart MUST have a provenance file, and the provenance -file MUST pass all verification steps. - -There are five different ways you can express the chart you want to install: - -1. By chart reference: helm install mymaria example/mariadb -2. By path to a packaged chart: helm install mynginx ./nginx-1.2.3.tgz -3. By path to an unpacked chart directory: helm install mynginx ./nginx -4. By absolute URL: helm install mynginx https://example.com/charts/nginx-1.2.3.tgz -5. By chart reference and repo url: helm install --repo https://example.com/charts/ mynginx nginx - -CHART REFERENCES - -A chart reference is a convenient way of referencing a chart in a chart repository. - -When you use a chart reference with a repo prefix ('example/mariadb'), Helm will look in the local -configuration for a chart repository named 'example', and will then look for a -chart in that repository whose name is 'mariadb'. It will install the latest stable version of that chart -until you specify '--devel' flag to also include development version (alpha, beta, and release candidate releases), or -supply a version number with the '--version' flag. - -To see the list of chart repositories, use 'helm repo list'. To search for -charts in a repository, use 'helm search'. -` - -func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewInstall(cfg) - valueOpts := &values.Options{} - var outfmt output.Format - - cmd := &cobra.Command{ - Use: "install [NAME] [CHART]", - Short: "install a chart", - Long: installDesc, - Args: require.MinimumNArgs(1), - RunE: func(_ *cobra.Command, args []string) error { - rel, err := runInstall(args, client, valueOpts, out) - if err != nil { - return err - } - - return outfmt.Write(out, &statusPrinter{rel, settings.Debug}) - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - return compInstall(args, toComplete, client) - }) - - addInstallFlags(cmd.Flags(), client, valueOpts) - bindOutputFlag(cmd, &outfmt) - - return cmd -} - -func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) { - f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install") - f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install") - f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production") - f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") - f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout") - f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)") - f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release") - f.StringVar(&client.Description, "description", "", "add a custom description") - f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") - f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "run helm dependency update before installing the chart") - f.BoolVar(&client.Atomic, "atomic", false, "if set, installation process purges chart on fail. The --wait flag will be set automatically if --atomic is used") - f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present") - f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") - addValueOptionsFlags(f, valueOpts) - addChartPathOptionsFlags(f, &client.ChartPathOptions) -} - -func runInstall(args []string, client *action.Install, valueOpts *values.Options, out io.Writer) (*release.Release, error) { - debug("Original chart version: %q", client.Version) - if client.Version == "" && client.Devel { - debug("setting version to >0.0.0-0") - client.Version = ">0.0.0-0" - } - - name, chart, err := client.NameAndChart(args) - if err != nil { - return nil, err - } - client.ReleaseName = name - - cp, err := client.ChartPathOptions.LocateChart(chart, settings) - if err != nil { - return nil, err - } - - debug("CHART PATH: %s\n", cp) - - p := getter.All(settings) - vals, err := valueOpts.MergeValues(p) - if err != nil { - return nil, err - } - - // Check chart dependencies to make sure all are present in /charts - chartRequested, err := loader.Load(cp) - if err != nil { - return nil, err - } - - validInstallableChart, err := isChartInstallable(chartRequested) - if !validInstallableChart { - return nil, err - } - - if chartRequested.Metadata.Deprecated { - fmt.Fprintln(out, "WARNING: This chart is deprecated") - } - - if req := chartRequested.Metadata.Dependencies; req != nil { - // If CheckDependencies returns an error, we have unfulfilled dependencies. - // As of Helm 2.4.0, this is treated as a stopping condition: - // https://github.com/helm/helm/issues/2209 - if err := action.CheckDependencies(chartRequested, req); err != nil { - if client.DependencyUpdate { - man := &downloader.Manager{ - Out: out, - ChartPath: cp, - Keyring: client.ChartPathOptions.Keyring, - SkipUpdate: false, - Getters: p, - RepositoryConfig: settings.RepositoryConfig, - RepositoryCache: settings.RepositoryCache, - } - if err := man.Update(); err != nil { - return nil, err - } - } else { - return nil, err - } - } - } - - client.Namespace = settings.Namespace() - return client.Run(chartRequested, vals) -} - -// isChartInstallable validates if a chart can be installed -// -// Application chart type is only installable -func isChartInstallable(ch *chart.Chart) (bool, error) { - switch ch.Metadata.Type { - case "", "application": - return true, nil - } - return false, errors.Errorf("%s charts are not installable", ch.Metadata.Type) -} - -// Provide dynamic auto-completion for the install and template commands -func compInstall(args []string, toComplete string, client *action.Install) ([]string, completion.BashCompDirective) { - requiredArgs := 1 - if client.GenerateName { - requiredArgs = 0 - } - if len(args) == requiredArgs { - return compListCharts(toComplete, true) - } - return nil, completion.BashCompDirectiveNoFileComp -} diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go deleted file mode 100644 index 5797202..0000000 --- a/cmd/helm/install_test.go +++ /dev/null @@ -1,199 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" -) - -func TestInstall(t *testing.T) { - tests := []cmdTestCase{ - // Install, base case - { - name: "basic install", - cmd: "install aeneas testdata/testcharts/empty --namespace default", - golden: "output/install.txt", - }, - - // Install, values from cli - { - name: "install with values", - cmd: "install virgil testdata/testcharts/alpine --set test.Name=bar", - golden: "output/install-with-values.txt", - }, - // Install, values from cli via multiple --set - { - name: "install with multiple values", - cmd: "install virgil testdata/testcharts/alpine --set test.Color=yellow --set test.Name=banana", - golden: "output/install-with-multiple-values.txt", - }, - // Install, values from yaml - { - name: "install with values file", - cmd: "install virgil testdata/testcharts/alpine -f testdata/testcharts/alpine/extra_values.yaml", - golden: "output/install-with-values-file.txt", - }, - // Install, no hooks - { - name: "install without hooks", - cmd: "install aeneas testdata/testcharts/alpine --no-hooks --set test.Name=hello", - golden: "output/install-no-hooks.txt", - }, - // Install, values from multiple yaml - { - name: "install with values", - cmd: "install virgil testdata/testcharts/alpine -f testdata/testcharts/alpine/extra_values.yaml -f testdata/testcharts/alpine/more_values.yaml", - golden: "output/install-with-multiple-values-files.txt", - }, - // Install, no charts - { - name: "install with no chart specified", - cmd: "install", - golden: "output/install-no-args.txt", - wantError: true, - }, - // Install, re-use name - { - name: "install and replace release", - cmd: "install aeneas testdata/testcharts/empty --replace", - golden: "output/install-and-replace.txt", - }, - // Install, with timeout - { - name: "install with a timeout", - cmd: "install foobar testdata/testcharts/empty --timeout 120s", - golden: "output/install-with-timeout.txt", - }, - // Install, with wait - { - name: "install with a wait", - cmd: "install apollo testdata/testcharts/empty --wait", - golden: "output/install-with-wait.txt", - }, - // Install, using the name-template - { - name: "install with name-template", - cmd: "install testdata/testcharts/empty --name-template '{{upper \"foobar\"}}'", - golden: "output/install-name-template.txt", - }, - // Install, perform chart verification along the way. - { - name: "install with verification, missing provenance", - cmd: "install bogus testdata/testcharts/compressedchart-0.1.0.tgz --verify --keyring testdata/helm-test-key.pub", - wantError: true, - }, - { - name: "install with verification, directory instead of file", - cmd: "install bogus testdata/testcharts/signtest --verify --keyring testdata/helm-test-key.pub", - wantError: true, - }, - { - name: "install with verification, valid", - cmd: "install signtest testdata/testcharts/signtest-0.1.0.tgz --verify --keyring testdata/helm-test-key.pub", - }, - // Install, chart with missing dependencies in /charts - { - name: "install chart with missing dependencies", - cmd: "install nodeps testdata/testcharts/chart-missing-deps", - wantError: true, - }, - // Install, chart with bad dependencies in Chart.yaml in /charts - { - name: "install chart with bad dependencies in Chart.yaml", - cmd: "install badreq testdata/testcharts/chart-bad-requirements", - wantError: true, - }, - // Install, chart with library chart dependency - { - name: "install chart with library chart dependency", - cmd: "install withlibchartp testdata/testcharts/chart-with-lib-dep", - }, - // Install, library chart - { - name: "install library chart", - cmd: "install libchart testdata/testcharts/lib-chart", - wantError: true, - golden: "output/template-lib-chart.txt", - }, - // Install, chart with bad type - { - name: "install chart with bad type", - cmd: "install badtype testdata/testcharts/chart-bad-type", - wantError: true, - golden: "output/install-chart-bad-type.txt", - }, - // Install, values from yaml, schematized - { - name: "install with schema file", - cmd: "install schema testdata/testcharts/chart-with-schema", - golden: "output/schema.txt", - }, - // Install, values from yaml, schematized with errors - { - name: "install with schema file, with errors", - cmd: "install schema testdata/testcharts/chart-with-schema-negative", - wantError: true, - golden: "output/schema-negative.txt", - }, - // Install, values from yaml, extra values from yaml, schematized with errors - { - name: "install with schema file, extra values from yaml, with errors", - cmd: "install schema testdata/testcharts/chart-with-schema -f testdata/testcharts/chart-with-schema/extra-values.yaml", - wantError: true, - golden: "output/schema-negative.txt", - }, - // Install, values from yaml, extra values from cli, schematized with errors - { - name: "install with schema file, extra values from cli, with errors", - cmd: "install schema testdata/testcharts/chart-with-schema --set age=-5", - wantError: true, - golden: "output/schema-negative-cli.txt", - }, - // Install with subchart, values from yaml, schematized with errors - { - name: "install with schema file and schematized subchart, with errors", - cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart", - wantError: true, - golden: "output/subchart-schema-negative.txt", - }, - // Install with subchart, values from yaml, extra values from cli, schematized with errors - { - name: "install with schema file and schematized subchart, extra values from cli", - cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart --set lastname=doe --set subchart-with-schema.age=25", - golden: "output/subchart-schema-cli.txt", - }, - // Install with subchart, values from yaml, extra values from cli, schematized with errors - { - name: "install with schema file and schematized subchart, extra values from cli, with errors", - cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart --set lastname=doe --set subchart-with-schema.age=-25", - wantError: true, - golden: "output/subchart-schema-cli-negative.txt", - }, - // Install deprecated chart - { - name: "install with warning about deprecated chart", - cmd: "install aeneas testdata/testcharts/deprecated --namespace default", - golden: "output/deprecated-chart.txt", - }, - } - - runTestActionCmd(t, tests) -} - -func TestInstallOutputCompletion(t *testing.T) { - outputFlagCompletionTest(t, "install") -} diff --git a/cmd/helm/lint.go b/cmd/helm/lint.go deleted file mode 100644 index 9a2e8d3..0000000 --- a/cmd/helm/lint.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "strings" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli/values" - "helm.sh/helm/v3/pkg/getter" -) - -var longLintHelp = ` -This command takes a path to a chart and runs a series of tests to verify that -the chart is well-formed. - -If the linter encounters things that will cause the chart to fail installation, -it will emit [ERROR] messages. If it encounters issues that break with convention -or recommendation, it will emit [WARNING] messages. -` - -func newLintCmd(out io.Writer) *cobra.Command { - client := action.NewLint() - valueOpts := &values.Options{} - - cmd := &cobra.Command{ - Use: "lint PATH", - Short: "examines a chart for possible issues", - Long: longLintHelp, - RunE: func(cmd *cobra.Command, args []string) error { - paths := []string{"."} - if len(args) > 0 { - paths = args - } - client.Namespace = settings.Namespace() - vals, err := valueOpts.MergeValues(getter.All(settings)) - if err != nil { - return err - } - - var message strings.Builder - failed := 0 - - for _, path := range paths { - fmt.Fprintf(&message, "==> Linting %s\n", path) - - result := client.Run([]string{path}, vals) - - // All the Errors that are generated by a chart - // that failed a lint will be included in the - // results.Messages so we only need to print - // the Errors if there are no Messages. - if len(result.Messages) == 0 { - for _, err := range result.Errors { - fmt.Fprintf(&message, "Error %s\n", err) - } - } - - for _, msg := range result.Messages { - fmt.Fprintf(&message, "%s\n", msg) - } - - if len(result.Errors) != 0 { - failed++ - } - - // Adding extra new line here to break up the - // results, stops this from being a big wall of - // text and makes it easier to follow. - fmt.Fprint(&message, "\n") - } - - fmt.Fprintf(out, message.String()) - - summary := fmt.Sprintf("%d chart(s) linted, %d chart(s) failed", len(paths), failed) - if failed > 0 { - return errors.New(summary) - } - fmt.Fprintln(out, summary) - return nil - }, - } - - f := cmd.Flags() - f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings") - addValueOptionsFlags(f, valueOpts) - - return cmd -} diff --git a/cmd/helm/list.go b/cmd/helm/list.go deleted file mode 100644 index 4b65208..0000000 --- a/cmd/helm/list.go +++ /dev/null @@ -1,190 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "os" - "strconv" - - "github.com/gosuri/uitable" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli/output" - "helm.sh/helm/v3/pkg/release" -) - -var listHelp = ` -This command lists all of the releases for a specified namespace (uses current namespace context if namespace not specified). - -By default, it lists only releases that are deployed or failed. Flags like -'--uninstalled' and '--all' will alter this behavior. Such flags can be combined: -'--uninstalled --failed'. - -By default, items are sorted alphabetically. Use the '-d' flag to sort by -release date. - -If the --filter flag is provided, it will be treated as a filter. Filters are -regular expressions (Perl compatible) that are applied to the list of releases. -Only items that match the filter will be returned. - - $ helm list --filter 'ara[a-z]+' - NAME UPDATED CHART - maudlin-arachnid Mon May 9 16:07:08 2016 alpine-0.1.0 - -If no results are found, 'helm list' will exit 0, but with no output (or in -the case of no '-q' flag, only headers). - -By default, up to 256 items may be returned. To limit this, use the '--max' flag. -Setting '--max' to 0 will not return all results. Rather, it will return the -server's default, which may be much higher than 256. Pairing the '--max' -flag with the '--offset' flag allows you to page through results. -` - -func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewList(cfg) - var outfmt output.Format - - cmd := &cobra.Command{ - Use: "list", - Short: "list releases", - Long: listHelp, - Aliases: []string{"ls"}, - Args: require.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - if client.AllNamespaces { - if err := cfg.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), debug); err != nil { - return err - } - } - client.SetStateMask() - - results, err := client.Run() - if err != nil { - return err - } - - if client.Short { - for _, res := range results { - fmt.Fprintln(out, res.Name) - } - return nil - } - - return outfmt.Write(out, newReleaseListWriter(results)) - }, - } - - f := cmd.Flags() - f.BoolVarP(&client.Short, "short", "q", false, "output short (quiet) listing format") - f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date") - f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order") - f.BoolVarP(&client.All, "all", "a", false, "show all releases without any filter applied") - f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases (if 'helm uninstall --keep-history' was used)") - f.BoolVar(&client.Superseded, "superseded", false, "show superseded releases") - f.BoolVar(&client.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled") - f.BoolVar(&client.Deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled") - f.BoolVar(&client.Failed, "failed", false, "show failed releases") - f.BoolVar(&client.Pending, "pending", false, "show pending releases") - f.BoolVarP(&client.AllNamespaces, "all-namespaces", "A", false, "list releases across all namespaces") - f.IntVarP(&client.Limit, "max", "m", 256, "maximum number of releases to fetch") - f.IntVar(&client.Offset, "offset", 0, "next release name in the list, used to offset from start value") - f.StringVarP(&client.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results") - bindOutputFlag(cmd, &outfmt) - - return cmd -} - -type releaseElement struct { - Name string `json:"name"` - Namespace string `json:"namespace"` - Revision string `json:"revision"` - Updated string `json:"updated"` - Status string `json:"status"` - Chart string `json:"chart"` - AppVersion string `json:"app_version"` -} - -type releaseListWriter struct { - releases []releaseElement -} - -func newReleaseListWriter(releases []*release.Release) *releaseListWriter { - // Initialize the array so no results returns an empty array instead of null - elements := make([]releaseElement, 0, len(releases)) - for _, r := range releases { - element := releaseElement{ - Name: r.Name, - Namespace: r.Namespace, - Revision: strconv.Itoa(r.Version), - Status: r.Info.Status.String(), - Chart: fmt.Sprintf("%s-%s", r.Chart.Metadata.Name, r.Chart.Metadata.Version), - AppVersion: r.Chart.Metadata.AppVersion, - } - t := "-" - if tspb := r.Info.LastDeployed; !tspb.IsZero() { - t = tspb.String() - } - element.Updated = t - elements = append(elements, element) - } - return &releaseListWriter{elements} -} - -func (r *releaseListWriter) WriteTable(out io.Writer) error { - table := uitable.New() - table.AddRow("NAME", "NAMESPACE", "REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION") - for _, r := range r.releases { - table.AddRow(r.Name, r.Namespace, r.Revision, r.Updated, r.Status, r.Chart, r.AppVersion) - } - return output.EncodeTable(out, table) -} - -func (r *releaseListWriter) WriteJSON(out io.Writer) error { - return output.EncodeJSON(out, r.releases) -} - -func (r *releaseListWriter) WriteYAML(out io.Writer) error { - return output.EncodeYAML(out, r.releases) -} - -// Provide dynamic auto-completion for release names -func compListReleases(toComplete string, cfg *action.Configuration) ([]string, completion.BashCompDirective) { - completion.CompDebugln(fmt.Sprintf("compListReleases with toComplete %s", toComplete)) - - client := action.NewList(cfg) - client.All = true - client.Limit = 0 - client.Filter = fmt.Sprintf("^%s", toComplete) - - client.SetStateMask() - results, err := client.Run() - if err != nil { - return nil, completion.BashCompDirectiveDefault - } - - var choices []string - for _, res := range results { - choices = append(choices, res.Name) - } - - return choices, completion.BashCompDirectiveNoFileComp -} diff --git a/cmd/helm/list_test.go b/cmd/helm/list_test.go deleted file mode 100644 index 127a8a9..0000000 --- a/cmd/helm/list_test.go +++ /dev/null @@ -1,212 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/time" -) - -func TestListCmd(t *testing.T) { - defaultNamespace := "default" - - sampleTimeSeconds := int64(1452902400) - timestamp1 := time.Unix(sampleTimeSeconds+1, 0).UTC() - timestamp2 := time.Unix(sampleTimeSeconds+2, 0).UTC() - timestamp3 := time.Unix(sampleTimeSeconds+3, 0).UTC() - timestamp4 := time.Unix(sampleTimeSeconds+4, 0).UTC() - chartInfo := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "chickadee", - Version: "1.0.0", - AppVersion: "0.0.1", - }, - } - - releaseFixture := []*release.Release{ - { - Name: "starlord", - Version: 1, - Namespace: defaultNamespace, - Info: &release.Info{ - LastDeployed: timestamp1, - Status: release.StatusSuperseded, - }, - Chart: chartInfo, - }, - { - Name: "starlord", - Version: 2, - Namespace: defaultNamespace, - Info: &release.Info{ - LastDeployed: timestamp1, - Status: release.StatusDeployed, - }, - Chart: chartInfo, - }, - { - Name: "groot", - Version: 1, - Namespace: defaultNamespace, - Info: &release.Info{ - LastDeployed: timestamp1, - Status: release.StatusUninstalled, - }, - Chart: chartInfo, - }, - { - Name: "gamora", - Version: 1, - Namespace: defaultNamespace, - Info: &release.Info{ - LastDeployed: timestamp1, - Status: release.StatusSuperseded, - }, - Chart: chartInfo, - }, - { - Name: "rocket", - Version: 1, - Namespace: defaultNamespace, - Info: &release.Info{ - LastDeployed: timestamp2, - Status: release.StatusFailed, - }, - Chart: chartInfo, - }, - { - Name: "drax", - Version: 1, - Namespace: defaultNamespace, - Info: &release.Info{ - LastDeployed: timestamp1, - Status: release.StatusUninstalling, - }, - Chart: chartInfo, - }, - { - Name: "thanos", - Version: 1, - Namespace: defaultNamespace, - Info: &release.Info{ - LastDeployed: timestamp1, - Status: release.StatusPendingInstall, - }, - Chart: chartInfo, - }, - { - Name: "hummingbird", - Version: 1, - Namespace: defaultNamespace, - Info: &release.Info{ - LastDeployed: timestamp3, - Status: release.StatusDeployed, - }, - Chart: chartInfo, - }, - { - Name: "iguana", - Version: 2, - Namespace: defaultNamespace, - Info: &release.Info{ - LastDeployed: timestamp4, - Status: release.StatusDeployed, - }, - Chart: chartInfo, - }, - } - - tests := []cmdTestCase{{ - name: "list releases", - cmd: "list", - golden: "output/list.txt", - rels: releaseFixture, - }, { - name: "list all releases", - cmd: "list --all", - golden: "output/list-all.txt", - rels: releaseFixture, - }, { - name: "list releases sorted by release date", - cmd: "list --date", - golden: "output/list-date.txt", - rels: releaseFixture, - }, { - name: "list failed releases", - cmd: "list --failed", - golden: "output/list-failed.txt", - rels: releaseFixture, - }, { - name: "list filtered releases", - cmd: "list --filter='.*'", - golden: "output/list-filter.txt", - rels: releaseFixture, - }, { - name: "list releases, limited to one release", - cmd: "list --max 1", - golden: "output/list-max.txt", - rels: releaseFixture, - }, { - name: "list releases, offset by one", - cmd: "list --offset 1", - golden: "output/list-offset.txt", - rels: releaseFixture, - }, { - name: "list pending releases", - cmd: "list --pending", - golden: "output/list-pending.txt", - rels: releaseFixture, - }, { - name: "list releases in reverse order", - cmd: "list --reverse", - golden: "output/list-reverse.txt", - rels: releaseFixture, - }, { - name: "list releases sorted by reversed release date", - cmd: "list --date --reverse", - golden: "output/list-date-reversed.txt", - rels: releaseFixture, - }, { - name: "list releases in short output format", - cmd: "list --short", - golden: "output/list-short.txt", - rels: releaseFixture, - }, { - name: "list superseded releases", - cmd: "list --superseded", - golden: "output/list-superseded.txt", - rels: releaseFixture, - }, { - name: "list uninstalled releases", - cmd: "list --uninstalled", - golden: "output/list-uninstalled.txt", - rels: releaseFixture, - }, { - name: "list releases currently uninstalling", - cmd: "list --uninstalling", - golden: "output/list-uninstalling.txt", - rels: releaseFixture, - }} - runTestCmd(t, tests) -} - -func TestListOutputCompletion(t *testing.T) { - outputFlagCompletionTest(t, "list") -} diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go deleted file mode 100644 index f2fb5c0..0000000 --- a/cmd/helm/load_plugins.go +++ /dev/null @@ -1,182 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "os" - "os/exec" - "path/filepath" - "strings" - "syscall" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/pkg/plugin" -) - -type pluginError struct { - error - code int -} - -// loadPlugins loads plugins into the command list. -// -// This follows a different pattern than the other commands because it has -// to inspect its environment and then add commands to the base command -// as it finds them. -func loadPlugins(baseCmd *cobra.Command, out io.Writer) { - - // If HELM_NO_PLUGINS is set to 1, do not load plugins. - if os.Getenv("HELM_NO_PLUGINS") == "1" { - return - } - - found, err := findPlugins(settings.PluginsDirectory) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err) - return - } - - processParent := func(cmd *cobra.Command, args []string) ([]string, error) { - k, u := manuallyProcessArgs(args) - if err := cmd.Parent().ParseFlags(k); err != nil { - return nil, err - } - return u, nil - } - - // Now we create commands for all of these. - for _, plug := range found { - plug := plug - md := plug.Metadata - if md.Usage == "" { - md.Usage = fmt.Sprintf("the %q plugin", md.Name) - } - - c := &cobra.Command{ - Use: md.Name, - Short: md.Usage, - Long: md.Description, - RunE: func(cmd *cobra.Command, args []string) error { - u, err := processParent(cmd, args) - if err != nil { - return err - } - - // Call setupEnv before PrepareCommand because - // PrepareCommand uses os.ExpandEnv and expects the - // setupEnv vars. - plugin.SetupPluginEnv(settings, md.Name, plug.Dir) - main, argv, prepCmdErr := plug.PrepareCommand(u) - if prepCmdErr != nil { - os.Stderr.WriteString(prepCmdErr.Error()) - return errors.Errorf("plugin %q exited with error", md.Name) - } - - env := os.Environ() - for k, v := range settings.EnvVars() { - env = append(env, fmt.Sprintf("%s=%s", k, v)) - } - - prog := exec.Command(main, argv...) - prog.Env = env - prog.Stdin = os.Stdin - prog.Stdout = out - prog.Stderr = os.Stderr - if err := prog.Run(); err != nil { - if eerr, ok := err.(*exec.ExitError); ok { - os.Stderr.Write(eerr.Stderr) - status := eerr.Sys().(syscall.WaitStatus) - return pluginError{ - error: errors.Errorf("plugin %q exited with error", md.Name), - code: status.ExitStatus(), - } - } - return err - } - return nil - }, - // This passes all the flags to the subcommand. - DisableFlagParsing: true, - } - - // TODO: Make sure a command with this name does not already exist. - baseCmd.AddCommand(c) - } -} - -// manuallyProcessArgs processes an arg array, removing special args. -// -// Returns two sets of args: known and unknown (in that order) -func manuallyProcessArgs(args []string) ([]string, []string) { - known := []string{} - unknown := []string{} - kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--registry-config", "--repository-cache", "--repository-config"} - knownArg := func(a string) bool { - for _, pre := range kvargs { - if strings.HasPrefix(a, pre+"=") { - return true - } - } - return false - } - - isKnown := func(v string) string { - for _, i := range kvargs { - if i == v { - return v - } - } - return "" - } - - for i := 0; i < len(args); i++ { - switch a := args[i]; a { - case "--debug": - known = append(known, a) - case isKnown(a): - known = append(known, a) - i++ - if i < len(args) { - known = append(known, args[i]) - } - default: - if knownArg(a) { - known = append(known, a) - continue - } - unknown = append(unknown, a) - } - } - return known, unknown -} - -// findPlugins returns a list of YAML files that describe plugins. -func findPlugins(plugdirs string) ([]*plugin.Plugin, error) { - found := []*plugin.Plugin{} - // Let's get all UNIXy and allow path separators - for _, p := range filepath.SplitList(plugdirs) { - matches, err := plugin.LoadAll(p) - if err != nil { - return matches, err - } - found = append(found, matches...) - } - return found, nil -} diff --git a/cmd/helm/package.go b/cmd/helm/package.go deleted file mode 100644 index 00fe0ef..0000000 --- a/cmd/helm/package.go +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli/values" - "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/getter" -) - -const packageDesc = ` -This command packages a chart into a versioned chart archive file. If a path -is given, this will look at that path for a chart (which must contain a -Chart.yaml file) and then package that directory. - -Versioned chart archives are used by Helm package repositories. - -To sign a chart, use the '--sign' flag. In most cases, you should also -provide '--keyring path/to/secret/keys' and '--key keyname'. - - $ helm package --sign ./mychart --key mykey --keyring ~/.gnupg/secring.gpg - -If '--keyring' is not specified, Helm usually defaults to the public keyring -unless your environment is otherwise configured. -` - -func newPackageCmd(out io.Writer) *cobra.Command { - client := action.NewPackage() - valueOpts := &values.Options{} - - cmd := &cobra.Command{ - Use: "package [CHART_PATH] [...]", - Short: "package a chart directory into a chart archive", - Long: packageDesc, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errors.Errorf("need at least one argument, the path to the chart") - } - if client.Sign { - if client.Key == "" { - return errors.New("--key is required for signing a package") - } - if client.Keyring == "" { - return errors.New("--keyring is required for signing a package") - } - } - client.RepositoryConfig = settings.RepositoryConfig - client.RepositoryCache = settings.RepositoryCache - p := getter.All(settings) - vals, err := valueOpts.MergeValues(p) - if err != nil { - return err - } - - for i := 0; i < len(args); i++ { - path, err := filepath.Abs(args[i]) - if err != nil { - return err - } - if _, err := os.Stat(args[i]); err != nil { - return err - } - - if client.DependencyUpdate { - downloadManager := &downloader.Manager{ - Out: ioutil.Discard, - ChartPath: path, - Keyring: client.Keyring, - Getters: p, - Debug: settings.Debug, - RepositoryConfig: settings.RepositoryConfig, - RepositoryCache: settings.RepositoryCache, - } - - if err := downloadManager.Update(); err != nil { - return err - } - } - p, err := client.Run(path, vals) - if err != nil { - return err - } - fmt.Fprintf(out, "Successfully packaged chart and saved it to: %s\n", p) - } - return nil - }, - } - - f := cmd.Flags() - f.BoolVar(&client.Sign, "sign", false, "use a PGP private key to sign this package") - f.StringVar(&client.Key, "key", "", "name of the key to use when signing. Used if --sign is true") - f.StringVar(&client.Keyring, "keyring", defaultKeyring(), "location of a public keyring") - f.StringVar(&client.Version, "version", "", "set the version on the chart to this semver version") - f.StringVar(&client.AppVersion, "app-version", "", "set the appVersion on the chart to this version") - f.StringVarP(&client.Destination, "destination", "d", ".", "location to write the chart.") - f.BoolVarP(&client.DependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`) - - return cmd -} diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go deleted file mode 100644 index e0a5fab..0000000 --- a/cmd/helm/package_test.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bytes" - "os" - "path/filepath" - "regexp" - "testing" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" -) - -func TestPackage(t *testing.T) { - tests := []struct { - name string - flags map[string]string - args []string - expect string - hasfile string - err bool - }{ - { - name: "package without chart path", - args: []string{}, - flags: map[string]string{}, - expect: "need at least one argument, the path to the chart", - err: true, - }, - { - name: "package --sign, no --key", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"sign": "1"}, - expect: "key is required for signing a package", - err: true, - }, - { - name: "package --sign, no --keyring", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"sign": "1", "key": "nosuchkey", "keyring": ""}, - expect: "keyring is required for signing a package", - err: true, - }, - { - name: "package testdata/testcharts/alpine, no save", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"save": "0"}, - expect: "", - hasfile: "alpine-0.1.0.tgz", - }, - { - name: "package testdata/testcharts/alpine", - args: []string{"testdata/testcharts/alpine"}, - expect: "", - hasfile: "alpine-0.1.0.tgz", - }, - { - name: "package testdata/testcharts/issue1979", - args: []string{"testdata/testcharts/issue1979"}, - expect: "", - hasfile: "alpine-0.1.0.tgz", - }, - { - name: "package --destination toot", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"destination": "toot"}, - expect: "", - hasfile: "toot/alpine-0.1.0.tgz", - }, - { - name: "package --sign --key=KEY --keyring=KEYRING testdata/testcharts/alpine", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"sign": "1", "keyring": "testdata/helm-test-key.secret", "key": "helm-test"}, - expect: "", - hasfile: "alpine-0.1.0.tgz", - }, - { - name: "package testdata/testcharts/chart-missing-deps", - args: []string{"testdata/testcharts/chart-missing-deps"}, - hasfile: "chart-missing-deps-0.1.0.tgz", - err: true, - }, - { - name: "package testdata/testcharts/chart-bad-type", - args: []string{"testdata/testcharts/chart-bad-type"}, - err: true, - }, - } - - origDir, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cachePath := ensure.TempDir(t) - defer testChdir(t, cachePath)() - - if err := os.MkdirAll("toot", 0777); err != nil { - t.Fatal(err) - } - var buf bytes.Buffer - c := newPackageCmd(&buf) - - // This is an unfortunate byproduct of the tmpdir - if v, ok := tt.flags["keyring"]; ok && len(v) > 0 { - tt.flags["keyring"] = filepath.Join(origDir, v) - } - - setFlags(c, tt.flags) - re := regexp.MustCompile(tt.expect) - - adjustedArgs := make([]string, len(tt.args)) - for i, f := range tt.args { - adjustedArgs[i] = filepath.Join(origDir, f) - } - - err := c.RunE(c, adjustedArgs) - if err != nil { - if tt.err && re.MatchString(err.Error()) { - return - } - t.Fatalf("%q: expected error %q, got %q", tt.name, tt.expect, err) - } - - if !re.Match(buf.Bytes()) { - t.Errorf("%q: expected output %q, got %q", tt.name, tt.expect, buf.String()) - } - - if len(tt.hasfile) > 0 { - if fi, err := os.Stat(tt.hasfile); err != nil { - t.Errorf("%q: expected file %q, got err %q", tt.name, tt.hasfile, err) - } else if fi.Size() == 0 { - t.Errorf("%q: file %q has zero bytes.", tt.name, tt.hasfile) - } - } - - if v, ok := tt.flags["sign"]; ok && v == "1" { - if fi, err := os.Stat(tt.hasfile + ".prov"); err != nil { - t.Errorf("%q: expected provenance file", tt.name) - } else if fi.Size() == 0 { - t.Errorf("%q: provenance file is empty", tt.name) - } - } - }) - } -} - -func TestSetAppVersion(t *testing.T) { - var ch *chart.Chart - expectedAppVersion := "app-version-foo" - - dir := ensure.TempDir(t) - - c := newPackageCmd(&bytes.Buffer{}) - flags := map[string]string{ - "destination": dir, - "app-version": expectedAppVersion, - } - setFlags(c, flags) - if err := c.RunE(c, []string{"testdata/testcharts/alpine"}); err != nil { - t.Errorf("unexpected error %q", err) - } - - chartPath := filepath.Join(dir, "alpine-0.1.0.tgz") - if fi, err := os.Stat(chartPath); err != nil { - t.Errorf("expected file %q, got err %q", chartPath, err) - } else if fi.Size() == 0 { - t.Errorf("file %q has zero bytes.", chartPath) - } - ch, err := loader.Load(chartPath) - if err != nil { - t.Fatalf("unexpected error loading packaged chart: %v", err) - } - if ch.Metadata.AppVersion != expectedAppVersion { - t.Errorf("expected app-version %q, found %q", expectedAppVersion, ch.Metadata.AppVersion) - } -} - -func setFlags(cmd *cobra.Command, flags map[string]string) { - dest := cmd.Flags() - for f, v := range flags { - dest.Set(f, v) - } -} diff --git a/cmd/helm/plugin.go b/cmd/helm/plugin.go deleted file mode 100644 index 8e1044f..0000000 --- a/cmd/helm/plugin.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "os" - "os/exec" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/pkg/plugin" -) - -const pluginHelp = ` -Manage client-side Helm plugins. -` - -func newPluginCmd(out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "plugin", - Short: "install, list, or uninstall Helm plugins", - Long: pluginHelp, - } - cmd.AddCommand( - newPluginInstallCmd(out), - newPluginListCmd(out), - newPluginUninstallCmd(out), - newPluginUpdateCmd(out), - ) - return cmd -} - -// runHook will execute a plugin hook. -func runHook(p *plugin.Plugin, event string) error { - hook := p.Metadata.Hooks[event] - if hook == "" { - return nil - } - - prog := exec.Command("sh", "-c", hook) - // TODO make this work on windows - // I think its ... ¯\_(ツ)_/¯ - // prog := exec.Command("cmd", "/C", p.Metadata.Hooks.Install()) - - debug("running %s hook: %s", event, prog) - - plugin.SetupPluginEnv(settings, p.Metadata.Name, p.Dir) - prog.Stdout, prog.Stderr = os.Stdout, os.Stderr - if err := prog.Run(); err != nil { - if eerr, ok := err.(*exec.ExitError); ok { - os.Stderr.Write(eerr.Stderr) - return errors.Errorf("plugin %s hook for %q exited with error", event, p.Metadata.Name) - } - return err - } - return nil -} diff --git a/cmd/helm/plugin_install.go b/cmd/helm/plugin_install.go deleted file mode 100644 index 8deb1f4..0000000 --- a/cmd/helm/plugin_install.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/plugin" - "helm.sh/helm/v3/pkg/plugin/installer" -) - -type pluginInstallOptions struct { - source string - version string -} - -const pluginInstallDesc = ` -This command allows you to install a plugin from a url to a VCS repo or a local path. -` - -func newPluginInstallCmd(out io.Writer) *cobra.Command { - o := &pluginInstallOptions{} - cmd := &cobra.Command{ - Use: "install [options] ...", - Short: "install one or more Helm plugins", - Long: pluginInstallDesc, - Aliases: []string{"add"}, - Args: require.ExactArgs(1), - PreRunE: func(cmd *cobra.Command, args []string) error { - return o.complete(args) - }, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out) - }, - } - cmd.Flags().StringVar(&o.version, "version", "", "specify a version constraint. If this is not specified, the latest version is installed") - return cmd -} - -func (o *pluginInstallOptions) complete(args []string) error { - o.source = args[0] - return nil -} - -func (o *pluginInstallOptions) run(out io.Writer) error { - installer.Debug = settings.Debug - - i, err := installer.NewForSource(o.source, o.version) - if err != nil { - return err - } - if err := installer.Install(i); err != nil { - return err - } - - debug("loading plugin from %s", i.Path()) - p, err := plugin.LoadDir(i.Path()) - if err != nil { - return err - } - - if err := runHook(p, plugin.Install); err != nil { - return err - } - - fmt.Fprintf(out, "Installed plugin: %s\n", p.Metadata.Name) - return nil -} diff --git a/cmd/helm/plugin_list.go b/cmd/helm/plugin_list.go deleted file mode 100644 index 0440b0b..0000000 --- a/cmd/helm/plugin_list.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "strings" - - "github.com/gosuri/uitable" - "github.com/spf13/cobra" -) - -func newPluginListCmd(out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "list installed Helm plugins", - RunE: func(cmd *cobra.Command, args []string) error { - debug("pluginDirs: %s", settings.PluginsDirectory) - plugins, err := findPlugins(settings.PluginsDirectory) - if err != nil { - return err - } - - table := uitable.New() - table.AddRow("NAME", "VERSION", "DESCRIPTION") - for _, p := range plugins { - table.AddRow(p.Metadata.Name, p.Metadata.Version, p.Metadata.Description) - } - fmt.Fprintln(out, table) - return nil - }, - } - return cmd -} - -// Provide dynamic auto-completion for plugin names -func compListPlugins(toComplete string) []string { - var pNames []string - plugins, err := findPlugins(settings.PluginsDirectory) - if err == nil { - for _, p := range plugins { - if strings.HasPrefix(p.Metadata.Name, toComplete) { - pNames = append(pNames, p.Metadata.Name) - } - } - } - return pNames -} diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go deleted file mode 100644 index 3fd3a41..0000000 --- a/cmd/helm/plugin_test.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bytes" - "os" - "runtime" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func TestManuallyProcessArgs(t *testing.T) { - input := []string{ - "--debug", - "--foo", "bar", - "--kubeconfig=/home/foo", - "--kubeconfig", "/home/foo", - "--kube-context=test1", - "--kube-context", "test1", - "-n=test2", - "-n", "test2", - "--namespace=test2", - "--namespace", "test2", - "--home=/tmp", - "command", - } - - expectKnown := []string{ - "--debug", - "--kubeconfig=/home/foo", - "--kubeconfig", "/home/foo", - "--kube-context=test1", - "--kube-context", "test1", - "-n=test2", - "-n", "test2", - "--namespace=test2", - "--namespace", "test2", - } - - expectUnknown := []string{ - "--foo", "bar", "--home=/tmp", "command", - } - - known, unknown := manuallyProcessArgs(input) - - for i, k := range known { - if k != expectKnown[i] { - t.Errorf("expected known flag %d to be %q, got %q", i, expectKnown[i], k) - } - } - for i, k := range unknown { - if k != expectUnknown[i] { - t.Errorf("expected unknown flag %d to be %q, got %q", i, expectUnknown[i], k) - } - } - -} - -func TestLoadPlugins(t *testing.T) { - settings.PluginsDirectory = "testdata/helmhome/helm/plugins" - settings.RepositoryConfig = "testdata/helmhome/helm/repositories.yaml" - settings.RepositoryCache = "testdata/helmhome/helm/repository" - - var ( - out bytes.Buffer - cmd cobra.Command - ) - loadPlugins(&cmd, &out) - - envs := strings.Join([]string{ - "fullenv", - "testdata/helmhome/helm/plugins/fullenv", - "testdata/helmhome/helm/plugins", - "testdata/helmhome/helm/repositories.yaml", - "testdata/helmhome/helm/repository", - os.Args[0], - }, "\n") - - // Test that the YAML file was correctly converted to a command. - tests := []struct { - use string - short string - long string - expect string - args []string - code int - }{ - {"args", "echo args", "This echos args", "-a -b -c\n", []string{"-a", "-b", "-c"}, 0}, - {"echo", "echo stuff", "This echos stuff", "hello\n", []string{}, 0}, - {"env", "env stuff", "show the env", "env\n", []string{}, 0}, - {"exitwith", "exitwith code", "This exits with the specified exit code", "", []string{"2"}, 2}, - {"fullenv", "show env vars", "show all env vars", envs + "\n", []string{}, 0}, - } - - plugins := cmd.Commands() - - if len(plugins) != len(tests) { - t.Fatalf("Expected %d plugins, got %d", len(tests), len(plugins)) - } - - for i := 0; i < len(plugins); i++ { - out.Reset() - tt := tests[i] - pp := plugins[i] - if pp.Use != tt.use { - t.Errorf("%d: Expected Use=%q, got %q", i, tt.use, pp.Use) - } - if pp.Short != tt.short { - t.Errorf("%d: Expected Use=%q, got %q", i, tt.short, pp.Short) - } - if pp.Long != tt.long { - t.Errorf("%d: Expected Use=%q, got %q", i, tt.long, pp.Long) - } - - // Currently, plugins assume a Linux subsystem. Skip the execution - // tests until this is fixed - if runtime.GOOS != "windows" { - if err := pp.RunE(pp, tt.args); err != nil { - if tt.code > 0 { - perr, ok := err.(pluginError) - if !ok { - t.Errorf("Expected %s to return pluginError: got %v(%T)", tt.use, err, err) - } - if perr.code != tt.code { - t.Errorf("Expected %s to return %d: got %d", tt.use, tt.code, perr.code) - } - } else { - t.Errorf("Error running %s: %+v", tt.use, err) - } - } - if out.String() != tt.expect { - t.Errorf("Expected %s to output:\n%s\ngot\n%s", tt.use, tt.expect, out.String()) - } - } - } -} - -func TestLoadPlugins_HelmNoPlugins(t *testing.T) { - settings.PluginsDirectory = "testdata/helmhome/helm/plugins" - settings.RepositoryConfig = "testdata/helmhome/helm/repository" - - os.Setenv("HELM_NO_PLUGINS", "1") - - out := bytes.NewBuffer(nil) - cmd := &cobra.Command{} - loadPlugins(cmd, out) - plugins := cmd.Commands() - - if len(plugins) != 0 { - t.Fatalf("Expected 0 plugins, got %d", len(plugins)) - } -} diff --git a/cmd/helm/plugin_uninstall.go b/cmd/helm/plugin_uninstall.go deleted file mode 100644 index f703ddc..0000000 --- a/cmd/helm/plugin_uninstall.go +++ /dev/null @@ -1,107 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "os" - "strings" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/plugin" -) - -type pluginUninstallOptions struct { - names []string -} - -func newPluginUninstallCmd(out io.Writer) *cobra.Command { - o := &pluginUninstallOptions{} - - cmd := &cobra.Command{ - Use: "uninstall ...", - Aliases: []string{"rm", "remove"}, - Short: "uninstall one or more Helm plugins", - PreRunE: func(cmd *cobra.Command, args []string) error { - return o.complete(args) - }, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out) - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListPlugins(toComplete), completion.BashCompDirectiveNoFileComp - }) - - return cmd -} - -func (o *pluginUninstallOptions) complete(args []string) error { - if len(args) == 0 { - return errors.New("please provide plugin name to uninstall") - } - o.names = args - return nil -} - -func (o *pluginUninstallOptions) run(out io.Writer) error { - debug("loading installed plugins from %s", settings.PluginsDirectory) - plugins, err := findPlugins(settings.PluginsDirectory) - if err != nil { - return err - } - var errorPlugins []string - for _, name := range o.names { - if found := findPlugin(plugins, name); found != nil { - if err := uninstallPlugin(found); err != nil { - errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to uninstall plugin %s, got error (%v)", name, err)) - } else { - fmt.Fprintf(out, "Uninstalled plugin: %s\n", name) - } - } else { - errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name)) - } - } - if len(errorPlugins) > 0 { - return errors.Errorf(strings.Join(errorPlugins, "\n")) - } - return nil -} - -func uninstallPlugin(p *plugin.Plugin) error { - if err := os.RemoveAll(p.Dir); err != nil { - return err - } - return runHook(p, plugin.Delete) -} - -func findPlugin(plugins []*plugin.Plugin, name string) *plugin.Plugin { - for _, p := range plugins { - if p.Metadata.Name == name { - return p - } - } - return nil -} diff --git a/cmd/helm/plugin_update.go b/cmd/helm/plugin_update.go deleted file mode 100644 index a24e805..0000000 --- a/cmd/helm/plugin_update.go +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/plugin" - "helm.sh/helm/v3/pkg/plugin/installer" -) - -type pluginUpdateOptions struct { - names []string -} - -func newPluginUpdateCmd(out io.Writer) *cobra.Command { - o := &pluginUpdateOptions{} - - cmd := &cobra.Command{ - Use: "update ...", - Aliases: []string{"up"}, - Short: "update one or more Helm plugins", - PreRunE: func(cmd *cobra.Command, args []string) error { - return o.complete(args) - }, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out) - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListPlugins(toComplete), completion.BashCompDirectiveNoFileComp - }) - - return cmd -} - -func (o *pluginUpdateOptions) complete(args []string) error { - if len(args) == 0 { - return errors.New("please provide plugin name to update") - } - o.names = args - return nil -} - -func (o *pluginUpdateOptions) run(out io.Writer) error { - installer.Debug = settings.Debug - debug("loading installed plugins from %s", settings.PluginsDirectory) - plugins, err := findPlugins(settings.PluginsDirectory) - if err != nil { - return err - } - var errorPlugins []string - - for _, name := range o.names { - if found := findPlugin(plugins, name); found != nil { - if err := updatePlugin(found); err != nil { - errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to update plugin %s, got error (%v)", name, err)) - } else { - fmt.Fprintf(out, "Updated plugin: %s\n", name) - } - } else { - errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name)) - } - } - if len(errorPlugins) > 0 { - return errors.Errorf(strings.Join(errorPlugins, "\n")) - } - return nil -} - -func updatePlugin(p *plugin.Plugin) error { - exactLocation, err := filepath.EvalSymlinks(p.Dir) - if err != nil { - return err - } - absExactLocation, err := filepath.Abs(exactLocation) - if err != nil { - return err - } - - i, err := installer.FindSource(absExactLocation) - if err != nil { - return err - } - if err := installer.Update(i); err != nil { - return err - } - - debug("loading plugin from %s", i.Path()) - updatedPlugin, err := plugin.LoadDir(i.Path()) - if err != nil { - return err - } - - return runHook(updatedPlugin, plugin.Update) -} diff --git a/cmd/helm/printer.go b/cmd/helm/printer.go deleted file mode 100644 index 7cf7bf9..0000000 --- a/cmd/helm/printer.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "text/template" -) - -func tpl(t string, vals map[string]interface{}, out io.Writer) error { - tt, err := template.New("_").Parse(t) - if err != nil { - return err - } - return tt.Execute(out, vals) -} diff --git a/cmd/helm/pull.go b/cmd/helm/pull.go deleted file mode 100644 index 16cd104..0000000 --- a/cmd/helm/pull.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" -) - -const pullDesc = ` -Retrieve a package from a package repository, and download it locally. - -This is useful for fetching packages to inspect, modify, or repackage. It can -also be used to perform cryptographic verification of a chart without installing -the chart. - -There are options for unpacking the chart after download. This will create a -directory for the chart and uncompress into that directory. - -If the --verify flag is specified, the requested chart MUST have a provenance -file, and MUST pass the verification process. Failure in any part of this will -result in an error, and the chart will not be saved locally. -` - -func newPullCmd(out io.Writer) *cobra.Command { - client := action.NewPull() - - cmd := &cobra.Command{ - Use: "pull [chart URL | repo/chartname] [...]", - Short: "download a chart from a repository and (optionally) unpack it in local directory", - Aliases: []string{"fetch"}, - Long: pullDesc, - Args: require.MinimumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - client.Settings = settings - if client.Version == "" && client.Devel { - debug("setting version to >0.0.0-0") - client.Version = ">0.0.0-0" - } - - for i := 0; i < len(args); i++ { - output, err := client.Run(args[i]) - if err != nil { - return err - } - fmt.Fprint(out, output) - } - return nil - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListCharts(toComplete, false) - }) - - f := cmd.Flags() - f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.") - f.BoolVar(&client.Untar, "untar", false, "if set to true, will untar the chart after downloading it") - f.BoolVar(&client.VerifyLater, "prov", false, "fetch the provenance file, but don't perform verification") - f.StringVar(&client.UntarDir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded") - f.StringVarP(&client.DestDir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this") - addChartPathOptionsFlags(f, &client.ChartPathOptions) - - return cmd -} diff --git a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go deleted file mode 100644 index 1aca661..0000000 --- a/cmd/helm/pull_test.go +++ /dev/null @@ -1,193 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "os" - "path/filepath" - "regexp" - "testing" - - "helm.sh/helm/v3/pkg/repo/repotest" -) - -func TestPullCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*") - if err != nil { - t.Fatal(err) - } - defer srv.Stop() - - if err := srv.LinkIndices(); err != nil { - t.Fatal(err) - } - - // all flags will get "-d outdir" appended. - tests := []struct { - name string - args string - existFile string - existDir string - wantError bool - wantErrorMsg string - failExpect string - expectFile string - expectDir bool - expectVerify bool - }{ - { - name: "Basic chart fetch", - args: "test/signtest", - expectFile: "./signtest-0.1.0.tgz", - }, - { - name: "Chart fetch with version", - args: "test/signtest --version=0.1.0", - expectFile: "./signtest-0.1.0.tgz", - }, - { - name: "Fail chart fetch with non-existent version", - args: "test/signtest --version=99.1.0", - wantError: true, - failExpect: "no such chart", - }, - { - name: "Fail fetching non-existent chart", - args: "test/nosuchthing", - failExpect: "Failed to fetch", - wantError: true, - }, - { - name: "Fetch and verify", - args: "test/signtest --verify --keyring testdata/helm-test-key.pub", - expectFile: "./signtest-0.1.0.tgz", - expectVerify: true, - }, - { - name: "Fetch and fail verify", - args: "test/reqtest --verify --keyring testdata/helm-test-key.pub", - failExpect: "Failed to fetch provenance", - wantError: true, - }, - { - name: "Fetch and untar", - args: "test/signtest --untar --untardir signtest", - expectFile: "./signtest", - expectDir: true, - }, - { - name: "Fetch untar when file with same name existed", - args: "test/test1 --untar --untardir test1", - existFile: "test1", - wantError: true, - wantErrorMsg: fmt.Sprintf("failed to untar: a file or directory with the name %s already exists", filepath.Join(srv.Root(), "test1")), - }, - { - name: "Fetch untar when dir with same name existed", - args: "test/test2 --untar --untardir test2", - existDir: "test2", - wantError: true, - wantErrorMsg: fmt.Sprintf("failed to untar: a file or directory with the name %s already exists", filepath.Join(srv.Root(), "test2")), - }, - { - name: "Fetch, verify, untar", - args: "test/signtest --verify --keyring=testdata/helm-test-key.pub --untar --untardir signtest2", - expectFile: "./signtest2", - expectDir: true, - expectVerify: true, - }, - { - name: "Chart fetch using repo URL", - expectFile: "./signtest-0.1.0.tgz", - args: "signtest --repo " + srv.URL(), - }, - { - name: "Fail fetching non-existent chart on repo URL", - args: "someChart --repo " + srv.URL(), - failExpect: "Failed to fetch chart", - wantError: true, - }, - { - name: "Specific version chart fetch using repo URL", - expectFile: "./signtest-0.1.0.tgz", - args: "signtest --version=0.1.0 --repo " + srv.URL(), - }, - { - name: "Specific version chart fetch using repo URL", - args: "signtest --version=0.2.0 --repo " + srv.URL(), - failExpect: "Failed to fetch chart version", - wantError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - outdir := srv.Root() - cmd := fmt.Sprintf("fetch %s -d '%s' --repository-config %s --repository-cache %s ", - tt.args, - outdir, - filepath.Join(outdir, "repositories.yaml"), - outdir, - ) - // Create file or Dir before helm pull --untar, see: https://github.com/helm/helm/issues/7182 - if tt.existFile != "" { - file := filepath.Join(outdir, tt.existFile) - _, err := os.Create(file) - if err != nil { - t.Fatal("err") - } - } - if tt.existDir != "" { - file := filepath.Join(outdir, tt.existDir) - err := os.Mkdir(file, 0755) - if err != nil { - t.Fatal(err) - } - } - _, out, err := executeActionCommand(cmd) - if err != nil { - if tt.wantError { - if tt.wantErrorMsg != "" && tt.wantErrorMsg == err.Error() { - t.Fatalf("Actual error %s, not equal to expected error %s", err, tt.wantErrorMsg) - } - return - } - t.Fatalf("%q reported error: %s", tt.name, err) - } - - if tt.expectVerify { - pointerAddressPattern := "0[xX][A-Fa-f0-9]+" - sha256Pattern := "[A-Fa-f0-9]{64}" - verificationRegex := regexp.MustCompile( - fmt.Sprintf("Verification: &{%s sha256:%s signtest-0.1.0.tgz}\n", pointerAddressPattern, sha256Pattern)) - if !verificationRegex.MatchString(out) { - t.Errorf("%q: expected match for regex %s, got %s", tt.name, verificationRegex, out) - } - } - - ef := filepath.Join(outdir, tt.expectFile) - fi, err := os.Stat(ef) - if err != nil { - t.Errorf("%q: expected a file at %s. %s", tt.name, ef, err) - } - if fi.IsDir() != tt.expectDir { - t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir) - } - }) - } -} diff --git a/cmd/helm/registry.go b/cmd/helm/registry.go deleted file mode 100644 index d13c308..0000000 --- a/cmd/helm/registry.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/pkg/action" -) - -const registryHelp = ` -This command consists of multiple subcommands to interact with registries. -` - -func newRegistryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "registry", - Short: "login to or logout from a registry", - Long: registryHelp, - Hidden: !FeatureGateOCI.IsEnabled(), - PersistentPreRunE: checkOCIFeatureGate(), - } - cmd.AddCommand( - newRegistryLoginCmd(cfg, out), - newRegistryLogoutCmd(cfg, out), - ) - return cmd -} diff --git a/cmd/helm/registry_login.go b/cmd/helm/registry_login.go deleted file mode 100644 index e3435bf..0000000 --- a/cmd/helm/registry_login.go +++ /dev/null @@ -1,136 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bufio" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "strings" - - "github.com/docker/docker/pkg/term" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" -) - -const registryLoginDesc = ` -Authenticate to a remote registry. -` - -func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - var usernameOpt, passwordOpt string - var passwordFromStdinOpt, insecureOpt bool - - cmd := &cobra.Command{ - Use: "login [host]", - Short: "login to a registry", - Long: registryLoginDesc, - Args: require.MinimumNArgs(1), - Hidden: !FeatureGateOCI.IsEnabled(), - RunE: func(cmd *cobra.Command, args []string) error { - hostname := args[0] - - username, password, err := getUsernamePassword(usernameOpt, passwordOpt, passwordFromStdinOpt) - if err != nil { - return err - } - - return action.NewRegistryLogin(cfg).Run(out, hostname, username, password, insecureOpt) - }, - } - - f := cmd.Flags() - f.StringVarP(&usernameOpt, "username", "u", "", "registry username") - f.StringVarP(&passwordOpt, "password", "p", "", "registry password or identity token") - f.BoolVarP(&passwordFromStdinOpt, "password-stdin", "", false, "read password or identity token from stdin") - f.BoolVarP(&insecureOpt, "insecure", "", false, "allow connections to TLS registry without certs") - - return cmd -} - -// Adapted from https://github.com/deislabs/oras -func getUsernamePassword(usernameOpt string, passwordOpt string, passwordFromStdinOpt bool) (string, string, error) { - var err error - username := usernameOpt - password := passwordOpt - - if passwordFromStdinOpt { - passwordFromStdin, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return "", "", err - } - password = strings.TrimSuffix(string(passwordFromStdin), "\n") - password = strings.TrimSuffix(password, "\r") - } else if password == "" { - if username == "" { - username, err = readLine("Username: ", false) - if err != nil { - return "", "", err - } - username = strings.TrimSpace(username) - } - if username == "" { - password, err = readLine("Token: ", true) - if err != nil { - return "", "", err - } else if password == "" { - return "", "", errors.New("token required") - } - } else { - password, err = readLine("Password: ", true) - if err != nil { - return "", "", err - } else if password == "" { - return "", "", errors.New("password required") - } - } - } else { - fmt.Fprintln(os.Stderr, "WARNING! Using --password via the CLI is insecure. Use --password-stdin.") - } - - return username, password, nil -} - -// Copied/adapted from https://github.com/deislabs/oras -func readLine(prompt string, silent bool) (string, error) { - fmt.Print(prompt) - if silent { - fd := os.Stdin.Fd() - state, err := term.SaveState(fd) - if err != nil { - return "", err - } - term.DisableEcho(fd, state) - defer term.RestoreTerminal(fd, state) - } - - reader := bufio.NewReader(os.Stdin) - line, _, err := reader.ReadLine() - if err != nil { - return "", err - } - if silent { - fmt.Println() - } - - return string(line), nil -} diff --git a/cmd/helm/registry_logout.go b/cmd/helm/registry_logout.go deleted file mode 100644 index e7e1a24..0000000 --- a/cmd/helm/registry_logout.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" -) - -const registryLogoutDesc = ` -Remove credentials stored for a remote registry. -` - -func newRegistryLogoutCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - return &cobra.Command{ - Use: "logout [host]", - Short: "logout from a registry", - Long: registryLogoutDesc, - Args: require.MinimumNArgs(1), - Hidden: !FeatureGateOCI.IsEnabled(), - RunE: func(cmd *cobra.Command, args []string) error { - hostname := args[0] - return action.NewRegistryLogout(cfg).Run(out, hostname) - }, - } -} diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go deleted file mode 100644 index e4690b9..0000000 --- a/cmd/helm/release_testing.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "time" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/cli/output" -) - -const releaseTestHelp = ` -The test command runs the tests for a release. - -The argument this command takes is the name of a deployed release. -The tests to be run are defined in the chart that was installed. -` - -func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewReleaseTesting(cfg) - var outfmt = output.Table - var outputLogs bool - - cmd := &cobra.Command{ - Use: "test [RELEASE]", - Short: "run tests for a release", - Long: releaseTestHelp, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - client.Namespace = settings.Namespace() - rel, runErr := client.Run(args[0]) - // We only return an error if we weren't even able to get the - // release, otherwise we keep going so we can print status and logs - // if requested - if runErr != nil && rel == nil { - return runErr - } - - if err := outfmt.Write(out, &statusPrinter{rel, settings.Debug}); err != nil { - return err - } - - if outputLogs { - // Print a newline to stdout to separate the output - fmt.Fprintln(out) - if err := client.GetPodLogs(out, rel); err != nil { - return err - } - } - - return runErr - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") - f.BoolVar(&outputLogs, "logs", false, "Dump the logs from test pods (this runs after all tests are complete, but before any cleanup)") - - return cmd -} diff --git a/cmd/helm/repo.go b/cmd/helm/repo.go deleted file mode 100644 index ad6ceaa..0000000 --- a/cmd/helm/repo.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "os" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" -) - -var repoHelm = ` -This command consists of multiple subcommands to interact with chart repositories. - -It can be used to add, remove, list, and index chart repositories. -` - -func newRepoCmd(out io.Writer) *cobra.Command { - cmd := &cobra.Command{ - Use: "repo add|remove|list|index|update [ARGS]", - Short: "add, list, remove, update, and index chart repositories", - Long: repoHelm, - Args: require.NoArgs, - } - - cmd.AddCommand(newRepoAddCmd(out)) - cmd.AddCommand(newRepoListCmd(out)) - cmd.AddCommand(newRepoRemoveCmd(out)) - cmd.AddCommand(newRepoIndexCmd(out)) - cmd.AddCommand(newRepoUpdateCmd(out)) - - return cmd -} - -func isNotExist(err error) bool { - return os.IsNotExist(errors.Cause(err)) -} diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go deleted file mode 100644 index e6afce3..0000000 --- a/cmd/helm/repo_add.go +++ /dev/null @@ -1,141 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "context" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" - - "github.com/gofrs/flock" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/repo" -) - -type repoAddOptions struct { - name string - url string - username string - password string - noUpdate bool - - certFile string - keyFile string - caFile string - - repoFile string - repoCache string -} - -func newRepoAddCmd(out io.Writer) *cobra.Command { - o := &repoAddOptions{} - - cmd := &cobra.Command{ - Use: "add [NAME] [URL]", - Short: "add a chart repository", - Args: require.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - o.name = args[0] - o.url = args[1] - o.repoFile = settings.RepositoryConfig - o.repoCache = settings.RepositoryCache - - return o.run(out) - }, - } - - f := cmd.Flags() - f.StringVar(&o.username, "username", "", "chart repository username") - f.StringVar(&o.password, "password", "", "chart repository password") - f.BoolVar(&o.noUpdate, "no-update", false, "raise error if repo is already registered") - f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") - f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") - f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") - - return cmd -} - -func (o *repoAddOptions) run(out io.Writer) error { - //Ensure the file directory exists as it is required for file locking - err := os.MkdirAll(filepath.Dir(o.repoFile), os.ModePerm) - if err != nil && !os.IsExist(err) { - return err - } - - // Acquire a file lock for process synchronization - fileLock := flock.New(strings.Replace(o.repoFile, filepath.Ext(o.repoFile), ".lock", 1)) - lockCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - locked, err := fileLock.TryLockContext(lockCtx, time.Second) - if err == nil && locked { - defer fileLock.Unlock() - } - if err != nil { - return err - } - - b, err := ioutil.ReadFile(o.repoFile) - if err != nil && !os.IsNotExist(err) { - return err - } - - var f repo.File - if err := yaml.Unmarshal(b, &f); err != nil { - return err - } - - if o.noUpdate && f.Has(o.name) { - return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) - } - - c := repo.Entry{ - Name: o.name, - URL: o.url, - Username: o.username, - Password: o.password, - CertFile: o.certFile, - KeyFile: o.keyFile, - CAFile: o.caFile, - } - - r, err := repo.NewChartRepository(&c, getter.All(settings)) - if err != nil { - return err - } - - if _, err := r.DownloadIndexFile(); err != nil { - return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", o.url) - } - - f.Update(&c) - - if err := f.WriteFile(o.repoFile, 0644); err != nil { - return err - } - fmt.Fprintf(out, "%q has been added to your repositories\n", o.name) - return nil -} diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go deleted file mode 100644 index d1b9bc0..0000000 --- a/cmd/helm/repo_add_test.go +++ /dev/null @@ -1,161 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "sync" - "testing" - - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/helmpath/xdg" - "helm.sh/helm/v3/pkg/repo" - "helm.sh/helm/v3/pkg/repo/repotest" -) - -func TestRepoAddCmd(t *testing.T) { - srv, err := repotest.NewTempServer("testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } - defer srv.Stop() - - repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml") - - tests := []cmdTestCase{{ - name: "add a repository", - cmd: fmt.Sprintf("repo add test-name %s --repository-config %s", srv.URL(), repoFile), - golden: "output/repo-add.txt", - }} - - runTestCmd(t, tests) -} - -func TestRepoAdd(t *testing.T) { - ts, err := repotest.NewTempServer("testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } - defer ts.Stop() - - rootDir := ensure.TempDir(t) - repoFile := filepath.Join(rootDir, "repositories.yaml") - - const testRepoName = "test-name" - - o := &repoAddOptions{ - name: testRepoName, - url: ts.URL(), - noUpdate: true, - repoFile: repoFile, - } - os.Setenv(xdg.CacheHomeEnvVar, rootDir) - - if err := o.run(ioutil.Discard); err != nil { - t.Error(err) - } - - f, err := repo.LoadFile(repoFile) - if err != nil { - t.Fatal(err) - } - - if !f.Has(testRepoName) { - t.Errorf("%s was not successfully inserted into %s", testRepoName, repoFile) - } - - idx := filepath.Join(helmpath.CachePath("repository"), helmpath.CacheIndexFile(testRepoName)) - if _, err := os.Stat(idx); os.IsNotExist(err) { - t.Errorf("Error cache index file was not created for repository %s", testRepoName) - } - idx = filepath.Join(helmpath.CachePath("repository"), helmpath.CacheChartsFile(testRepoName)) - if _, err := os.Stat(idx); os.IsNotExist(err) { - t.Errorf("Error cache charts file was not created for repository %s", testRepoName) - } - - o.noUpdate = false - - if err := o.run(ioutil.Discard); err != nil { - t.Errorf("Repository was not updated: %s", err) - } - - if err := o.run(ioutil.Discard); err != nil { - t.Errorf("Duplicate repository name was added") - } -} - -func TestRepoAddConcurrentGoRoutines(t *testing.T) { - const testName = "test-name" - repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml") - repoAddConcurrent(t, testName, repoFile) -} - -func TestRepoAddConcurrentDirNotExist(t *testing.T) { - const testName = "test-name-2" - repoFile := filepath.Join(ensure.TempDir(t), "foo", "repositories.yaml") - repoAddConcurrent(t, testName, repoFile) -} - -func repoAddConcurrent(t *testing.T, testName, repoFile string) { - ts, err := repotest.NewTempServer("testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } - defer ts.Stop() - - var wg sync.WaitGroup - wg.Add(3) - for i := 0; i < 3; i++ { - go func(name string) { - defer wg.Done() - o := &repoAddOptions{ - name: name, - url: ts.URL(), - noUpdate: true, - repoFile: repoFile, - } - if err := o.run(ioutil.Discard); err != nil { - t.Error(err) - } - }(fmt.Sprintf("%s-%d", testName, i)) - } - wg.Wait() - - b, err := ioutil.ReadFile(repoFile) - if err != nil { - t.Error(err) - } - - var f repo.File - if err := yaml.Unmarshal(b, &f); err != nil { - t.Error(err) - } - - var name string - for i := 0; i < 3; i++ { - name = fmt.Sprintf("%s-%d", testName, i) - if !f.Has(name) { - t.Errorf("%s was not successfully inserted into %s: %s", name, repoFile, f.Repositories[0]) - } - } -} diff --git a/cmd/helm/repo_index.go b/cmd/helm/repo_index.go deleted file mode 100644 index 63afaf3..0000000 --- a/cmd/helm/repo_index.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "os" - "path/filepath" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/repo" -) - -const repoIndexDesc = ` -Read the current directory and generate an index file based on the charts found. - -This tool is used for creating an 'index.yaml' file for a chart repository. To -set an absolute URL to the charts, use '--url' flag. - -To merge the generated index with an existing index file, use the '--merge' -flag. In this case, the charts found in the current directory will be merged -into the existing index, with local charts taking priority over existing charts. -` - -type repoIndexOptions struct { - dir string - url string - merge string -} - -func newRepoIndexCmd(out io.Writer) *cobra.Command { - o := &repoIndexOptions{} - - cmd := &cobra.Command{ - Use: "index [DIR]", - Short: "generate an index file given a directory containing packaged charts", - Long: repoIndexDesc, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - o.dir = args[0] - return o.run(out) - }, - } - - f := cmd.Flags() - f.StringVar(&o.url, "url", "", "url of chart repository") - f.StringVar(&o.merge, "merge", "", "merge the generated index into the given index") - - return cmd -} - -func (i *repoIndexOptions) run(out io.Writer) error { - path, err := filepath.Abs(i.dir) - if err != nil { - return err - } - - return index(path, i.url, i.merge) -} - -func index(dir, url, mergeTo string) error { - out := filepath.Join(dir, "index.yaml") - - i, err := repo.IndexDirectory(dir, url) - if err != nil { - return err - } - if mergeTo != "" { - // if index.yaml is missing then create an empty one to merge into - var i2 *repo.IndexFile - if _, err := os.Stat(mergeTo); os.IsNotExist(err) { - i2 = repo.NewIndexFile() - i2.WriteFile(mergeTo, 0644) - } else { - i2, err = repo.LoadIndexFile(mergeTo) - if err != nil { - return errors.Wrap(err, "merge failed") - } - } - i.Merge(i2) - } - i.SortEntries() - return i.WriteFile(out, 0644) -} diff --git a/cmd/helm/repo_index_test.go b/cmd/helm/repo_index_test.go deleted file mode 100644 index e04ae1b..0000000 --- a/cmd/helm/repo_index_test.go +++ /dev/null @@ -1,167 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bytes" - "io" - "os" - "path/filepath" - "testing" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/repo" -) - -func TestRepoIndexCmd(t *testing.T) { - - dir := ensure.TempDir(t) - - comp := filepath.Join(dir, "compressedchart-0.1.0.tgz") - if err := linkOrCopy("testdata/testcharts/compressedchart-0.1.0.tgz", comp); err != nil { - t.Fatal(err) - } - comp2 := filepath.Join(dir, "compressedchart-0.2.0.tgz") - if err := linkOrCopy("testdata/testcharts/compressedchart-0.2.0.tgz", comp2); err != nil { - t.Fatal(err) - } - - buf := bytes.NewBuffer(nil) - c := newRepoIndexCmd(buf) - - if err := c.RunE(c, []string{dir}); err != nil { - t.Error(err) - } - - destIndex := filepath.Join(dir, "index.yaml") - - index, err := repo.LoadIndexFile(destIndex) - if err != nil { - t.Fatal(err) - } - - if len(index.Entries) != 1 { - t.Errorf("expected 1 entry, got %d: %#v", len(index.Entries), index.Entries) - } - - vs := index.Entries["compressedchart"] - if len(vs) != 2 { - t.Errorf("expected 2 versions, got %d: %#v", len(vs), vs) - } - - expectedVersion := "0.2.0" - if vs[0].Version != expectedVersion { - t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version) - } - - // Test with `--merge` - - // Remove first two charts. - if err := os.Remove(comp); err != nil { - t.Fatal(err) - } - if err := os.Remove(comp2); err != nil { - t.Fatal(err) - } - // Add a new chart and a new version of an existing chart - if err := linkOrCopy("testdata/testcharts/reqtest-0.1.0.tgz", filepath.Join(dir, "reqtest-0.1.0.tgz")); err != nil { - t.Fatal(err) - } - if err := linkOrCopy("testdata/testcharts/compressedchart-0.3.0.tgz", filepath.Join(dir, "compressedchart-0.3.0.tgz")); err != nil { - t.Fatal(err) - } - - c.ParseFlags([]string{"--merge", destIndex}) - if err := c.RunE(c, []string{dir}); err != nil { - t.Error(err) - } - - index, err = repo.LoadIndexFile(destIndex) - if err != nil { - t.Fatal(err) - } - - if len(index.Entries) != 2 { - t.Errorf("expected 2 entries, got %d: %#v", len(index.Entries), index.Entries) - } - - vs = index.Entries["compressedchart"] - if len(vs) != 3 { - t.Errorf("expected 3 versions, got %d: %#v", len(vs), vs) - } - - expectedVersion = "0.3.0" - if vs[0].Version != expectedVersion { - t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version) - } - - // test that index.yaml gets generated on merge even when it doesn't exist - if err := os.Remove(destIndex); err != nil { - t.Fatal(err) - } - - c.ParseFlags([]string{"--merge", destIndex}) - if err := c.RunE(c, []string{dir}); err != nil { - t.Error(err) - } - - index, err = repo.LoadIndexFile(destIndex) - if err != nil { - t.Fatal(err) - } - - // verify it didn't create an empty index.yaml and the merged happened - if len(index.Entries) != 2 { - t.Errorf("expected 2 entries, got %d: %#v", len(index.Entries), index.Entries) - } - - vs = index.Entries["compressedchart"] - if len(vs) != 1 { - t.Errorf("expected 1 versions, got %d: %#v", len(vs), vs) - } - - expectedVersion = "0.3.0" - if vs[0].Version != expectedVersion { - t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version) - } -} - -func linkOrCopy(old, new string) error { - if err := os.Link(old, new); err != nil { - return copyFile(old, new) - } - - return nil -} - -func copyFile(dst, src string) error { - i, err := os.Open(dst) - if err != nil { - return err - } - defer i.Close() - - o, err := os.Create(src) - if err != nil { - return err - } - defer o.Close() - - _, err = io.Copy(o, i) - - return err -} diff --git a/cmd/helm/repo_list.go b/cmd/helm/repo_list.go deleted file mode 100644 index 25316ba..0000000 --- a/cmd/helm/repo_list.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - "strings" - - "github.com/gosuri/uitable" - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/cli/output" - "helm.sh/helm/v3/pkg/repo" -) - -func newRepoListCmd(out io.Writer) *cobra.Command { - var outfmt output.Format - cmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "list chart repositories", - Args: require.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - f, err := repo.LoadFile(settings.RepositoryConfig) - if isNotExist(err) || len(f.Repositories) == 0 { - return errors.New("no repositories to show") - } - - return outfmt.Write(out, &repoListWriter{f.Repositories}) - }, - } - - bindOutputFlag(cmd, &outfmt) - - return cmd -} - -type repositoryElement struct { - Name string `json:"name"` - URL string `json:"url"` -} - -type repoListWriter struct { - repos []*repo.Entry -} - -func (r *repoListWriter) WriteTable(out io.Writer) error { - table := uitable.New() - table.AddRow("NAME", "URL") - for _, re := range r.repos { - table.AddRow(re.Name, re.URL) - } - return output.EncodeTable(out, table) -} - -func (r *repoListWriter) WriteJSON(out io.Writer) error { - return r.encodeByFormat(out, output.JSON) -} - -func (r *repoListWriter) WriteYAML(out io.Writer) error { - return r.encodeByFormat(out, output.YAML) -} - -func (r *repoListWriter) encodeByFormat(out io.Writer, format output.Format) error { - // Initialize the array so no results returns an empty array instead of null - repolist := make([]repositoryElement, 0, len(r.repos)) - - for _, re := range r.repos { - repolist = append(repolist, repositoryElement{Name: re.Name, URL: re.URL}) - } - - switch format { - case output.JSON: - return output.EncodeJSON(out, repolist) - case output.YAML: - return output.EncodeYAML(out, repolist) - } - - // Because this is a non-exported function and only called internally by - // WriteJSON and WriteYAML, we shouldn't get invalid types - return nil -} - -// Provide dynamic auto-completion for repo names -func compListRepos(prefix string) []string { - var rNames []string - - f, err := repo.LoadFile(settings.RepositoryConfig) - if err == nil && len(f.Repositories) > 0 { - for _, repo := range f.Repositories { - if strings.HasPrefix(repo.Name, prefix) { - rNames = append(rNames, repo.Name) - } - } - } - return rNames -} diff --git a/cmd/helm/repo_list_test.go b/cmd/helm/repo_list_test.go deleted file mode 100644 index f371452..0000000 --- a/cmd/helm/repo_list_test.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" -) - -func TestRepoListOutputCompletion(t *testing.T) { - outputFlagCompletionTest(t, "repo list") -} diff --git a/cmd/helm/repo_remove.go b/cmd/helm/repo_remove.go deleted file mode 100644 index e8c0ec0..0000000 --- a/cmd/helm/repo_remove.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "os" - "path/filepath" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/repo" -) - -type repoRemoveOptions struct { - name string - repoFile string - repoCache string -} - -func newRepoRemoveCmd(out io.Writer) *cobra.Command { - o := &repoRemoveOptions{} - - cmd := &cobra.Command{ - Use: "remove [NAME]", - Aliases: []string{"rm"}, - Short: "remove a chart repository", - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - o.repoFile = settings.RepositoryConfig - o.repoCache = settings.RepositoryCache - o.name = args[0] - return o.run(out) - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListRepos(toComplete), completion.BashCompDirectiveNoFileComp - }) - - return cmd -} - -func (o *repoRemoveOptions) run(out io.Writer) error { - r, err := repo.LoadFile(o.repoFile) - if isNotExist(err) || len(r.Repositories) == 0 { - return errors.New("no repositories configured") - } - - if !r.Remove(o.name) { - return errors.Errorf("no repo named %q found", o.name) - } - if err := r.WriteFile(o.repoFile, 0644); err != nil { - return err - } - - if err := removeRepoCache(o.repoCache, o.name); err != nil { - return err - } - - fmt.Fprintf(out, "%q has been removed from your repositories\n", o.name) - return nil -} - -func removeRepoCache(root, name string) error { - idx := filepath.Join(root, helmpath.CacheChartsFile(name)) - if _, err := os.Stat(idx); err == nil { - os.Remove(idx) - } - - idx = filepath.Join(root, helmpath.CacheIndexFile(name)) - if _, err := os.Stat(idx); os.IsNotExist(err) { - return nil - } else if err != nil { - return errors.Wrapf(err, "can't remove index file %s", idx) - } - return os.Remove(idx) -} diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go deleted file mode 100644 index 85c76bb..0000000 --- a/cmd/helm/repo_remove_test.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bytes" - "os" - "path/filepath" - "strings" - "testing" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/repo" - "helm.sh/helm/v3/pkg/repo/repotest" -) - -func TestRepoRemove(t *testing.T) { - ts, err := repotest.NewTempServer("testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } - defer ts.Stop() - - rootDir := ensure.TempDir(t) - repoFile := filepath.Join(rootDir, "repositories.yaml") - - const testRepoName = "test-name" - - b := bytes.NewBuffer(nil) - - rmOpts := repoRemoveOptions{ - name: testRepoName, - repoFile: repoFile, - repoCache: rootDir, - } - - if err := rmOpts.run(os.Stderr); err == nil { - t.Errorf("Expected error removing %s, but did not get one.", testRepoName) - } - o := &repoAddOptions{ - name: testRepoName, - url: ts.URL(), - repoFile: repoFile, - } - - if err := o.run(os.Stderr); err != nil { - t.Error(err) - } - - idx := filepath.Join(rootDir, helmpath.CacheIndexFile(testRepoName)) - mf, _ := os.Create(idx) - mf.Close() - - idx2 := filepath.Join(rootDir, helmpath.CacheChartsFile(testRepoName)) - mf, _ = os.Create(idx2) - mf.Close() - - b.Reset() - - if err := rmOpts.run(b); err != nil { - t.Errorf("Error removing %s from repositories", testRepoName) - } - if !strings.Contains(b.String(), "has been removed") { - t.Errorf("Unexpected output: %s", b.String()) - } - - if _, err := os.Stat(idx); err == nil { - t.Errorf("Error cache index file was not removed for repository %s", testRepoName) - } - - if _, err := os.Stat(idx2); err == nil { - t.Errorf("Error cache chart file was not removed for repository %s", testRepoName) - } - - f, err := repo.LoadFile(repoFile) - if err != nil { - t.Error(err) - } - - if f.Has(testRepoName) { - t.Errorf("%s was not successfully removed from repositories list", testRepoName) - } -} diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go deleted file mode 100644 index 027f185..0000000 --- a/cmd/helm/repo_update.go +++ /dev/null @@ -1,95 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "sync" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/repo" -) - -const updateDesc = ` -Update gets the latest information about charts from the respective chart repositories. -Information is cached locally, where it is used by commands like 'helm search'. -` - -var errNoRepositories = errors.New("no repositories found. You must add one before updating") - -type repoUpdateOptions struct { - update func([]*repo.ChartRepository, io.Writer) - repoFile string -} - -func newRepoUpdateCmd(out io.Writer) *cobra.Command { - o := &repoUpdateOptions{update: updateCharts} - - cmd := &cobra.Command{ - Use: "update", - Aliases: []string{"up"}, - Short: "update information of available charts locally from chart repositories", - Long: updateDesc, - Args: require.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - o.repoFile = settings.RepositoryConfig - return o.run(out) - }, - } - return cmd -} - -func (o *repoUpdateOptions) run(out io.Writer) error { - f, err := repo.LoadFile(o.repoFile) - if isNotExist(err) || len(f.Repositories) == 0 { - return errNoRepositories - } - var repos []*repo.ChartRepository - for _, cfg := range f.Repositories { - r, err := repo.NewChartRepository(cfg, getter.All(settings)) - if err != nil { - return err - } - repos = append(repos, r) - } - - o.update(repos, out) - return nil -} - -func updateCharts(repos []*repo.ChartRepository, out io.Writer) { - fmt.Fprintln(out, "Hang tight while we grab the latest from your chart repositories...") - var wg sync.WaitGroup - for _, re := range repos { - wg.Add(1) - go func(re *repo.ChartRepository) { - defer wg.Done() - if _, err := re.DownloadIndexFile(); err != nil { - fmt.Fprintf(out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", re.Config.Name, re.Config.URL, err) - } else { - fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", re.Config.Name) - } - }(re) - } - wg.Wait() - fmt.Fprintln(out, "Update Complete. ⎈ Happy Helming!⎈ ") -} diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go deleted file mode 100644 index 6ddce06..0000000 --- a/cmd/helm/repo_update_test.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bytes" - "fmt" - "io" - "strings" - "testing" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/repo" - "helm.sh/helm/v3/pkg/repo/repotest" -) - -func TestUpdateCmd(t *testing.T) { - var out bytes.Buffer - // Instead of using the HTTP updater, we provide our own for this test. - // The TestUpdateCharts test verifies the HTTP behavior independently. - updater := func(repos []*repo.ChartRepository, out io.Writer) { - for _, re := range repos { - fmt.Fprintln(out, re.Config.Name) - } - } - o := &repoUpdateOptions{ - update: updater, - repoFile: "testdata/repositories.yaml", - } - if err := o.run(&out); err != nil { - t.Fatal(err) - } - - if got := out.String(); !strings.Contains(got, "charts") { - t.Errorf("Expected 'charts' got %q", got) - } -} - -func TestUpdateCharts(t *testing.T) { - defer resetEnv()() - defer ensure.HelmHome(t)() - - ts, err := repotest.NewTempServer("testdata/testserver/*.*") - if err != nil { - t.Fatal(err) - } - defer ts.Stop() - - r, err := repo.NewChartRepository(&repo.Entry{ - Name: "charts", - URL: ts.URL(), - }, getter.All(settings)) - if err != nil { - t.Error(err) - } - - b := bytes.NewBuffer(nil) - updateCharts([]*repo.ChartRepository{r}, b) - - got := b.String() - if strings.Contains(got, "Unable to get an update") { - t.Errorf("Failed to get a repo: %q", got) - } - if !strings.Contains(got, "Update Complete.") { - t.Error("Update was not successful") - } -} diff --git a/cmd/helm/require/args.go b/cmd/helm/require/args.go deleted file mode 100644 index cfa8a01..0000000 --- a/cmd/helm/require/args.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package require - -import ( - "github.com/pkg/errors" - "github.com/spf13/cobra" -) - -// NoArgs returns an error if any args are included. -func NoArgs(cmd *cobra.Command, args []string) error { - if len(args) > 0 { - return errors.Errorf( - "%q accepts no arguments\n\nUsage: %s", - cmd.CommandPath(), - cmd.UseLine(), - ) - } - return nil -} - -// ExactArgs returns an error if there are not exactly n args. -func ExactArgs(n int) cobra.PositionalArgs { - return func(cmd *cobra.Command, args []string) error { - if len(args) != n { - return errors.Errorf( - "%q requires %d %s\n\nUsage: %s", - cmd.CommandPath(), - n, - pluralize("argument", n), - cmd.UseLine(), - ) - } - return nil - } -} - -// MaximumNArgs returns an error if there are more than N args. -func MaximumNArgs(n int) cobra.PositionalArgs { - return func(cmd *cobra.Command, args []string) error { - if len(args) > n { - return errors.Errorf( - "%q accepts at most %d %s\n\nUsage: %s", - cmd.CommandPath(), - n, - pluralize("argument", n), - cmd.UseLine(), - ) - } - return nil - } -} - -// MinimumNArgs returns an error if there is not at least N args. -func MinimumNArgs(n int) cobra.PositionalArgs { - return func(cmd *cobra.Command, args []string) error { - if len(args) < n { - return errors.Errorf( - "%q requires at least %d %s\n\nUsage: %s", - cmd.CommandPath(), - n, - pluralize("argument", n), - cmd.UseLine(), - ) - } - return nil - } -} - -func pluralize(word string, n int) string { - if n == 1 { - return word - } - return word + "s" -} diff --git a/cmd/helm/require/args_test.go b/cmd/helm/require/args_test.go deleted file mode 100644 index c8d5c31..0000000 --- a/cmd/helm/require/args_test.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package require - -import ( - "fmt" - "io/ioutil" - "strings" - "testing" - - "github.com/spf13/cobra" -) - -func TestArgs(t *testing.T) { - runTestCases(t, []testCase{{ - validateFunc: NoArgs, - }, { - args: []string{"one"}, - validateFunc: NoArgs, - wantError: `"root" accepts no arguments`, - }, { - args: []string{"one"}, - validateFunc: ExactArgs(1), - }, { - validateFunc: ExactArgs(1), - wantError: `"root" requires 1 argument`, - }, { - validateFunc: ExactArgs(2), - wantError: `"root" requires 2 arguments`, - }, { - args: []string{"one"}, - validateFunc: MaximumNArgs(1), - }, { - args: []string{"one", "two"}, - validateFunc: MaximumNArgs(1), - wantError: `"root" accepts at most 1 argument`, - }, { - validateFunc: MinimumNArgs(1), - wantError: `"root" requires at least 1 argument`, - }, { - args: []string{"one", "two"}, - validateFunc: MinimumNArgs(1), - }}) -} - -type testCase struct { - args []string - validateFunc cobra.PositionalArgs - wantError string -} - -func runTestCases(t *testing.T, testCases []testCase) { - for i, tc := range testCases { - t.Run(fmt.Sprint(i), func(t *testing.T) { - cmd := &cobra.Command{ - Use: "root", - Run: func(*cobra.Command, []string) {}, - Args: tc.validateFunc, - } - cmd.SetArgs(tc.args) - cmd.SetOutput(ioutil.Discard) - - err := cmd.Execute() - if tc.wantError == "" { - if err != nil { - t.Fatalf("unexpected error, got '%v'", err) - } - return - } - if !strings.Contains(err.Error(), tc.wantError) { - t.Fatalf("unexpected error \n\nWANT:\n%q\n\nGOT:\n%q\n", tc.wantError, err) - } - if !strings.Contains(err.Error(), "Usage:") { - t.Fatalf("unexpected error: want Usage string\n\nGOT:\n%q\n", err) - } - }) - } -} diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go deleted file mode 100644 index 745e910..0000000 --- a/cmd/helm/rollback.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "strconv" - "time" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" -) - -const rollbackDesc = ` -This command rolls back a release to a previous revision. - -The first argument of the rollback command is the name of a release, and the -second is a revision (version) number. If this argument is omitted, it will -roll back to the previous release. - -To see revision numbers, run 'helm history RELEASE'. -` - -func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewRollback(cfg) - - cmd := &cobra.Command{ - Use: "rollback [REVISION]", - Short: "roll back a release to a previous revision", - Long: rollbackDesc, - Args: require.MinimumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) > 1 { - ver, err := strconv.Atoi(args[1]) - if err != nil { - return fmt.Errorf("could not convert revision to a number: %v", err) - } - client.Version = ver - } - - if err := client.Run(args[0]); err != nil { - return err - } - - fmt.Fprintf(out, "Rollback was a success! Happy Helming!\n") - return nil - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.BoolVar(&client.DryRun, "dry-run", false, "simulate a rollback") - f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") - f.BoolVar(&client.Force, "force", false, "force resource update through delete/recreate if needed") - f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during rollback") - f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") - f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout") - f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this rollback when rollback fails") - - return cmd -} diff --git a/cmd/helm/rollback_test.go b/cmd/helm/rollback_test.go deleted file mode 100644 index fdc627b..0000000 --- a/cmd/helm/rollback_test.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/release" -) - -func TestRollbackCmd(t *testing.T) { - rels := []*release.Release{ - { - Name: "funny-honey", - Info: &release.Info{Status: release.StatusSuperseded}, - Chart: &chart.Chart{}, - Version: 1, - }, - { - Name: "funny-honey", - Info: &release.Info{Status: release.StatusDeployed}, - Chart: &chart.Chart{}, - Version: 2, - }, - } - - tests := []cmdTestCase{{ - name: "rollback a release", - cmd: "rollback funny-honey 1", - golden: "output/rollback.txt", - rels: rels, - }, { - name: "rollback a release with timeout", - cmd: "rollback funny-honey 1 --timeout 120s", - golden: "output/rollback-timeout.txt", - rels: rels, - }, { - name: "rollback a release with wait", - cmd: "rollback funny-honey 1 --wait", - golden: "output/rollback-wait.txt", - rels: rels, - }, { - name: "rollback a release without revision", - cmd: "rollback funny-honey", - golden: "output/rollback-no-revision.txt", - rels: rels, - }, { - name: "rollback a release without release name", - cmd: "rollback", - golden: "output/rollback-no-args.txt", - rels: rels, - wantError: true, - }} - runTestCmd(t, tests) -} diff --git a/cmd/helm/root.go b/cmd/helm/root.go deleted file mode 100644 index 6ce1dcb..0000000 --- a/cmd/helm/root.go +++ /dev/null @@ -1,208 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main // import "helm.sh/helm/v3/cmd/helm" - -import ( - "fmt" - "io" - "strings" - - "github.com/spf13/cobra" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/internal/experimental/registry" - "helm.sh/helm/v3/pkg/action" -) - -const ( - contextCompFunc = ` -__helm_get_contexts() -{ - __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - local template out - template="{{ range .contexts }}{{ .name }} {{ end }}" - if out=$(kubectl config -o template --template="${template}" view 2>/dev/null); then - COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) - fi -} -` -) - -var ( - // Mapping of global flags that can have dynamic completion and the - // completion function to be used. - bashCompletionFlags = map[string]string{ - // Cannot convert the kube-context flag to Go completion yet because - // an incomplete kube-context will make actionConfig.Init() fail at the very start - "kube-context": "__helm_get_contexts", - } -) - -var globalUsage = `The Kubernetes package manager - -Common actions for Helm: - -- helm search: search for charts -- helm pull: download a chart to your local directory to view -- helm install: upload the chart to Kubernetes -- helm list: list releases of charts - -Environment variables: - -+------------------+-----------------------------------------------------------------------------+ -| Name | Description | -+------------------+-----------------------------------------------------------------------------+ -| $XDG_CACHE_HOME | set an alternative location for storing cached files. | -| $XDG_CONFIG_HOME | set an alternative location for storing Helm configuration. | -| $XDG_DATA_HOME | set an alternative location for storing Helm data. | -| $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory | -| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. | -| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") | -+------------------+-----------------------------------------------------------------------------+ - -Helm stores configuration based on the XDG base directory specification, so - -- cached files are stored in $XDG_CACHE_HOME/helm -- configuration is stored in $XDG_CONFIG_HOME/helm -- data is stored in $XDG_DATA_HOME/helm - -By default, the default directories depend on the Operating System. The defaults are listed below: - -+------------------+---------------------------+--------------------------------+-------------------------+ -| Operating System | Cache Path | Configuration Path | Data Path | -+------------------+---------------------------+--------------------------------+-------------------------+ -| Linux | $HOME/.cache/helm | $HOME/.config/helm | $HOME/.local/share/helm | -| macOS | $HOME/Library/Caches/helm | $HOME/Library/Preferences/helm | $HOME/Library/helm | -| Windows | %TEMP%\helm | %APPDATA%\helm | %APPDATA%\helm | -+------------------+---------------------------+--------------------------------+-------------------------+ -` - -func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string) *cobra.Command { - cmd := &cobra.Command{ - Use: "helm", - Short: "The Helm package manager for Kubernetes.", - Long: globalUsage, - SilenceUsage: true, - Args: require.NoArgs, - BashCompletionFunction: fmt.Sprintf("%s%s", contextCompFunc, completion.GetBashCustomFunction()), - } - flags := cmd.PersistentFlags() - - settings.AddFlags(flags) - - flag := flags.Lookup("namespace") - // Setup shell completion for the namespace flag - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if client, err := actionConfig.KubernetesClientSet(); err == nil { - // Choose a long enough timeout that the user notices somethings is not working - // but short enough that the user is not made to wait very long - to := int64(3) - completion.CompDebugln(fmt.Sprintf("About to call kube client for namespaces with timeout of: %d", to)) - - nsNames := []string{} - if namespaces, err := client.CoreV1().Namespaces().List(metav1.ListOptions{TimeoutSeconds: &to}); err == nil { - for _, ns := range namespaces.Items { - if strings.HasPrefix(ns.Name, toComplete) { - nsNames = append(nsNames, ns.Name) - } - } - return nsNames, completion.BashCompDirectiveNoFileComp - } - } - return nil, completion.BashCompDirectiveDefault - }) - - // We can safely ignore any errors that flags.Parse encounters since - // those errors will be caught later during the call to cmd.Execution. - // This call is required to gather configuration information prior to - // execution. - flags.ParseErrorsWhitelist.UnknownFlags = true - flags.Parse(args) - - // Add subcommands - cmd.AddCommand( - // chart commands - newCreateCmd(out), - newDependencyCmd(out), - newPullCmd(out), - newShowCmd(out), - newLintCmd(out), - newPackageCmd(out), - newRepoCmd(out), - newSearchCmd(out), - newVerifyCmd(out), - - // release commands - newGetCmd(actionConfig, out), - newHistoryCmd(actionConfig, out), - newInstallCmd(actionConfig, out), - newListCmd(actionConfig, out), - newReleaseTestCmd(actionConfig, out), - newRollbackCmd(actionConfig, out), - newStatusCmd(actionConfig, out), - newTemplateCmd(actionConfig, out), - newUninstallCmd(actionConfig, out), - newUpgradeCmd(actionConfig, out), - - newCompletionCmd(out), - newEnvCmd(out), - newPluginCmd(out), - newVersionCmd(out), - - // Hidden documentation generator command: 'helm docs' - newDocsCmd(out), - - // Setup the special hidden __complete command to allow for dynamic auto-completion - completion.NewCompleteCmd(settings, out), - ) - - // Add annotation to flags for which we can generate completion choices - for name, completion := range bashCompletionFlags { - if cmd.Flag(name) != nil { - if cmd.Flag(name).Annotations == nil { - cmd.Flag(name).Annotations = map[string][]string{} - } - cmd.Flag(name).Annotations[cobra.BashCompCustom] = append( - cmd.Flag(name).Annotations[cobra.BashCompCustom], - completion, - ) - } - } - - // Add *experimental* subcommands - registryClient, err := registry.NewClient( - registry.ClientOptDebug(settings.Debug), - registry.ClientOptWriter(out), - ) - if err != nil { - // TODO: don't panic here, refactor newRootCmd to return error - panic(err) - } - actionConfig.RegistryClient = registryClient - cmd.AddCommand( - newRegistryCmd(actionConfig, out), - newChartCmd(actionConfig, out), - ) - - // Find and add plugins - loadPlugins(cmd, out) - - return cmd -} diff --git a/cmd/helm/root_test.go b/cmd/helm/root_test.go deleted file mode 100644 index df592a9..0000000 --- a/cmd/helm/root_test.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "os" - "path/filepath" - "testing" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/helmpath/xdg" -) - -func TestRootCmd(t *testing.T) { - defer resetEnv()() - - tests := []struct { - name, args, cachePath, configPath, dataPath string - envvars map[string]string - }{ - { - name: "defaults", - args: "env", - }, - { - name: "with $XDG_CACHE_HOME set", - args: "env", - envvars: map[string]string{xdg.CacheHomeEnvVar: "/bar"}, - cachePath: "/bar/helm", - }, - { - name: "with $XDG_CONFIG_HOME set", - args: "env", - envvars: map[string]string{xdg.ConfigHomeEnvVar: "/bar"}, - configPath: "/bar/helm", - }, - { - name: "with $XDG_DATA_HOME set", - args: "env", - envvars: map[string]string{xdg.DataHomeEnvVar: "/bar"}, - dataPath: "/bar/helm", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer ensure.HelmHome(t)() - - for k, v := range tt.envvars { - os.Setenv(k, v) - } - - if _, _, err := executeActionCommand(tt.args); err != nil { - t.Fatalf("unexpected error: %s", err) - } - - // NOTE(bacongobbler): we need to check here after calling ensure.HelmHome so we - // load the proper paths after XDG_*_HOME is set - if tt.cachePath == "" { - tt.cachePath = filepath.Join(os.Getenv(xdg.CacheHomeEnvVar), "helm") - } - - if tt.configPath == "" { - tt.configPath = filepath.Join(os.Getenv(xdg.ConfigHomeEnvVar), "helm") - } - - if tt.dataPath == "" { - tt.dataPath = filepath.Join(os.Getenv(xdg.DataHomeEnvVar), "helm") - } - - if helmpath.CachePath() != tt.cachePath { - t.Errorf("expected cache path %q, got %q", tt.cachePath, helmpath.CachePath()) - } - if helmpath.ConfigPath() != tt.configPath { - t.Errorf("expected config path %q, got %q", tt.configPath, helmpath.ConfigPath()) - } - if helmpath.DataPath() != tt.dataPath { - t.Errorf("expected data path %q, got %q", tt.dataPath, helmpath.DataPath()) - } - }) - } -} diff --git a/cmd/helm/search.go b/cmd/helm/search.go deleted file mode 100644 index 240d5e7..0000000 --- a/cmd/helm/search.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" -) - -const searchDesc = ` -Search provides the ability to search for Helm charts in the various places -they can be stored including the Helm Hub and repositories you have added. Use -search subcommands to search different locations for charts. -` - -func newSearchCmd(out io.Writer) *cobra.Command { - - cmd := &cobra.Command{ - Use: "search [keyword]", - Short: "search for a keyword in charts", - Long: searchDesc, - } - - cmd.AddCommand(newSearchHubCmd(out)) - cmd.AddCommand(newSearchRepoCmd(out)) - - return cmd -} diff --git a/cmd/helm/search/search.go b/cmd/helm/search/search.go deleted file mode 100644 index fc7f305..0000000 --- a/cmd/helm/search/search.go +++ /dev/null @@ -1,227 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package search provides client-side repository searching. - -This supports building an in-memory search index based on the contents of -multiple repositories, and then using string matching or regular expressions -to find matches. -*/ -package search - -import ( - "path" - "regexp" - "sort" - "strings" - - "github.com/Masterminds/semver/v3" - - "helm.sh/helm/v3/pkg/repo" -) - -// Result is a search result. -// -// Score indicates how close it is to match. The higher the score, the longer -// the distance. -type Result struct { - Name string - Score int - Chart *repo.ChartVersion -} - -// Index is a searchable index of chart information. -type Index struct { - lines map[string]string - charts map[string]*repo.ChartVersion -} - -const sep = "\v" - -// NewIndex creates a new Index. -func NewIndex() *Index { - return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}} -} - -// verSep is a separator for version fields in map keys. -const verSep = "$$" - -// AddRepo adds a repository index to the search index. -func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) { - ind.SortEntries() - for name, ref := range ind.Entries { - if len(ref) == 0 { - // Skip chart names that have zero releases. - continue - } - // By convention, an index file is supposed to have the newest at the - // 0 slot, so our best bet is to grab the 0 entry and build the index - // entry off of that. - // Note: Do not use filePath.Join since on Windows it will return \ - // which results in a repo name that cannot be understood. - fname := path.Join(rname, name) - if !all { - i.lines[fname] = indstr(rname, ref[0]) - i.charts[fname] = ref[0] - continue - } - - // If 'all' is set, then we go through all of the refs, and add them all - // to the index. This will generate a lot of near-duplicate entries. - for _, rr := range ref { - versionedName := fname + verSep + rr.Version - i.lines[versionedName] = indstr(rname, rr) - i.charts[versionedName] = rr - } - } -} - -// All returns all charts in the index as if they were search results. -// -// Each will be given a score of 0. -func (i *Index) All() []*Result { - res := make([]*Result, len(i.charts)) - j := 0 - for name, ch := range i.charts { - parts := strings.Split(name, verSep) - res[j] = &Result{ - Name: parts[0], - Chart: ch, - } - j++ - } - return res -} - -// Search searches an index for the given term. -// -// Threshold indicates the maximum score a term may have before being marked -// irrelevant. (Low score means higher relevance. Golf, not bowling.) -// -// If regexp is true, the term is treated as a regular expression. Otherwise, -// term is treated as a literal string. -func (i *Index) Search(term string, threshold int, regexp bool) ([]*Result, error) { - if regexp { - return i.SearchRegexp(term, threshold) - } - return i.SearchLiteral(term, threshold), nil -} - -// calcScore calculates a score for a match. -func (i *Index) calcScore(index int, matchline string) int { - - // This is currently tied to the fact that sep is a single char. - splits := []int{} - s := rune(sep[0]) - for i, ch := range matchline { - if ch == s { - splits = append(splits, i) - } - } - - for i, pos := range splits { - if index > pos { - continue - } - return i - } - return len(splits) -} - -// SearchLiteral does a literal string search (no regexp). -func (i *Index) SearchLiteral(term string, threshold int) []*Result { - term = strings.ToLower(term) - buf := []*Result{} - for k, v := range i.lines { - lk := strings.ToLower(k) - lv := strings.ToLower(v) - res := strings.Index(lv, term) - if score := i.calcScore(res, lv); res != -1 && score < threshold { - parts := strings.Split(lk, verSep) // Remove version, if it is there. - buf = append(buf, &Result{Name: parts[0], Score: score, Chart: i.charts[k]}) - } - } - return buf -} - -// SearchRegexp searches using a regular expression. -func (i *Index) SearchRegexp(re string, threshold int) ([]*Result, error) { - matcher, err := regexp.Compile(re) - if err != nil { - return []*Result{}, err - } - buf := []*Result{} - for k, v := range i.lines { - ind := matcher.FindStringIndex(v) - if len(ind) == 0 { - continue - } - if score := i.calcScore(ind[0], v); ind[0] >= 0 && score < threshold { - parts := strings.Split(k, verSep) // Remove version, if it is there. - buf = append(buf, &Result{Name: parts[0], Score: score, Chart: i.charts[k]}) - } - } - return buf, nil -} - -// SortScore does an in-place sort of the results. -// -// Lowest scores are highest on the list. Matching scores are subsorted alphabetically. -func SortScore(r []*Result) { - sort.Sort(scoreSorter(r)) -} - -// scoreSorter sorts results by score, and subsorts by alpha Name. -type scoreSorter []*Result - -// Len returns the length of this scoreSorter. -func (s scoreSorter) Len() int { return len(s) } - -// Swap performs an in-place swap. -func (s scoreSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// Less compares a to b, and returns true if a is less than b. -func (s scoreSorter) Less(a, b int) bool { - first := s[a] - second := s[b] - - if first.Score > second.Score { - return false - } - if first.Score < second.Score { - return true - } - if first.Name == second.Name { - v1, err := semver.NewVersion(first.Chart.Version) - if err != nil { - return true - } - v2, err := semver.NewVersion(second.Chart.Version) - if err != nil { - return true - } - // Sort so that the newest chart is higher than the oldest chart. This is - // the opposite of what you'd expect in a function called Less. - return v1.GreaterThan(v2) - } - return first.Name < second.Name -} - -func indstr(name string, ref *repo.ChartVersion) string { - i := ref.Name + sep + name + "/" + ref.Name + sep + - ref.Description + sep + strings.Join(ref.Keywords, " ") - return i -} diff --git a/cmd/helm/search/search_test.go b/cmd/helm/search/search_test.go deleted file mode 100644 index 9c1859d..0000000 --- a/cmd/helm/search/search_test.go +++ /dev/null @@ -1,303 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package search - -import ( - "strings" - "testing" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/repo" -) - -func TestSortScore(t *testing.T) { - in := []*Result{ - {Name: "bbb", Score: 0, Chart: &repo.ChartVersion{Metadata: &chart.Metadata{Version: "1.2.3"}}}, - {Name: "aaa", Score: 5}, - {Name: "abb", Score: 5}, - {Name: "aab", Score: 0}, - {Name: "bab", Score: 5}, - {Name: "ver", Score: 5, Chart: &repo.ChartVersion{Metadata: &chart.Metadata{Version: "1.2.4"}}}, - {Name: "ver", Score: 5, Chart: &repo.ChartVersion{Metadata: &chart.Metadata{Version: "1.2.3"}}}, - } - expect := []string{"aab", "bbb", "aaa", "abb", "bab", "ver", "ver"} - expectScore := []int{0, 0, 5, 5, 5, 5, 5} - SortScore(in) - - // Test Score - for i := 0; i < len(expectScore); i++ { - if expectScore[i] != in[i].Score { - t.Errorf("Sort error on index %d: expected %d, got %d", i, expectScore[i], in[i].Score) - } - } - // Test Name - for i := 0; i < len(expect); i++ { - if expect[i] != in[i].Name { - t.Errorf("Sort error: expected %s, got %s", expect[i], in[i].Name) - } - } - - // Test version of last two items - if in[5].Chart.Version != "1.2.4" { - t.Errorf("Expected 1.2.4, got %s", in[5].Chart.Version) - } - if in[6].Chart.Version != "1.2.3" { - t.Error("Expected 1.2.3 to be last") - } -} - -var indexfileEntries = map[string]repo.ChartVersions{ - "niña": { - { - URLs: []string{"http://example.com/charts/nina-0.1.0.tgz"}, - Metadata: &chart.Metadata{ - Name: "niña", - Version: "0.1.0", - Description: "One boat", - }, - }, - }, - "pinta": { - { - URLs: []string{"http://example.com/charts/pinta-0.1.0.tgz"}, - Metadata: &chart.Metadata{ - Name: "pinta", - Version: "0.1.0", - Description: "Two ship", - }, - }, - }, - "santa-maria": { - { - URLs: []string{"http://example.com/charts/santa-maria-1.2.3.tgz"}, - Metadata: &chart.Metadata{ - Name: "santa-maria", - Version: "1.2.3", - Description: "Three boat", - }, - }, - { - URLs: []string{"http://example.com/charts/santa-maria-1.2.2-rc-1.tgz"}, - Metadata: &chart.Metadata{ - Name: "santa-maria", - Version: "1.2.2-RC-1", - Description: "Three boat", - }, - }, - }, -} - -func loadTestIndex(t *testing.T, all bool) *Index { - i := NewIndex() - i.AddRepo("testing", &repo.IndexFile{Entries: indexfileEntries}, all) - i.AddRepo("ztesting", &repo.IndexFile{Entries: map[string]repo.ChartVersions{ - "pinta": { - { - URLs: []string{"http://example.com/charts/pinta-2.0.0.tgz"}, - Metadata: &chart.Metadata{ - Name: "pinta", - Version: "2.0.0", - Description: "Two ship, version two", - }, - }, - }, - }}, all) - return i -} - -func TestAll(t *testing.T) { - i := loadTestIndex(t, false) - all := i.All() - if len(all) != 4 { - t.Errorf("Expected 4 entries, got %d", len(all)) - } - - i = loadTestIndex(t, true) - all = i.All() - if len(all) != 5 { - t.Errorf("Expected 5 entries, got %d", len(all)) - } -} - -func TestAddRepo_Sort(t *testing.T) { - i := loadTestIndex(t, true) - sr, err := i.Search("TESTING/SANTA-MARIA", 100, false) - if err != nil { - t.Fatal(err) - } - SortScore(sr) - - ch := sr[0] - expect := "1.2.3" - if ch.Chart.Version != expect { - t.Errorf("Expected %q, got %q", expect, ch.Chart.Version) - } -} - -func TestSearchByName(t *testing.T) { - - tests := []struct { - name string - query string - expect []*Result - regexp bool - fail bool - failMsg string - }{ - { - name: "basic search for one result", - query: "santa-maria", - expect: []*Result{ - {Name: "testing/santa-maria"}, - }, - }, - { - name: "basic search for two results", - query: "pinta", - expect: []*Result{ - {Name: "testing/pinta"}, - {Name: "ztesting/pinta"}, - }, - }, - { - name: "repo-specific search for one result", - query: "ztesting/pinta", - expect: []*Result{ - {Name: "ztesting/pinta"}, - }, - }, - { - name: "partial name search", - query: "santa", - expect: []*Result{ - {Name: "testing/santa-maria"}, - }, - }, - { - name: "description search, one result", - query: "Three", - expect: []*Result{ - {Name: "testing/santa-maria"}, - }, - }, - { - name: "description search, two results", - query: "two", - expect: []*Result{ - {Name: "testing/pinta"}, - {Name: "ztesting/pinta"}, - }, - }, - { - name: "description upper search, two results", - query: "TWO", - expect: []*Result{ - {Name: "testing/pinta"}, - {Name: "ztesting/pinta"}, - }, - }, - { - name: "nothing found", - query: "mayflower", - expect: []*Result{}, - }, - { - name: "regexp, one result", - query: "Th[ref]*", - expect: []*Result{ - {Name: "testing/santa-maria"}, - }, - regexp: true, - }, - { - name: "regexp, fail compile", - query: "th[", - expect: []*Result{}, - regexp: true, - fail: true, - failMsg: "error parsing regexp:", - }, - } - - i := loadTestIndex(t, false) - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - - charts, err := i.Search(tt.query, 100, tt.regexp) - if err != nil { - if tt.fail { - if !strings.Contains(err.Error(), tt.failMsg) { - t.Fatalf("Unexpected error message: %s", err) - } - return - } - t.Fatalf("%s: %s", tt.name, err) - } - // Give us predictably ordered results. - SortScore(charts) - - l := len(charts) - if l != len(tt.expect) { - t.Fatalf("Expected %d result, got %d", len(tt.expect), l) - } - // For empty result sets, just keep going. - if l == 0 { - return - } - - for i, got := range charts { - ex := tt.expect[i] - if got.Name != ex.Name { - t.Errorf("[%d]: Expected name %q, got %q", i, ex.Name, got.Name) - } - } - - }) - } -} - -func TestSearchByNameAll(t *testing.T) { - // Test with the All bit turned on. - i := loadTestIndex(t, true) - cs, err := i.Search("santa-maria", 100, false) - if err != nil { - t.Fatal(err) - } - if len(cs) != 2 { - t.Errorf("expected 2 charts, got %d", len(cs)) - } -} - -func TestCalcScore(t *testing.T) { - i := NewIndex() - - fields := []string{"aaa", "bbb", "ccc", "ddd"} - matchline := strings.Join(fields, sep) - if r := i.calcScore(2, matchline); r != 0 { - t.Errorf("Expected 0, got %d", r) - } - if r := i.calcScore(5, matchline); r != 1 { - t.Errorf("Expected 1, got %d", r) - } - if r := i.calcScore(10, matchline); r != 2 { - t.Errorf("Expected 2, got %d", r) - } - if r := i.calcScore(14, matchline); r != 3 { - t.Errorf("Expected 3, got %d", r) - } -} diff --git a/cmd/helm/search_hub.go b/cmd/helm/search_hub.go deleted file mode 100644 index 89139ec..0000000 --- a/cmd/helm/search_hub.go +++ /dev/null @@ -1,150 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "strings" - - "github.com/gosuri/uitable" - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/internal/monocular" - "helm.sh/helm/v3/pkg/cli/output" -) - -const searchHubDesc = ` -Search the Helm Hub or an instance of Monocular for Helm charts. - -The Helm Hub provides a centralized search for publicly available distributed -charts. It is maintained by the Helm project. It can be visited at -https://hub.helm.sh - -Monocular is a web-based application that enables the search and discovery of -charts from multiple Helm Chart repositories. It is the codebase that powers the -Helm Hub. You can find it at https://github.com/helm/monocular -` - -type searchHubOptions struct { - searchEndpoint string - maxColWidth uint - outputFormat output.Format -} - -func newSearchHubCmd(out io.Writer) *cobra.Command { - o := &searchHubOptions{} - - cmd := &cobra.Command{ - Use: "hub [keyword]", - Short: "search for charts in the Helm Hub or an instance of Monocular", - Long: searchHubDesc, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out, args) - }, - } - - f := cmd.Flags() - f.StringVar(&o.searchEndpoint, "endpoint", "https://hub.helm.sh", "monocular instance to query for charts") - f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table") - bindOutputFlag(cmd, &o.outputFormat) - - return cmd -} - -func (o *searchHubOptions) run(out io.Writer, args []string) error { - c, err := monocular.New(o.searchEndpoint) - if err != nil { - return errors.Wrap(err, fmt.Sprintf("unable to create connection to %q", o.searchEndpoint)) - } - - q := strings.Join(args, " ") - results, err := c.Search(q) - if err != nil { - debug("%s", err) - return fmt.Errorf("unable to perform search against %q", o.searchEndpoint) - } - - return o.outputFormat.Write(out, newHubSearchWriter(results, o.searchEndpoint, o.maxColWidth)) -} - -type hubChartElement struct { - URL string `json:"url"` - Version string `json:"version"` - AppVersion string `json:"app_version"` - Description string `json:"description"` -} - -type hubSearchWriter struct { - elements []hubChartElement - columnWidth uint -} - -func newHubSearchWriter(results []monocular.SearchResult, endpoint string, columnWidth uint) *hubSearchWriter { - var elements []hubChartElement - for _, r := range results { - url := endpoint + "/charts/" + r.ID - elements = append(elements, hubChartElement{url, r.Relationships.LatestChartVersion.Data.Version, r.Relationships.LatestChartVersion.Data.AppVersion, r.Attributes.Description}) - } - return &hubSearchWriter{elements, columnWidth} -} - -func (h *hubSearchWriter) WriteTable(out io.Writer) error { - if len(h.elements) == 0 { - _, err := out.Write([]byte("No results found\n")) - if err != nil { - return fmt.Errorf("unable to write results: %s", err) - } - return nil - } - table := uitable.New() - table.MaxColWidth = h.columnWidth - table.AddRow("URL", "CHART VERSION", "APP VERSION", "DESCRIPTION") - for _, r := range h.elements { - table.AddRow(r.URL, r.Version, r.AppVersion, r.Description) - } - return output.EncodeTable(out, table) -} - -func (h *hubSearchWriter) WriteJSON(out io.Writer) error { - return h.encodeByFormat(out, output.JSON) -} - -func (h *hubSearchWriter) WriteYAML(out io.Writer) error { - return h.encodeByFormat(out, output.YAML) -} - -func (h *hubSearchWriter) encodeByFormat(out io.Writer, format output.Format) error { - // Initialize the array so no results returns an empty array instead of null - chartList := make([]hubChartElement, 0, len(h.elements)) - - for _, r := range h.elements { - chartList = append(chartList, hubChartElement{r.URL, r.Version, r.AppVersion, r.Description}) - } - - switch format { - case output.JSON: - return output.EncodeJSON(out, chartList) - case output.YAML: - return output.EncodeYAML(out, chartList) - } - - // Because this is a non-exported function and only called internally by - // WriteJSON and WriteYAML, we shouldn't get invalid types - return nil -} diff --git a/cmd/helm/search_hub_test.go b/cmd/helm/search_hub_test.go deleted file mode 100644 index 7b0f3a3..0000000 --- a/cmd/helm/search_hub_test.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" -) - -func TestSearchHubCmd(t *testing.T) { - - // Setup a mock search service - var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://kubernetes-charts.storage.googleapis.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://kubernetes-charts.storage.googleapis.com/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}` - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, searchResult) - })) - defer ts.Close() - - // The expected output has the URL to the mocked search service in it - var expected = fmt.Sprintf(`URL CHART VERSION APP VERSION DESCRIPTION -%s/charts/stable/phpmyadmin 3.0.0 4.9.0-1 phpMyAdmin is an mysql administration frontend -%s/charts/bitnami/phpmyadmin 3.0.0 4.9.0-1 phpMyAdmin is an mysql administration frontend -`, ts.URL, ts.URL) - - testcmd := "search hub --endpoint " + ts.URL + " maria" - storage := storageFixture() - _, out, err := executeActionCommandC(storage, testcmd) - if err != nil { - t.Errorf("unexpected error, %s", err) - } - if out != expected { - t.Error("expected and actual output did not match") - t.Log(out) - t.Log(expected) - } -} - -func TestSearchHubOutputCompletion(t *testing.T) { - outputFlagCompletionTest(t, "search hub") -} diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go deleted file mode 100644 index 9f5af1e..0000000 --- a/cmd/helm/search_repo.go +++ /dev/null @@ -1,386 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bufio" - "bytes" - "fmt" - "io" - "io/ioutil" - "path/filepath" - "strings" - - "github.com/Masterminds/semver/v3" - "github.com/gosuri/uitable" - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/search" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/cli/output" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/repo" -) - -const searchRepoDesc = ` -Search reads through all of the repositories configured on the system, and -looks for matches. Search of these repositories uses the metadata stored on -the system. - -It will display the latest stable versions of the charts found. If you -specify the --devel flag, the output will include pre-release versions. -If you want to search using a version constraint, use --version. - -Examples: - - # Search for stable release versions matching the keyword "nginx" - $ helm search repo nginx - - # Search for release versions matching the keyword "nginx", including pre-release versions - $ helm search repo nginx --devel - - # Search for the latest stable release for nginx-ingress with a major version of 1 - $ helm search repo nginx-ingress --version ^1.0.0 - -Repositories are managed with 'helm repo' commands. -` - -// searchMaxScore suggests that any score higher than this is not considered a match. -const searchMaxScore = 25 - -type searchRepoOptions struct { - versions bool - regexp bool - devel bool - version string - maxColWidth uint - repoFile string - repoCacheDir string - outputFormat output.Format -} - -func newSearchRepoCmd(out io.Writer) *cobra.Command { - o := &searchRepoOptions{} - - cmd := &cobra.Command{ - Use: "repo [keyword]", - Short: "search repositories for a keyword in charts", - Long: searchRepoDesc, - RunE: func(cmd *cobra.Command, args []string) error { - o.repoFile = settings.RepositoryConfig - o.repoCacheDir = settings.RepositoryCache - return o.run(out, args) - }, - } - - f := cmd.Flags() - f.BoolVarP(&o.regexp, "regexp", "r", false, "use regular expressions for searching repositories you have added") - f.BoolVarP(&o.versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line, for repositories you have added") - f.BoolVar(&o.devel, "devel", false, "use development versions (alpha, beta, and release candidate releases), too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") - f.StringVar(&o.version, "version", "", "search using semantic versioning constraints on repositories you have added") - f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table") - bindOutputFlag(cmd, &o.outputFormat) - - return cmd -} - -func (o *searchRepoOptions) run(out io.Writer, args []string) error { - o.setupSearchedVersion() - - index, err := o.buildIndex(out) - if err != nil { - return err - } - - var res []*search.Result - if len(args) == 0 { - res = index.All() - } else { - q := strings.Join(args, " ") - res, err = index.Search(q, searchMaxScore, o.regexp) - if err != nil { - return err - } - } - - search.SortScore(res) - data, err := o.applyConstraint(res) - if err != nil { - return err - } - - return o.outputFormat.Write(out, &repoSearchWriter{data, o.maxColWidth}) -} - -func (o *searchRepoOptions) setupSearchedVersion() { - debug("Original chart version: %q", o.version) - - if o.version != "" { - return - } - - if o.devel { // search for releases and prereleases (alpha, beta, and release candidate releases). - debug("setting version to >0.0.0-0") - o.version = ">0.0.0-0" - } else { // search only for stable releases, prerelease versions will be skip - debug("setting version to >0.0.0") - o.version = ">0.0.0" - } -} - -func (o *searchRepoOptions) applyConstraint(res []*search.Result) ([]*search.Result, error) { - if len(o.version) == 0 { - return res, nil - } - - constraint, err := semver.NewConstraint(o.version) - if err != nil { - return res, errors.Wrap(err, "an invalid version/constraint format") - } - - data := res[:0] - foundNames := map[string]bool{} - for _, r := range res { - if _, found := foundNames[r.Name]; found { - continue - } - v, err := semver.NewVersion(r.Chart.Version) - if err != nil || constraint.Check(v) { - data = append(data, r) - if !o.versions { - foundNames[r.Name] = true // If user hasn't requested all versions, only show the latest that matches - } - } - } - - return data, nil -} - -func (o *searchRepoOptions) buildIndex(out io.Writer) (*search.Index, error) { - // Load the repositories.yaml - rf, err := repo.LoadFile(o.repoFile) - if isNotExist(err) || len(rf.Repositories) == 0 { - return nil, errors.New("no repositories configured") - } - - i := search.NewIndex() - for _, re := range rf.Repositories { - n := re.Name - f := filepath.Join(o.repoCacheDir, helmpath.CacheIndexFile(n)) - ind, err := repo.LoadIndexFile(f) - if err != nil { - // TODO should print to stderr - fmt.Fprintf(out, "WARNING: Repo %q is corrupt or missing. Try 'helm repo update'.", n) - continue - } - - i.AddRepo(n, ind, o.versions || len(o.version) > 0) - } - return i, nil -} - -type repoChartElement struct { - Name string `json:"name"` - Version string `json:"version"` - AppVersion string `json:"app_version"` - Description string `json:"description"` -} - -type repoSearchWriter struct { - results []*search.Result - columnWidth uint -} - -func (r *repoSearchWriter) WriteTable(out io.Writer) error { - if len(r.results) == 0 { - _, err := out.Write([]byte("No results found\n")) - if err != nil { - return fmt.Errorf("unable to write results: %s", err) - } - return nil - } - table := uitable.New() - table.MaxColWidth = r.columnWidth - table.AddRow("NAME", "CHART VERSION", "APP VERSION", "DESCRIPTION") - for _, r := range r.results { - table.AddRow(r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description) - } - return output.EncodeTable(out, table) -} - -func (r *repoSearchWriter) WriteJSON(out io.Writer) error { - return r.encodeByFormat(out, output.JSON) -} - -func (r *repoSearchWriter) WriteYAML(out io.Writer) error { - return r.encodeByFormat(out, output.YAML) -} - -func (r *repoSearchWriter) encodeByFormat(out io.Writer, format output.Format) error { - // Initialize the array so no results returns an empty array instead of null - chartList := make([]repoChartElement, 0, len(r.results)) - - for _, r := range r.results { - chartList = append(chartList, repoChartElement{r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description}) - } - - switch format { - case output.JSON: - return output.EncodeJSON(out, chartList) - case output.YAML: - return output.EncodeYAML(out, chartList) - } - - // Because this is a non-exported function and only called internally by - // WriteJSON and WriteYAML, we shouldn't get invalid types - return nil -} - -// Provides the list of charts that are part of the specified repo, and that starts with 'prefix'. -func compListChartsOfRepo(repoName string, prefix string) []string { - var charts []string - - path := filepath.Join(settings.RepositoryCache, helmpath.CacheChartsFile(repoName)) - content, err := ioutil.ReadFile(path) - if err == nil { - scanner := bufio.NewScanner(bytes.NewReader(content)) - for scanner.Scan() { - fullName := fmt.Sprintf("%s/%s", repoName, scanner.Text()) - if strings.HasPrefix(fullName, prefix) { - charts = append(charts, fullName) - } - } - return charts - } - - if isNotExist(err) { - // If there is no cached charts file, fallback to the full index file. - // This is much slower but can happen after the caching feature is first - // installed but before the user does a 'helm repo update' to generate the - // first cached charts file. - path = filepath.Join(settings.RepositoryCache, helmpath.CacheIndexFile(repoName)) - if indexFile, err := repo.LoadIndexFile(path); err == nil { - for name := range indexFile.Entries { - fullName := fmt.Sprintf("%s/%s", repoName, name) - if strings.HasPrefix(fullName, prefix) { - charts = append(charts, fullName) - } - } - return charts - } - } - - return []string{} -} - -// Provide dynamic auto-completion for commands that operate on charts (e.g., helm show) -// When true, the includeFiles argument indicates that completion should include local files (e.g., local charts) -func compListCharts(toComplete string, includeFiles bool) ([]string, completion.BashCompDirective) { - completion.CompDebugln(fmt.Sprintf("compListCharts with toComplete %s", toComplete)) - - noSpace := false - noFile := false - var completions []string - - // First check completions for repos - repos := compListRepos("") - for _, repo := range repos { - repoWithSlash := fmt.Sprintf("%s/", repo) - if strings.HasPrefix(toComplete, repoWithSlash) { - // Must complete with charts within the specified repo - completions = append(completions, compListChartsOfRepo(repo, toComplete)...) - noSpace = false - break - } else if strings.HasPrefix(repo, toComplete) { - // Must complete the repo name - completions = append(completions, repoWithSlash) - noSpace = true - } - } - completion.CompDebugln(fmt.Sprintf("Completions after repos: %v", completions)) - - // Now handle completions for url prefixes - for _, url := range []string{"https://", "http://", "file://"} { - if strings.HasPrefix(toComplete, url) { - // The user already put in the full url prefix; we don't have - // anything to add, but make sure the shell does not default - // to file completion since we could be returning an empty array. - noFile = true - noSpace = true - } else if strings.HasPrefix(url, toComplete) { - // We are completing a url prefix - completions = append(completions, url) - noSpace = true - } - } - completion.CompDebugln(fmt.Sprintf("Completions after urls: %v", completions)) - - // Finally, provide file completion if we need to. - // We only do this if: - // 1- There are other completions found (if there are no completions, - // the shell will do file completion itself) - // 2- If there is some input from the user (or else we will end up - // listing the entire content of the current directory which will - // be too many choices for the user to find the real repos) - if includeFiles && len(completions) > 0 && len(toComplete) > 0 { - if files, err := ioutil.ReadDir("."); err == nil { - for _, file := range files { - if strings.HasPrefix(file.Name(), toComplete) { - // We are completing a file prefix - completions = append(completions, file.Name()) - } - } - } - } - completion.CompDebugln(fmt.Sprintf("Completions after files: %v", completions)) - - // If the user didn't provide any input to completion, - // we provide a hint that a path can also be used - if includeFiles && len(toComplete) == 0 { - completions = append(completions, "./", "/") - } - completion.CompDebugln(fmt.Sprintf("Completions after checking empty input: %v", completions)) - - directive := completion.BashCompDirectiveDefault - if noFile { - directive = directive | completion.BashCompDirectiveNoFileComp - } - if noSpace { - directive = directive | completion.BashCompDirectiveNoSpace - // The completion.BashCompDirective flags do not work for zsh right now. - // We handle it ourselves instead. - completions = compEnforceNoSpace(completions) - } - return completions, directive -} - -// This function prevents the shell from adding a space after -// a completion by adding a second, fake completion. -// It is only needed for zsh, but we cannot tell which shell -// is being used here, so we do the fake completion all the time; -// there are no real downsides to doing this for bash as well. -func compEnforceNoSpace(completions []string) []string { - // To prevent the shell from adding space after the completion, - // we trick it by pretending there is a second, longer match. - // We only do this if there is a single choice for completion. - if len(completions) == 1 { - completions = append(completions, completions[0]+".") - completion.CompDebugln(fmt.Sprintf("compEnforceNoSpace: completions now are %v", completions)) - } - return completions -} diff --git a/cmd/helm/search_repo_test.go b/cmd/helm/search_repo_test.go deleted file mode 100644 index 402ef29..0000000 --- a/cmd/helm/search_repo_test.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" -) - -func TestSearchRepositoriesCmd(t *testing.T) { - repoFile := "testdata/helmhome/helm/repositories.yaml" - repoCache := "testdata/helmhome/helm/repository" - - tests := []cmdTestCase{{ - name: "search for 'alpine', expect one match with latest stable version", - cmd: "search repo alpine", - golden: "output/search-multiple-stable-release.txt", - }, { - name: "search for 'alpine', expect one match with newest development version", - cmd: "search repo alpine --devel", - golden: "output/search-multiple-devel-release.txt", - }, { - name: "search for 'alpine' with versions, expect three matches", - cmd: "search repo alpine --versions", - golden: "output/search-multiple-versions.txt", - }, { - name: "search for 'alpine' with version constraint, expect one match with version 0.1.0", - cmd: "search repo alpine --version '>= 0.1, < 0.2'", - golden: "output/search-constraint.txt", - }, { - name: "search for 'alpine' with version constraint, expect one match with version 0.1.0", - cmd: "search repo alpine --versions --version '>= 0.1, < 0.2'", - golden: "output/search-versions-constraint.txt", - }, { - name: "search for 'alpine' with version constraint, expect one match with version 0.2.0", - cmd: "search repo alpine --version '>= 0.1'", - golden: "output/search-constraint-single.txt", - }, { - name: "search for 'alpine' with version constraint and --versions, expect two matches", - cmd: "search repo alpine --versions --version '>= 0.1'", - golden: "output/search-multiple-versions-constraints.txt", - }, { - name: "search for 'syzygy', expect no matches", - cmd: "search repo syzygy", - golden: "output/search-not-found.txt", - }, { - name: "search for 'alp[a-z]+', expect two matches", - cmd: "search repo alp[a-z]+ --regexp", - golden: "output/search-regex.txt", - }, { - name: "search for 'alp[', expect failure to compile regexp", - cmd: "search repo alp[ --regexp", - wantError: true, - }, { - name: "search for 'maria', expect valid json output", - cmd: "search repo maria --output json", - golden: "output/search-output-json.txt", - }, { - name: "search for 'alpine', expect valid yaml output", - cmd: "search repo alpine --output yaml", - golden: "output/search-output-yaml.txt", - }} - - settings.Debug = true - defer func() { settings.Debug = false }() - - for i := range tests { - tests[i].cmd += " --repository-config " + repoFile - tests[i].cmd += " --repository-cache " + repoCache - } - runTestCmd(t, tests) -} - -func TestSearchRepoOutputCompletion(t *testing.T) { - outputFlagCompletionTest(t, "search repo") -} diff --git a/cmd/helm/show.go b/cmd/helm/show.go deleted file mode 100644 index a82ad27..0000000 --- a/cmd/helm/show.go +++ /dev/null @@ -1,163 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" -) - -const showDesc = ` -This command consists of multiple subcommands to display information about a chart -` - -const showAllDesc = ` -This command inspects a chart (directory, file, or URL) and displays all its content -(values.yaml, Charts.yaml, README) -` - -const showValuesDesc = ` -This command inspects a chart (directory, file, or URL) and displays the contents -of the values.yaml file -` - -const showChartDesc = ` -This command inspects a chart (directory, file, or URL) and displays the contents -of the Charts.yaml file -` - -const readmeChartDesc = ` -This command inspects a chart (directory, file, or URL) and displays the contents -of the README file -` - -func newShowCmd(out io.Writer) *cobra.Command { - client := action.NewShow(action.ShowAll) - - showCommand := &cobra.Command{ - Use: "show", - Short: "show information of a chart", - Aliases: []string{"inspect"}, - Long: showDesc, - Args: require.NoArgs, - } - - // Function providing dynamic auto-completion - validArgsFunc := func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListCharts(toComplete, true) - } - - all := &cobra.Command{ - Use: "all [CHART]", - Short: "shows all information of the chart", - Long: showAllDesc, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - client.OutputFormat = action.ShowAll - cp, err := client.ChartPathOptions.LocateChart(args[0], settings) - if err != nil { - return err - } - output, err := client.Run(cp) - if err != nil { - return err - } - fmt.Fprint(out, output) - return nil - }, - } - - valuesSubCmd := &cobra.Command{ - Use: "values [CHART]", - Short: "shows the chart's values", - Long: showValuesDesc, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - client.OutputFormat = action.ShowValues - cp, err := client.ChartPathOptions.LocateChart(args[0], settings) - if err != nil { - return err - } - output, err := client.Run(cp) - if err != nil { - return err - } - fmt.Fprint(out, output) - return nil - }, - } - - chartSubCmd := &cobra.Command{ - Use: "chart [CHART]", - Short: "shows the chart's definition", - Long: showChartDesc, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - client.OutputFormat = action.ShowChart - cp, err := client.ChartPathOptions.LocateChart(args[0], settings) - if err != nil { - return err - } - output, err := client.Run(cp) - if err != nil { - return err - } - fmt.Fprint(out, output) - return nil - }, - } - - readmeSubCmd := &cobra.Command{ - Use: "readme [CHART]", - Short: "shows the chart's README", - Long: readmeChartDesc, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - client.OutputFormat = action.ShowReadme - cp, err := client.ChartPathOptions.LocateChart(args[0], settings) - if err != nil { - return err - } - output, err := client.Run(cp) - if err != nil { - return err - } - fmt.Fprint(out, output) - return nil - }, - } - - cmds := []*cobra.Command{all, readmeSubCmd, valuesSubCmd, chartSubCmd} - for _, subCmd := range cmds { - addChartPathOptionsFlags(subCmd.Flags(), &client.ChartPathOptions) - showCommand.AddCommand(subCmd) - - // Register the completion function for each subcommand - completion.RegisterValidArgsFunc(subCmd, validArgsFunc) - } - - return showCommand -} diff --git a/cmd/helm/status.go b/cmd/helm/status.go deleted file mode 100644 index 34543c6..0000000 --- a/cmd/helm/status.go +++ /dev/null @@ -1,185 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "strings" - "time" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/cli/output" - "helm.sh/helm/v3/pkg/release" -) - -// NOTE: Keep the list of statuses up-to-date with pkg/release/status.go. -var statusHelp = ` -This command shows the status of a named release. -The status consists of: -- last deployment time -- k8s namespace in which the release lives -- state of the release (can be: unknown, deployed, uninstalled, superseded, failed, uninstalling, pending-install, pending-upgrade or pending-rollback) -- list of resources that this release consists of, sorted by kind -- details on last test suite run, if applicable -- additional notes provided by the chart -` - -func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewStatus(cfg) - var outfmt output.Format - - cmd := &cobra.Command{ - Use: "status RELEASE_NAME", - Short: "displays the status of the named release", - Long: statusHelp, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - rel, err := client.Run(args[0]) - if err != nil { - return err - } - - // strip chart metadata from the output - rel.Chart = nil - - return outfmt.Write(out, &statusPrinter{rel, false}) - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.PersistentFlags() - - f.IntVar(&client.Version, "revision", 0, "if set, display the status of the named release with revision") - flag := f.Lookup("revision") - completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) == 1 { - return compListRevisions(cfg, args[0]) - } - return nil, completion.BashCompDirectiveNoFileComp - }) - - bindOutputFlag(cmd, &outfmt) - - return cmd -} - -type statusPrinter struct { - release *release.Release - debug bool -} - -func (s statusPrinter) WriteJSON(out io.Writer) error { - return output.EncodeJSON(out, s.release) -} - -func (s statusPrinter) WriteYAML(out io.Writer) error { - return output.EncodeYAML(out, s.release) -} - -func (s statusPrinter) WriteTable(out io.Writer) error { - if s.release == nil { - return nil - } - fmt.Fprintf(out, "NAME: %s\n", s.release.Name) - if !s.release.Info.LastDeployed.IsZero() { - fmt.Fprintf(out, "LAST DEPLOYED: %s\n", s.release.Info.LastDeployed.Format(time.ANSIC)) - } - fmt.Fprintf(out, "NAMESPACE: %s\n", s.release.Namespace) - fmt.Fprintf(out, "STATUS: %s\n", s.release.Info.Status.String()) - fmt.Fprintf(out, "REVISION: %d\n", s.release.Version) - - executions := executionsByHookEvent(s.release) - if tests, ok := executions[release.HookTest]; !ok || len(tests) == 0 { - fmt.Fprintln(out, "TEST SUITE: None") - } else { - for _, h := range tests { - // Don't print anything if hook has not been initiated - if h.LastRun.StartedAt.IsZero() { - continue - } - fmt.Fprintf(out, "TEST SUITE: %s\n%s\n%s\n%s\n", - h.Name, - fmt.Sprintf("Last Started: %s", h.LastRun.StartedAt.Format(time.ANSIC)), - fmt.Sprintf("Last Completed: %s", h.LastRun.CompletedAt.Format(time.ANSIC)), - fmt.Sprintf("Phase: %s", h.LastRun.Phase), - ) - } - } - - if s.debug { - fmt.Fprintln(out, "USER-SUPPLIED VALUES:") - err := output.EncodeYAML(out, s.release.Config) - if err != nil { - return err - } - // Print an extra newline - fmt.Fprintln(out) - - cfg, err := chartutil.CoalesceValues(s.release.Chart, s.release.Config) - if err != nil { - return err - } - - fmt.Fprintln(out, "COMPUTED VALUES:") - err = output.EncodeYAML(out, cfg.AsMap()) - if err != nil { - return err - } - // Print an extra newline - fmt.Fprintln(out) - } - - if strings.EqualFold(s.release.Info.Description, "Dry run complete") || s.debug { - fmt.Fprintln(out, "HOOKS:") - for _, h := range s.release.Hooks { - fmt.Fprintf(out, "---\n# Source: %s\n%s\n", h.Path, h.Manifest) - } - fmt.Fprintf(out, "MANIFEST:\n%s\n", s.release.Manifest) - } - - if len(s.release.Info.Notes) > 0 { - fmt.Fprintf(out, "NOTES:\n%s\n", strings.TrimSpace(s.release.Info.Notes)) - } - return nil -} - -func executionsByHookEvent(rel *release.Release) map[release.HookEvent][]*release.Hook { - result := make(map[release.HookEvent][]*release.Hook) - for _, h := range rel.Hooks { - for _, e := range h.Events { - executions, ok := result[e] - if !ok { - executions = []*release.Hook{} - } - result[e] = append(executions, h) - } - } - return result -} diff --git a/cmd/helm/status_test.go b/cmd/helm/status_test.go deleted file mode 100644 index 0d2500e..0000000 --- a/cmd/helm/status_test.go +++ /dev/null @@ -1,173 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" - "time" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/release" - helmtime "helm.sh/helm/v3/pkg/time" -) - -func TestStatusCmd(t *testing.T) { - releasesMockWithStatus := func(info *release.Info, hooks ...*release.Hook) []*release.Release { - info.LastDeployed = helmtime.Unix(1452902400, 0).UTC() - return []*release.Release{{ - Name: "flummoxed-chickadee", - Namespace: "default", - Info: info, - Chart: &chart.Chart{}, - Hooks: hooks, - }} - } - - tests := []cmdTestCase{{ - name: "get status of a deployed release", - cmd: "status flummoxed-chickadee", - golden: "output/status.txt", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - }), - }, { - name: "get status of a deployed release with notes", - cmd: "status flummoxed-chickadee", - golden: "output/status-with-notes.txt", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - Notes: "release notes", - }), - }, { - name: "get status of a deployed release with notes in json", - cmd: "status flummoxed-chickadee -o json", - golden: "output/status.json", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - Notes: "release notes", - }), - }, { - name: "get status of a deployed release with test suite", - cmd: "status flummoxed-chickadee", - golden: "output/status-with-test-suite.txt", - rels: releasesMockWithStatus( - &release.Info{ - Status: release.StatusDeployed, - }, - &release.Hook{ - Name: "never-run-test", - Events: []release.HookEvent{release.HookTest}, - }, - &release.Hook{ - Name: "passing-test", - Events: []release.HookEvent{release.HookTest}, - LastRun: release.HookExecution{ - StartedAt: mustParseTime("2006-01-02T15:04:05Z"), - CompletedAt: mustParseTime("2006-01-02T15:04:07Z"), - Phase: release.HookPhaseSucceeded, - }, - }, - &release.Hook{ - Name: "failing-test", - Events: []release.HookEvent{release.HookTest}, - LastRun: release.HookExecution{ - StartedAt: mustParseTime("2006-01-02T15:10:05Z"), - CompletedAt: mustParseTime("2006-01-02T15:10:07Z"), - Phase: release.HookPhaseFailed, - }, - }, - &release.Hook{ - Name: "passing-pre-install", - Events: []release.HookEvent{release.HookPreInstall}, - LastRun: release.HookExecution{ - StartedAt: mustParseTime("2006-01-02T15:00:05Z"), - CompletedAt: mustParseTime("2006-01-02T15:00:07Z"), - Phase: release.HookPhaseSucceeded, - }, - }, - ), - }} - runTestCmd(t, tests) -} - -func mustParseTime(t string) helmtime.Time { - res, _ := helmtime.Parse(time.RFC3339, t) - return res -} - -func TestStatusCompletion(t *testing.T) { - releasesMockWithStatus := func(info *release.Info, hooks ...*release.Hook) []*release.Release { - info.LastDeployed = helmtime.Unix(1452902400, 0).UTC() - return []*release.Release{{ - Name: "athos", - Namespace: "default", - Info: info, - Chart: &chart.Chart{}, - Hooks: hooks, - }, { - Name: "porthos", - Namespace: "default", - Info: info, - Chart: &chart.Chart{}, - Hooks: hooks, - }, { - Name: "aramis", - Namespace: "default", - Info: info, - Chart: &chart.Chart{}, - Hooks: hooks, - }, { - Name: "dartagnan", - Namespace: "gascony", - Info: info, - Chart: &chart.Chart{}, - Hooks: hooks, - }} - } - - tests := []cmdTestCase{{ - name: "completion for status", - cmd: "__complete status a", - golden: "output/status-comp.txt", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - }), - }, { - name: "completion for status with too many arguments", - cmd: "__complete status dartagnan ''", - golden: "output/status-wrong-args-comp.txt", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - }), - }, { - name: "completion for status with too many arguments", - cmd: "__complete status --debug a", - golden: "output/status-comp.txt", - rels: releasesMockWithStatus(&release.Info{ - Status: release.StatusDeployed, - }), - }} - runTestCmd(t, tests) -} - -func TestStatusRevisionCompletion(t *testing.T) { - revisionFlagCompletionTest(t, "status") -} - -func TestStatusOutputCompletion(t *testing.T) { - outputFlagCompletionTest(t, "status") -} diff --git a/cmd/helm/template.go b/cmd/helm/template.go deleted file mode 100644 index dc62c6e..0000000 --- a/cmd/helm/template.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bytes" - "fmt" - "io" - "path/filepath" - "regexp" - "strings" - - "helm.sh/helm/v3/pkg/releaseutil" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/cli/values" -) - -const templateDesc = ` -Render chart templates locally and display the output. - -Any values that would normally be looked up or retrieved in-cluster will be -faked locally. Additionally, none of the server-side testing of chart validity -(e.g. whether an API is supported) is done. -` - -func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - var validate bool - var includeCrds bool - client := action.NewInstall(cfg) - valueOpts := &values.Options{} - var extraAPIs []string - var showFiles []string - - cmd := &cobra.Command{ - Use: "template [NAME] [CHART]", - Short: fmt.Sprintf("locally render templates"), - Long: templateDesc, - Args: require.MinimumNArgs(1), - RunE: func(_ *cobra.Command, args []string) error { - client.DryRun = true - client.ReleaseName = "RELEASE-NAME" - client.Replace = true // Skip the name check - client.ClientOnly = !validate - client.APIVersions = chartutil.VersionSet(extraAPIs) - rel, err := runInstall(args, client, valueOpts, out) - if err != nil { - return err - } - - var manifests bytes.Buffer - - if includeCrds { - for _, f := range rel.Chart.CRDs() { - fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", f.Name, f.Data) - } - } - - fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest)) - - if !client.DisableHooks { - for _, m := range rel.Hooks { - fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest) - } - } - - // if we have a list of files to render, then check that each of the - // provided files exists in the chart. - if len(showFiles) > 0 { - splitManifests := releaseutil.SplitManifests(manifests.String()) - manifestNameRegex := regexp.MustCompile("# Source: [^/]+/(.+)") - var manifestsToRender []string - for _, f := range showFiles { - missing := true - for _, manifest := range splitManifests { - submatch := manifestNameRegex.FindStringSubmatch(manifest) - if len(submatch) == 0 { - continue - } - manifestName := submatch[1] - // manifest.Name is rendered using linux-style filepath separators on Windows as - // well as macOS/linux. - manifestPathSplit := strings.Split(manifestName, "/") - manifestPath := filepath.Join(manifestPathSplit...) - - // if the filepath provided matches a manifest path in the - // chart, render that manifest - if f == manifestPath { - manifestsToRender = append(manifestsToRender, manifest) - missing = false - } - } - if missing { - return fmt.Errorf("could not find template %s in chart", f) - } - } - for _, m := range manifestsToRender { - fmt.Fprintf(out, "---\n%s\n", m) - } - } else { - fmt.Fprintf(out, "%s", manifests.String()) - } - - return nil - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - return compInstall(args, toComplete, client) - }) - - f := cmd.Flags() - addInstallFlags(f, client, valueOpts) - f.StringArrayVarP(&showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates") - f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout") - f.BoolVar(&validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install") - f.BoolVar(&includeCrds, "include-crds", false, "include CRDs in the templated output") - f.BoolVar(&client.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall") - f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions") - - return cmd -} diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go deleted file mode 100644 index dc7987d..0000000 --- a/cmd/helm/template_test.go +++ /dev/null @@ -1,107 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "path/filepath" - "testing" -) - -var chartPath = "./../../pkg/chartutil/testdata/subpop/charts/subchart1" - -func TestTemplateCmd(t *testing.T) { - tests := []cmdTestCase{ - { - name: "check name", - cmd: fmt.Sprintf("template '%s'", chartPath), - golden: "output/template.txt", - }, - { - name: "check set name", - cmd: fmt.Sprintf("template '%s' --set service.name=apache", chartPath), - golden: "output/template-set.txt", - }, - { - name: "check values files", - cmd: fmt.Sprintf("template '%s' --values '%s'", chartPath, filepath.Join(chartPath, "/charts/subchartA/values.yaml")), - golden: "output/template-values-files.txt", - }, - { - name: "check name template", - cmd: fmt.Sprintf(`template '%s' --name-template='foobar-{{ b64enc "abc" }}-baz'`, chartPath), - golden: "output/template-name-template.txt", - }, - { - name: "check no args", - cmd: "template", - wantError: true, - golden: "output/template-no-args.txt", - }, - { - name: "check library chart", - cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/lib-chart"), - wantError: true, - golden: "output/template-lib-chart.txt", - }, - { - name: "check chart bad type", - cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/chart-bad-type"), - wantError: true, - golden: "output/install-chart-bad-type.txt", - }, - { - name: "check chart with dependency which is an app chart acting as a library chart", - cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/chart-with-template-lib-dep"), - golden: "output/template-chart-with-template-lib-dep.txt", - }, - { - name: "check chart with dependency which is an app chart archive acting as a library chart", - cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/chart-with-template-lib-archive-dep"), - golden: "output/template-chart-with-template-lib-archive-dep.txt", - }, - { - name: "check kube api versions", - cmd: fmt.Sprintf("template --api-versions helm.k8s.io/test '%s'", chartPath), - golden: "output/template-with-api-version.txt", - }, - { - name: "template with CRDs", - cmd: fmt.Sprintf("template '%s' --include-crds", chartPath), - golden: "output/template-with-crds.txt", - }, - { - name: "template with show-only one", - cmd: fmt.Sprintf("template '%s' --show-only templates/service.yaml", chartPath), - golden: "output/template-show-only-one.txt", - }, - { - name: "template with show-only multiple", - cmd: fmt.Sprintf("template '%s' --show-only templates/service.yaml --show-only charts/subcharta/templates/service.yaml", chartPath), - golden: "output/template-show-only-multiple.txt", - }, - { - name: "sorted output of manifests (order of filenames, then order of objects within each YAML file)", - cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/object-order"), - golden: "output/object-order.txt", - // Helm previously used random file order. Repeat the test so we - // don't accidentally get the expected result. - repeat: 10, - }, - } - runTestCmd(t, tests) -} diff --git a/cmd/helm/testdata/helm-test-key.pub b/cmd/helm/testdata/helm-test-key.pub deleted file mode 100644 index 38714f2..0000000 Binary files a/cmd/helm/testdata/helm-test-key.pub and /dev/null differ diff --git a/cmd/helm/testdata/helm-test-key.secret b/cmd/helm/testdata/helm-test-key.secret deleted file mode 100644 index a966aef..0000000 Binary files a/cmd/helm/testdata/helm-test-key.secret and /dev/null differ diff --git a/cmd/helm/testdata/helmhome/helm/plugins/args/args.sh b/cmd/helm/testdata/helmhome/helm/plugins/args/args.sh deleted file mode 100755 index 678b4ef..0000000 --- a/cmd/helm/testdata/helmhome/helm/plugins/args/args.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -echo $* diff --git a/cmd/helm/testdata/helmhome/helm/plugins/args/plugin.yaml b/cmd/helm/testdata/helmhome/helm/plugins/args/plugin.yaml deleted file mode 100644 index 21e28a7..0000000 --- a/cmd/helm/testdata/helmhome/helm/plugins/args/plugin.yaml +++ /dev/null @@ -1,4 +0,0 @@ -name: args -usage: "echo args" -description: "This echos args" -command: "$HELM_PLUGIN_DIR/args.sh" diff --git a/cmd/helm/testdata/helmhome/helm/plugins/echo/plugin.yaml b/cmd/helm/testdata/helmhome/helm/plugins/echo/plugin.yaml deleted file mode 100644 index 7b9362a..0000000 --- a/cmd/helm/testdata/helmhome/helm/plugins/echo/plugin.yaml +++ /dev/null @@ -1,4 +0,0 @@ -name: echo -usage: "echo stuff" -description: "This echos stuff" -command: "echo hello" diff --git a/cmd/helm/testdata/helmhome/helm/plugins/env/plugin.yaml b/cmd/helm/testdata/helmhome/helm/plugins/env/plugin.yaml deleted file mode 100644 index 52cb7a8..0000000 --- a/cmd/helm/testdata/helmhome/helm/plugins/env/plugin.yaml +++ /dev/null @@ -1,4 +0,0 @@ -name: env -usage: "env stuff" -description: "show the env" -command: "echo $HELM_PLUGIN_NAME" diff --git a/cmd/helm/testdata/helmhome/helm/plugins/exitwith/exitwith.sh b/cmd/helm/testdata/helmhome/helm/plugins/exitwith/exitwith.sh deleted file mode 100755 index ec84696..0000000 --- a/cmd/helm/testdata/helmhome/helm/plugins/exitwith/exitwith.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exit $* diff --git a/cmd/helm/testdata/helmhome/helm/plugins/exitwith/plugin.yaml b/cmd/helm/testdata/helmhome/helm/plugins/exitwith/plugin.yaml deleted file mode 100644 index 5691d17..0000000 --- a/cmd/helm/testdata/helmhome/helm/plugins/exitwith/plugin.yaml +++ /dev/null @@ -1,4 +0,0 @@ -name: exitwith -usage: "exitwith code" -description: "This exits with the specified exit code" -command: "$HELM_PLUGIN_DIR/exitwith.sh" diff --git a/cmd/helm/testdata/helmhome/helm/plugins/fullenv/fullenv.sh b/cmd/helm/testdata/helmhome/helm/plugins/fullenv/fullenv.sh deleted file mode 100755 index 2efad9b..0000000 --- a/cmd/helm/testdata/helmhome/helm/plugins/fullenv/fullenv.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -echo $HELM_PLUGIN_NAME -echo $HELM_PLUGIN_DIR -echo $HELM_PLUGINS -echo $HELM_REPOSITORY_CONFIG -echo $HELM_REPOSITORY_CACHE -echo $HELM_BIN diff --git a/cmd/helm/testdata/helmhome/helm/plugins/fullenv/plugin.yaml b/cmd/helm/testdata/helmhome/helm/plugins/fullenv/plugin.yaml deleted file mode 100644 index 63f2f12..0000000 --- a/cmd/helm/testdata/helmhome/helm/plugins/fullenv/plugin.yaml +++ /dev/null @@ -1,4 +0,0 @@ -name: fullenv -usage: "show env vars" -description: "show all env vars" -command: "$HELM_PLUGIN_DIR/fullenv.sh" diff --git a/cmd/helm/testdata/helmhome/helm/repositories.yaml b/cmd/helm/testdata/helmhome/helm/repositories.yaml deleted file mode 100644 index 3835aaa..0000000 --- a/cmd/helm/testdata/helmhome/helm/repositories.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -generated: 2016-10-03T16:03:10.640376913-06:00 -repositories: -- cache: testing-index.yaml - name: testing - url: http://example.com/charts diff --git a/cmd/helm/testdata/helmhome/helm/repository/testing-index.yaml b/cmd/helm/testdata/helmhome/helm/repository/testing-index.yaml deleted file mode 100644 index 429388f..0000000 --- a/cmd/helm/testdata/helmhome/helm/repository/testing-index.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: v1 -entries: - alpine: - - name: alpine - url: https://kubernetes-charts.storage.googleapis.com/alpine-0.1.0.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.1.0 - appVersion: 1.2.3 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - - name: alpine - url: https://kubernetes-charts.storage.googleapis.com/alpine-0.2.0.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.2.0 - appVersion: 2.3.4 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - - name: alpine - url: https://kubernetes-charts.storage.googleapis.com/alpine-0.3.0-rc.1.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.3.0-rc.1 - appVersion: 3.0.0 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - mariadb: - - name: mariadb - url: https://kubernetes-charts.storage.googleapis.com/mariadb-0.3.0.tgz - checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56 - home: https://mariadb.org - sources: - - https://github.com/bitnami/bitnami-docker-mariadb - version: 0.3.0 - description: Chart for MariaDB - keywords: - - mariadb - - mysql - - database - - sql - maintainers: - - name: Bitnami - email: containers@bitnami.com - icon: "" diff --git a/cmd/helm/testdata/output/dependency-list-archive.txt b/cmd/helm/testdata/output/dependency-list-archive.txt deleted file mode 100644 index a0fc13c..0000000 --- a/cmd/helm/testdata/output/dependency-list-archive.txt +++ /dev/null @@ -1,5 +0,0 @@ -NAME VERSION REPOSITORY STATUS -reqsubchart 0.1.0 https://example.com/charts missing -reqsubchart2 0.2.0 https://example.com/charts missing -reqsubchart3 >=0.1.0 https://example.com/charts missing - diff --git a/cmd/helm/testdata/output/dependency-list-no-chart-linux.txt b/cmd/helm/testdata/output/dependency-list-no-chart-linux.txt deleted file mode 100644 index 8fab8f8..0000000 --- a/cmd/helm/testdata/output/dependency-list-no-chart-linux.txt +++ /dev/null @@ -1 +0,0 @@ -Error: stat /no/such/chart: no such file or directory diff --git a/cmd/helm/testdata/output/dependency-list-no-requirements-linux.txt b/cmd/helm/testdata/output/dependency-list-no-requirements-linux.txt deleted file mode 100644 index 35fe1d2..0000000 --- a/cmd/helm/testdata/output/dependency-list-no-requirements-linux.txt +++ /dev/null @@ -1 +0,0 @@ -WARNING: no dependencies at testdata/testcharts/alpine/charts diff --git a/cmd/helm/testdata/output/dependency-list.txt b/cmd/helm/testdata/output/dependency-list.txt deleted file mode 100644 index b57c21a..0000000 --- a/cmd/helm/testdata/output/dependency-list.txt +++ /dev/null @@ -1,5 +0,0 @@ -NAME VERSION REPOSITORY STATUS -reqsubchart 0.1.0 https://example.com/charts unpacked -reqsubchart2 0.2.0 https://example.com/charts unpacked -reqsubchart3 >=0.1.0 https://example.com/charts ok - diff --git a/cmd/helm/testdata/output/deprecated-chart.txt b/cmd/helm/testdata/output/deprecated-chart.txt deleted file mode 100644 index e5be2c3..0000000 --- a/cmd/helm/testdata/output/deprecated-chart.txt +++ /dev/null @@ -1,7 +0,0 @@ -WARNING: This chart is deprecated -NAME: aeneas -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/get-all-no-args.txt b/cmd/helm/testdata/output/get-all-no-args.txt deleted file mode 100644 index cc3fc2a..0000000 --- a/cmd/helm/testdata/output/get-all-no-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm get all" requires 1 argument - -Usage: helm get all RELEASE_NAME [flags] diff --git a/cmd/helm/testdata/output/get-hooks-no-args.txt b/cmd/helm/testdata/output/get-hooks-no-args.txt deleted file mode 100644 index 2911fdb..0000000 --- a/cmd/helm/testdata/output/get-hooks-no-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm get hooks" requires 1 argument - -Usage: helm get hooks RELEASE_NAME [flags] diff --git a/cmd/helm/testdata/output/get-hooks.txt b/cmd/helm/testdata/output/get-hooks.txt deleted file mode 100644 index 81e87b1..0000000 --- a/cmd/helm/testdata/output/get-hooks.txt +++ /dev/null @@ -1,8 +0,0 @@ ---- -# Source: pre-install-hook.yaml -apiVersion: v1 -kind: Job -metadata: - annotations: - "helm.sh/hook": pre-install - diff --git a/cmd/helm/testdata/output/get-manifest-no-args.txt b/cmd/helm/testdata/output/get-manifest-no-args.txt deleted file mode 100644 index df7aa5b..0000000 --- a/cmd/helm/testdata/output/get-manifest-no-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm get manifest" requires 1 argument - -Usage: helm get manifest RELEASE_NAME [flags] diff --git a/cmd/helm/testdata/output/get-manifest.txt b/cmd/helm/testdata/output/get-manifest.txt deleted file mode 100644 index 88937e0..0000000 --- a/cmd/helm/testdata/output/get-manifest.txt +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: fixture - diff --git a/cmd/helm/testdata/output/get-notes-no-args.txt b/cmd/helm/testdata/output/get-notes-no-args.txt deleted file mode 100644 index 1a0c20c..0000000 --- a/cmd/helm/testdata/output/get-notes-no-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm get notes" requires 1 argument - -Usage: helm get notes RELEASE_NAME [flags] diff --git a/cmd/helm/testdata/output/get-notes.txt b/cmd/helm/testdata/output/get-notes.txt deleted file mode 100644 index e710c78..0000000 --- a/cmd/helm/testdata/output/get-notes.txt +++ /dev/null @@ -1,2 +0,0 @@ -NOTES: -Some mock release notes! diff --git a/cmd/helm/testdata/output/get-release-template.txt b/cmd/helm/testdata/output/get-release-template.txt deleted file mode 100644 index 02d44fb..0000000 --- a/cmd/helm/testdata/output/get-release-template.txt +++ /dev/null @@ -1 +0,0 @@ -0.1.0-beta.1 \ No newline at end of file diff --git a/cmd/helm/testdata/output/get-release.txt b/cmd/helm/testdata/output/get-release.txt deleted file mode 100644 index f6c3b57..0000000 --- a/cmd/helm/testdata/output/get-release.txt +++ /dev/null @@ -1,29 +0,0 @@ -NAME: thomas-guide -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None -USER-SUPPLIED VALUES: -name: value - -COMPUTED VALUES: -name: value - -HOOKS: ---- -# Source: pre-install-hook.yaml -apiVersion: v1 -kind: Job -metadata: - annotations: - "helm.sh/hook": pre-install - -MANIFEST: -apiVersion: v1 -kind: Secret -metadata: - name: fixture - -NOTES: -Some mock release notes! diff --git a/cmd/helm/testdata/output/get-values-all.txt b/cmd/helm/testdata/output/get-values-all.txt deleted file mode 100644 index b7e9696..0000000 --- a/cmd/helm/testdata/output/get-values-all.txt +++ /dev/null @@ -1,2 +0,0 @@ -COMPUTED VALUES: -name: value diff --git a/cmd/helm/testdata/output/get-values-args.txt b/cmd/helm/testdata/output/get-values-args.txt deleted file mode 100644 index c8a65e7..0000000 --- a/cmd/helm/testdata/output/get-values-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm get values" requires 1 argument - -Usage: helm get values RELEASE_NAME [flags] diff --git a/cmd/helm/testdata/output/get-values.txt b/cmd/helm/testdata/output/get-values.txt deleted file mode 100644 index b7d146b..0000000 --- a/cmd/helm/testdata/output/get-values.txt +++ /dev/null @@ -1,2 +0,0 @@ -USER-SUPPLIED VALUES: -name: value diff --git a/cmd/helm/testdata/output/history-limit.txt b/cmd/helm/testdata/output/history-limit.txt deleted file mode 100644 index aee0fad..0000000 --- a/cmd/helm/testdata/output/history-limit.txt +++ /dev/null @@ -1,3 +0,0 @@ -REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION -3 Fri Sep 2 22:04:05 1977 superseded foo-0.1.0-beta.1 1.0 Release mock -4 Fri Sep 2 22:04:05 1977 deployed foo-0.1.0-beta.1 1.0 Release mock diff --git a/cmd/helm/testdata/output/history.json b/cmd/helm/testdata/output/history.json deleted file mode 100644 index 35311d3..0000000 --- a/cmd/helm/testdata/output/history.json +++ /dev/null @@ -1 +0,0 @@ -[{"revision":3,"updated":"1977-09-02T22:04:05Z","status":"superseded","chart":"foo-0.1.0-beta.1","app_version":"1.0","description":"Release mock"},{"revision":4,"updated":"1977-09-02T22:04:05Z","status":"deployed","chart":"foo-0.1.0-beta.1","app_version":"1.0","description":"Release mock"}] diff --git a/cmd/helm/testdata/output/history.txt b/cmd/helm/testdata/output/history.txt deleted file mode 100644 index 2a5d69c..0000000 --- a/cmd/helm/testdata/output/history.txt +++ /dev/null @@ -1,5 +0,0 @@ -REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION -1 Fri Sep 2 22:04:05 1977 superseded foo-0.1.0-beta.1 1.0 Release mock -2 Fri Sep 2 22:04:05 1977 superseded foo-0.1.0-beta.1 1.0 Release mock -3 Fri Sep 2 22:04:05 1977 superseded foo-0.1.0-beta.1 1.0 Release mock -4 Fri Sep 2 22:04:05 1977 deployed foo-0.1.0-beta.1 1.0 Release mock diff --git a/cmd/helm/testdata/output/history.yaml b/cmd/helm/testdata/output/history.yaml deleted file mode 100644 index b7ae03b..0000000 --- a/cmd/helm/testdata/output/history.yaml +++ /dev/null @@ -1,12 +0,0 @@ -- app_version: "1.0" - chart: foo-0.1.0-beta.1 - description: Release mock - revision: 3 - status: superseded - updated: "1977-09-02T22:04:05Z" -- app_version: "1.0" - chart: foo-0.1.0-beta.1 - description: Release mock - revision: 4 - status: deployed - updated: "1977-09-02T22:04:05Z" diff --git a/cmd/helm/testdata/output/install-and-replace.txt b/cmd/helm/testdata/output/install-and-replace.txt deleted file mode 100644 index 039d6ae..0000000 --- a/cmd/helm/testdata/output/install-and-replace.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: aeneas -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/install-chart-bad-type.txt b/cmd/helm/testdata/output/install-chart-bad-type.txt deleted file mode 100644 index d8a3bf2..0000000 --- a/cmd/helm/testdata/output/install-chart-bad-type.txt +++ /dev/null @@ -1 +0,0 @@ -Error: validation: chart.metadata.type must be application or library diff --git a/cmd/helm/testdata/output/install-name-template.txt b/cmd/helm/testdata/output/install-name-template.txt deleted file mode 100644 index 67e06d9..0000000 --- a/cmd/helm/testdata/output/install-name-template.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: FOOBAR -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/install-no-args.txt b/cmd/helm/testdata/output/install-no-args.txt deleted file mode 100644 index 47f010a..0000000 --- a/cmd/helm/testdata/output/install-no-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm install" requires at least 1 argument - -Usage: helm install [NAME] [CHART] [flags] diff --git a/cmd/helm/testdata/output/install-no-hooks.txt b/cmd/helm/testdata/output/install-no-hooks.txt deleted file mode 100644 index 039d6ae..0000000 --- a/cmd/helm/testdata/output/install-no-hooks.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: aeneas -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/install-with-multiple-values-files.txt b/cmd/helm/testdata/output/install-with-multiple-values-files.txt deleted file mode 100644 index 406e522..0000000 --- a/cmd/helm/testdata/output/install-with-multiple-values-files.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: virgil -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/install-with-multiple-values.txt b/cmd/helm/testdata/output/install-with-multiple-values.txt deleted file mode 100644 index 406e522..0000000 --- a/cmd/helm/testdata/output/install-with-multiple-values.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: virgil -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/install-with-timeout.txt b/cmd/helm/testdata/output/install-with-timeout.txt deleted file mode 100644 index 19952e3..0000000 --- a/cmd/helm/testdata/output/install-with-timeout.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: foobar -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/install-with-values-file.txt b/cmd/helm/testdata/output/install-with-values-file.txt deleted file mode 100644 index 406e522..0000000 --- a/cmd/helm/testdata/output/install-with-values-file.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: virgil -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/install-with-values.txt b/cmd/helm/testdata/output/install-with-values.txt deleted file mode 100644 index 406e522..0000000 --- a/cmd/helm/testdata/output/install-with-values.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: virgil -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/install-with-wait.txt b/cmd/helm/testdata/output/install-with-wait.txt deleted file mode 100644 index 7ce22d4..0000000 --- a/cmd/helm/testdata/output/install-with-wait.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: apollo -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/install.txt b/cmd/helm/testdata/output/install.txt deleted file mode 100644 index 039d6ae..0000000 --- a/cmd/helm/testdata/output/install.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: aeneas -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/list-all.txt b/cmd/helm/testdata/output/list-all.txt deleted file mode 100644 index ef6d44c..0000000 --- a/cmd/helm/testdata/output/list-all.txt +++ /dev/null @@ -1,9 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -drax default 1 2016-01-16 00:00:01 +0000 UTC uninstalling chickadee-1.0.0 0.0.1 -gamora default 1 2016-01-16 00:00:01 +0000 UTC superseded chickadee-1.0.0 0.0.1 -groot default 1 2016-01-16 00:00:01 +0000 UTC uninstalled chickadee-1.0.0 0.0.1 -hummingbird default 1 2016-01-16 00:00:03 +0000 UTC deployed chickadee-1.0.0 0.0.1 -iguana default 2 2016-01-16 00:00:04 +0000 UTC deployed chickadee-1.0.0 0.0.1 -rocket default 1 2016-01-16 00:00:02 +0000 UTC failed chickadee-1.0.0 0.0.1 -starlord default 2 2016-01-16 00:00:01 +0000 UTC deployed chickadee-1.0.0 0.0.1 -thanos default 1 2016-01-16 00:00:01 +0000 UTC pending-install chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-date-reversed.txt b/cmd/helm/testdata/output/list-date-reversed.txt deleted file mode 100644 index 8b4e71a..0000000 --- a/cmd/helm/testdata/output/list-date-reversed.txt +++ /dev/null @@ -1,5 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -iguana default 2 2016-01-16 00:00:04 +0000 UTC deployed chickadee-1.0.0 0.0.1 -hummingbird default 1 2016-01-16 00:00:03 +0000 UTC deployed chickadee-1.0.0 0.0.1 -rocket default 1 2016-01-16 00:00:02 +0000 UTC failed chickadee-1.0.0 0.0.1 -starlord default 2 2016-01-16 00:00:01 +0000 UTC deployed chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-date.txt b/cmd/helm/testdata/output/list-date.txt deleted file mode 100644 index 3d2b27a..0000000 --- a/cmd/helm/testdata/output/list-date.txt +++ /dev/null @@ -1,5 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -starlord default 2 2016-01-16 00:00:01 +0000 UTC deployed chickadee-1.0.0 0.0.1 -rocket default 1 2016-01-16 00:00:02 +0000 UTC failed chickadee-1.0.0 0.0.1 -hummingbird default 1 2016-01-16 00:00:03 +0000 UTC deployed chickadee-1.0.0 0.0.1 -iguana default 2 2016-01-16 00:00:04 +0000 UTC deployed chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-failed.txt b/cmd/helm/testdata/output/list-failed.txt deleted file mode 100644 index a8ec3e1..0000000 --- a/cmd/helm/testdata/output/list-failed.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -rocket default 1 2016-01-16 00:00:02 +0000 UTC failed chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-filter.txt b/cmd/helm/testdata/output/list-filter.txt deleted file mode 100644 index 0a82092..0000000 --- a/cmd/helm/testdata/output/list-filter.txt +++ /dev/null @@ -1,5 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -hummingbird default 1 2016-01-16 00:00:03 +0000 UTC deployed chickadee-1.0.0 0.0.1 -iguana default 2 2016-01-16 00:00:04 +0000 UTC deployed chickadee-1.0.0 0.0.1 -rocket default 1 2016-01-16 00:00:02 +0000 UTC failed chickadee-1.0.0 0.0.1 -starlord default 2 2016-01-16 00:00:01 +0000 UTC deployed chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-max.txt b/cmd/helm/testdata/output/list-max.txt deleted file mode 100644 index a909322..0000000 --- a/cmd/helm/testdata/output/list-max.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -hummingbird default 1 2016-01-16 00:00:03 +0000 UTC deployed chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-offset.txt b/cmd/helm/testdata/output/list-offset.txt deleted file mode 100644 index 36e963c..0000000 --- a/cmd/helm/testdata/output/list-offset.txt +++ /dev/null @@ -1,4 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -iguana default 2 2016-01-16 00:00:04 +0000 UTC deployed chickadee-1.0.0 0.0.1 -rocket default 1 2016-01-16 00:00:02 +0000 UTC failed chickadee-1.0.0 0.0.1 -starlord default 2 2016-01-16 00:00:01 +0000 UTC deployed chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-pending.txt b/cmd/helm/testdata/output/list-pending.txt deleted file mode 100644 index f3d7aa0..0000000 --- a/cmd/helm/testdata/output/list-pending.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -thanos default 1 2016-01-16 00:00:01 +0000 UTC pending-install chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-reverse.txt b/cmd/helm/testdata/output/list-reverse.txt deleted file mode 100644 index da178b2..0000000 --- a/cmd/helm/testdata/output/list-reverse.txt +++ /dev/null @@ -1,5 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -starlord default 2 2016-01-16 00:00:01 +0000 UTC deployed chickadee-1.0.0 0.0.1 -rocket default 1 2016-01-16 00:00:02 +0000 UTC failed chickadee-1.0.0 0.0.1 -iguana default 2 2016-01-16 00:00:04 +0000 UTC deployed chickadee-1.0.0 0.0.1 -hummingbird default 1 2016-01-16 00:00:03 +0000 UTC deployed chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-short.txt b/cmd/helm/testdata/output/list-short.txt deleted file mode 100644 index 0a63be9..0000000 --- a/cmd/helm/testdata/output/list-short.txt +++ /dev/null @@ -1,4 +0,0 @@ -hummingbird -iguana -rocket -starlord diff --git a/cmd/helm/testdata/output/list-superseded.txt b/cmd/helm/testdata/output/list-superseded.txt deleted file mode 100644 index 50b4358..0000000 --- a/cmd/helm/testdata/output/list-superseded.txt +++ /dev/null @@ -1,3 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -gamora default 1 2016-01-16 00:00:01 +0000 UTC superseded chickadee-1.0.0 0.0.1 -starlord default 1 2016-01-16 00:00:01 +0000 UTC superseded chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-uninstalled.txt b/cmd/helm/testdata/output/list-uninstalled.txt deleted file mode 100644 index 430cf32..0000000 --- a/cmd/helm/testdata/output/list-uninstalled.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -groot default 1 2016-01-16 00:00:01 +0000 UTC uninstalled chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list-uninstalling.txt b/cmd/helm/testdata/output/list-uninstalling.txt deleted file mode 100644 index 9228963..0000000 --- a/cmd/helm/testdata/output/list-uninstalling.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -drax default 1 2016-01-16 00:00:01 +0000 UTC uninstalling chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/list.txt b/cmd/helm/testdata/output/list.txt deleted file mode 100644 index 0a82092..0000000 --- a/cmd/helm/testdata/output/list.txt +++ /dev/null @@ -1,5 +0,0 @@ -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -hummingbird default 1 2016-01-16 00:00:03 +0000 UTC deployed chickadee-1.0.0 0.0.1 -iguana default 2 2016-01-16 00:00:04 +0000 UTC deployed chickadee-1.0.0 0.0.1 -rocket default 1 2016-01-16 00:00:02 +0000 UTC failed chickadee-1.0.0 0.0.1 -starlord default 2 2016-01-16 00:00:01 +0000 UTC deployed chickadee-1.0.0 0.0.1 diff --git a/cmd/helm/testdata/output/object-order.txt b/cmd/helm/testdata/output/object-order.txt deleted file mode 100644 index 307f928..0000000 --- a/cmd/helm/testdata/output/object-order.txt +++ /dev/null @@ -1,191 +0,0 @@ ---- -# Source: object-order/templates/01-a.yml -# 1 -kind: NetworkPolicy -apiVersion: networking.k8s.io/v1 -metadata: - name: first -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/01-a.yml -# 2 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: second -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/01-a.yml -# 3 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: third -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 5 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: fifth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 7 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: seventh -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 8 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: eighth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 9 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: ninth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 10 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: tenth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 11 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: eleventh -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 12 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: twelfth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 13 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: thirteenth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 14 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: fourteenth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/02-b.yml -# 15 (11th object within 02-b.yml, in order to test `SplitManifests` which assigns `manifest-10` -# to this object which should then come *after* `manifest-9`) -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: fifteenth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress ---- -# Source: object-order/templates/01-a.yml -# 4 (Deployment should come after all NetworkPolicy manifests, since 'helm template' outputs in install order) -apiVersion: apps/v1 -kind: Deployment -metadata: - name: fourth -spec: - selector: - matchLabels: - pod: fourth - replicas: 1 - template: - metadata: - labels: - pod: fourth - spec: - containers: - - name: hello-world - image: gcr.io/google-samples/node-hello:1.0 ---- -# Source: object-order/templates/02-b.yml -# 6 (implementation detail: currently, 'helm template' outputs hook manifests last; and yes, NetworkPolicy won't make a reasonable hook, this is just a dummy unit test manifest) -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - annotations: - "helm.sh/hook": pre-install - name: sixth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress diff --git a/cmd/helm/testdata/output/output-comp.txt b/cmd/helm/testdata/output/output-comp.txt deleted file mode 100644 index de5f16f..0000000 --- a/cmd/helm/testdata/output/output-comp.txt +++ /dev/null @@ -1,4 +0,0 @@ -table -json -yaml -:0 diff --git a/cmd/helm/testdata/output/repo-add.txt b/cmd/helm/testdata/output/repo-add.txt deleted file mode 100644 index e888232..0000000 --- a/cmd/helm/testdata/output/repo-add.txt +++ /dev/null @@ -1 +0,0 @@ -"test-name" has been added to your repositories diff --git a/cmd/helm/testdata/output/revision-comp.txt b/cmd/helm/testdata/output/revision-comp.txt deleted file mode 100644 index b4799f0..0000000 --- a/cmd/helm/testdata/output/revision-comp.txt +++ /dev/null @@ -1,5 +0,0 @@ -8 -9 -10 -11 -:0 diff --git a/cmd/helm/testdata/output/revision-wrong-args-comp.txt b/cmd/helm/testdata/output/revision-wrong-args-comp.txt deleted file mode 100644 index b6f8671..0000000 --- a/cmd/helm/testdata/output/revision-wrong-args-comp.txt +++ /dev/null @@ -1 +0,0 @@ -:4 diff --git a/cmd/helm/testdata/output/rollback-no-args.txt b/cmd/helm/testdata/output/rollback-no-args.txt deleted file mode 100644 index a1bc30b..0000000 --- a/cmd/helm/testdata/output/rollback-no-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm rollback" requires at least 1 argument - -Usage: helm rollback [REVISION] [flags] diff --git a/cmd/helm/testdata/output/rollback-no-revision.txt b/cmd/helm/testdata/output/rollback-no-revision.txt deleted file mode 100644 index ae3c6f1..0000000 --- a/cmd/helm/testdata/output/rollback-no-revision.txt +++ /dev/null @@ -1 +0,0 @@ -Rollback was a success! Happy Helming! diff --git a/cmd/helm/testdata/output/rollback-timeout.txt b/cmd/helm/testdata/output/rollback-timeout.txt deleted file mode 100644 index ae3c6f1..0000000 --- a/cmd/helm/testdata/output/rollback-timeout.txt +++ /dev/null @@ -1 +0,0 @@ -Rollback was a success! Happy Helming! diff --git a/cmd/helm/testdata/output/rollback-wait.txt b/cmd/helm/testdata/output/rollback-wait.txt deleted file mode 100644 index ae3c6f1..0000000 --- a/cmd/helm/testdata/output/rollback-wait.txt +++ /dev/null @@ -1 +0,0 @@ -Rollback was a success! Happy Helming! diff --git a/cmd/helm/testdata/output/rollback.txt b/cmd/helm/testdata/output/rollback.txt deleted file mode 100644 index ae3c6f1..0000000 --- a/cmd/helm/testdata/output/rollback.txt +++ /dev/null @@ -1 +0,0 @@ -Rollback was a success! Happy Helming! diff --git a/cmd/helm/testdata/output/schema-negative-cli.txt b/cmd/helm/testdata/output/schema-negative-cli.txt deleted file mode 100644 index 26bc92b..0000000 --- a/cmd/helm/testdata/output/schema-negative-cli.txt +++ /dev/null @@ -1,4 +0,0 @@ -Error: values don't meet the specifications of the schema(s) in the following chart(s): -empty: -- age: Must be greater than or equal to 0/1 - diff --git a/cmd/helm/testdata/output/schema-negative.txt b/cmd/helm/testdata/output/schema-negative.txt deleted file mode 100644 index 2ea97b7..0000000 --- a/cmd/helm/testdata/output/schema-negative.txt +++ /dev/null @@ -1,5 +0,0 @@ -Error: values don't meet the specifications of the schema(s) in the following chart(s): -empty: -- (root): employmentInfo is required -- age: Must be greater than or equal to 0/1 - diff --git a/cmd/helm/testdata/output/schema.txt b/cmd/helm/testdata/output/schema.txt deleted file mode 100644 index 22a94b3..0000000 --- a/cmd/helm/testdata/output/schema.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: schema -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/search-constraint-single.txt b/cmd/helm/testdata/output/search-constraint-single.txt deleted file mode 100644 index a1f7509..0000000 --- a/cmd/helm/testdata/output/search-constraint-single.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME CHART VERSION APP VERSION DESCRIPTION -testing/alpine 0.2.0 2.3.4 Deploy a basic Alpine Linux pod diff --git a/cmd/helm/testdata/output/search-constraint.txt b/cmd/helm/testdata/output/search-constraint.txt deleted file mode 100644 index 9fb22fe..0000000 --- a/cmd/helm/testdata/output/search-constraint.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME CHART VERSION APP VERSION DESCRIPTION -testing/alpine 0.1.0 1.2.3 Deploy a basic Alpine Linux pod diff --git a/cmd/helm/testdata/output/search-multiple-devel-release.txt b/cmd/helm/testdata/output/search-multiple-devel-release.txt deleted file mode 100644 index 7e29a8f..0000000 --- a/cmd/helm/testdata/output/search-multiple-devel-release.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME CHART VERSION APP VERSION DESCRIPTION -testing/alpine 0.3.0-rc.1 3.0.0 Deploy a basic Alpine Linux pod diff --git a/cmd/helm/testdata/output/search-multiple-stable-release.txt b/cmd/helm/testdata/output/search-multiple-stable-release.txt deleted file mode 100644 index a1f7509..0000000 --- a/cmd/helm/testdata/output/search-multiple-stable-release.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME CHART VERSION APP VERSION DESCRIPTION -testing/alpine 0.2.0 2.3.4 Deploy a basic Alpine Linux pod diff --git a/cmd/helm/testdata/output/search-multiple-versions-constraints.txt b/cmd/helm/testdata/output/search-multiple-versions-constraints.txt deleted file mode 100644 index a6a3888..0000000 --- a/cmd/helm/testdata/output/search-multiple-versions-constraints.txt +++ /dev/null @@ -1,3 +0,0 @@ -NAME CHART VERSION APP VERSION DESCRIPTION -testing/alpine 0.2.0 2.3.4 Deploy a basic Alpine Linux pod -testing/alpine 0.1.0 1.2.3 Deploy a basic Alpine Linux pod diff --git a/cmd/helm/testdata/output/search-multiple-versions.txt b/cmd/helm/testdata/output/search-multiple-versions.txt deleted file mode 100644 index a6a3888..0000000 --- a/cmd/helm/testdata/output/search-multiple-versions.txt +++ /dev/null @@ -1,3 +0,0 @@ -NAME CHART VERSION APP VERSION DESCRIPTION -testing/alpine 0.2.0 2.3.4 Deploy a basic Alpine Linux pod -testing/alpine 0.1.0 1.2.3 Deploy a basic Alpine Linux pod diff --git a/cmd/helm/testdata/output/search-not-found.txt b/cmd/helm/testdata/output/search-not-found.txt deleted file mode 100644 index 4f2a9fd..0000000 --- a/cmd/helm/testdata/output/search-not-found.txt +++ /dev/null @@ -1 +0,0 @@ -No results found diff --git a/cmd/helm/testdata/output/search-output-json.txt b/cmd/helm/testdata/output/search-output-json.txt deleted file mode 100644 index 9b211e1..0000000 --- a/cmd/helm/testdata/output/search-output-json.txt +++ /dev/null @@ -1 +0,0 @@ -[{"name":"testing/mariadb","version":"0.3.0","app_version":"","description":"Chart for MariaDB"}] diff --git a/cmd/helm/testdata/output/search-output-yaml.txt b/cmd/helm/testdata/output/search-output-yaml.txt deleted file mode 100644 index 122b7f3..0000000 --- a/cmd/helm/testdata/output/search-output-yaml.txt +++ /dev/null @@ -1,4 +0,0 @@ -- app_version: 2.3.4 - description: Deploy a basic Alpine Linux pod - name: testing/alpine - version: 0.2.0 diff --git a/cmd/helm/testdata/output/search-regex.txt b/cmd/helm/testdata/output/search-regex.txt deleted file mode 100644 index a1f7509..0000000 --- a/cmd/helm/testdata/output/search-regex.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME CHART VERSION APP VERSION DESCRIPTION -testing/alpine 0.2.0 2.3.4 Deploy a basic Alpine Linux pod diff --git a/cmd/helm/testdata/output/search-versions-constraint.txt b/cmd/helm/testdata/output/search-versions-constraint.txt deleted file mode 100644 index 9fb22fe..0000000 --- a/cmd/helm/testdata/output/search-versions-constraint.txt +++ /dev/null @@ -1,2 +0,0 @@ -NAME CHART VERSION APP VERSION DESCRIPTION -testing/alpine 0.1.0 1.2.3 Deploy a basic Alpine Linux pod diff --git a/cmd/helm/testdata/output/status-comp.txt b/cmd/helm/testdata/output/status-comp.txt deleted file mode 100644 index c978829..0000000 --- a/cmd/helm/testdata/output/status-comp.txt +++ /dev/null @@ -1,3 +0,0 @@ -aramis -athos -:4 diff --git a/cmd/helm/testdata/output/status-with-notes.txt b/cmd/helm/testdata/output/status-with-notes.txt deleted file mode 100644 index e992ce9..0000000 --- a/cmd/helm/testdata/output/status-with-notes.txt +++ /dev/null @@ -1,8 +0,0 @@ -NAME: flummoxed-chickadee -LAST DEPLOYED: Sat Jan 16 00:00:00 2016 -NAMESPACE: default -STATUS: deployed -REVISION: 0 -TEST SUITE: None -NOTES: -release notes diff --git a/cmd/helm/testdata/output/status-with-test-suite.txt b/cmd/helm/testdata/output/status-with-test-suite.txt deleted file mode 100644 index 58c67e1..0000000 --- a/cmd/helm/testdata/output/status-with-test-suite.txt +++ /dev/null @@ -1,13 +0,0 @@ -NAME: flummoxed-chickadee -LAST DEPLOYED: Sat Jan 16 00:00:00 2016 -NAMESPACE: default -STATUS: deployed -REVISION: 0 -TEST SUITE: passing-test -Last Started: Mon Jan 2 15:04:05 2006 -Last Completed: Mon Jan 2 15:04:07 2006 -Phase: Succeeded -TEST SUITE: failing-test -Last Started: Mon Jan 2 15:10:05 2006 -Last Completed: Mon Jan 2 15:10:07 2006 -Phase: Failed diff --git a/cmd/helm/testdata/output/status-wrong-args-comp.txt b/cmd/helm/testdata/output/status-wrong-args-comp.txt deleted file mode 100644 index b6f8671..0000000 --- a/cmd/helm/testdata/output/status-wrong-args-comp.txt +++ /dev/null @@ -1 +0,0 @@ -:4 diff --git a/cmd/helm/testdata/output/status.json b/cmd/helm/testdata/output/status.json deleted file mode 100644 index 4b499c9..0000000 --- a/cmd/helm/testdata/output/status.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"flummoxed-chickadee","info":{"first_deployed":"","last_deployed":"2016-01-16T00:00:00Z","deleted":"","status":"deployed","notes":"release notes"},"namespace":"default"} diff --git a/cmd/helm/testdata/output/status.txt b/cmd/helm/testdata/output/status.txt deleted file mode 100644 index a326c3d..0000000 --- a/cmd/helm/testdata/output/status.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: flummoxed-chickadee -LAST DEPLOYED: Sat Jan 16 00:00:00 2016 -NAMESPACE: default -STATUS: deployed -REVISION: 0 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/subchart-schema-cli-negative.txt b/cmd/helm/testdata/output/subchart-schema-cli-negative.txt deleted file mode 100644 index 86f6e87..0000000 --- a/cmd/helm/testdata/output/subchart-schema-cli-negative.txt +++ /dev/null @@ -1,4 +0,0 @@ -Error: values don't meet the specifications of the schema(s) in the following chart(s): -subchart-with-schema: -- age: Must be greater than or equal to 0/1 - diff --git a/cmd/helm/testdata/output/subchart-schema-cli.txt b/cmd/helm/testdata/output/subchart-schema-cli.txt deleted file mode 100644 index 22a94b3..0000000 --- a/cmd/helm/testdata/output/subchart-schema-cli.txt +++ /dev/null @@ -1,6 +0,0 @@ -NAME: schema -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 1 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/subchart-schema-negative.txt b/cmd/helm/testdata/output/subchart-schema-negative.txt deleted file mode 100644 index 5a84170..0000000 --- a/cmd/helm/testdata/output/subchart-schema-negative.txt +++ /dev/null @@ -1,6 +0,0 @@ -Error: values don't meet the specifications of the schema(s) in the following chart(s): -chart-without-schema: -- (root): lastname is required -subchart-with-schema: -- (root): age is required - diff --git a/cmd/helm/testdata/output/template-chart-with-template-lib-archive-dep.txt b/cmd/helm/testdata/output/template-chart-with-template-lib-archive-dep.txt deleted file mode 100644 index dc1aa29..0000000 --- a/cmd/helm/testdata/output/template-chart-with-template-lib-archive-dep.txt +++ /dev/null @@ -1,61 +0,0 @@ ---- -# Source: chart-with-template-lib-archive-dep/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - labels: - app: chart-with-template-lib-archive-dep - chart: chart-with-template-lib-archive-dep-0.1.0 - heritage: Helm - release: RELEASE-NAME - name: release-name-chart-with-template-lib-archive-dep -spec: - ports: - - name: http - port: 80 - targetPort: http - selector: - app: chart-with-template-lib-archive-dep - release: RELEASE-NAME - type: ClusterIP ---- -# Source: chart-with-template-lib-archive-dep/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: RELEASE-NAME-chart-with-template-lib-archive-dep - labels: - app: chart-with-template-lib-archive-dep - chart: chart-with-template-lib-archive-dep-0.1.0 - release: RELEASE-NAME - heritage: Helm -spec: - replicas: 1 - selector: - matchLabels: - app: chart-with-template-lib-archive-dep - release: RELEASE-NAME - template: - metadata: - labels: - app: chart-with-template-lib-archive-dep - release: RELEASE-NAME - spec: - containers: - - name: chart-with-template-lib-archive-dep - image: "nginx:stable" - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {} diff --git a/cmd/helm/testdata/output/template-chart-with-template-lib-dep.txt b/cmd/helm/testdata/output/template-chart-with-template-lib-dep.txt deleted file mode 100644 index 12adeb2..0000000 --- a/cmd/helm/testdata/output/template-chart-with-template-lib-dep.txt +++ /dev/null @@ -1,61 +0,0 @@ ---- -# Source: chart-with-template-lib-dep/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - labels: - app: chart-with-template-lib-dep - chart: chart-with-template-lib-dep-0.1.0 - heritage: Helm - release: RELEASE-NAME - name: release-name-chart-with-template-lib-dep -spec: - ports: - - name: http - port: 80 - targetPort: http - selector: - app: chart-with-template-lib-dep - release: RELEASE-NAME - type: ClusterIP ---- -# Source: chart-with-template-lib-dep/templates/deployment.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: RELEASE-NAME-chart-with-template-lib-dep - labels: - app: chart-with-template-lib-dep - chart: chart-with-template-lib-dep-0.1.0 - release: RELEASE-NAME - heritage: Helm -spec: - replicas: 1 - selector: - matchLabels: - app: chart-with-template-lib-dep - release: RELEASE-NAME - template: - metadata: - labels: - app: chart-with-template-lib-dep - release: RELEASE-NAME - spec: - containers: - - name: chart-with-template-lib-dep - image: "nginx:stable" - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {} diff --git a/cmd/helm/testdata/output/template-lib-chart.txt b/cmd/helm/testdata/output/template-lib-chart.txt deleted file mode 100644 index d8a3bf2..0000000 --- a/cmd/helm/testdata/output/template-lib-chart.txt +++ /dev/null @@ -1 +0,0 @@ -Error: validation: chart.metadata.type must be application or library diff --git a/cmd/helm/testdata/output/template-name-template.txt b/cmd/helm/testdata/output/template-name-template.txt deleted file mode 100644 index acba503..0000000 --- a/cmd/helm/testdata/output/template-name-template.txt +++ /dev/null @@ -1,55 +0,0 @@ ---- -# Source: subchart1/charts/subcharta/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subcharta - labels: - helm.sh/chart: "subcharta-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app.kubernetes.io/name: subcharta ---- -# Source: subchart1/charts/subchartb/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchartb - labels: - helm.sh/chart: "subchartb-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchartb ---- -# Source: subchart1/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchart1 - labels: - helm.sh/chart: "subchart1-0.1.0" - app.kubernetes.io/instance: "foobar-YWJj-baz" - kube-version/major: "1" - kube-version/minor: "16" - kube-version/version: "v1.16.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchart1 diff --git a/cmd/helm/testdata/output/template-no-args.txt b/cmd/helm/testdata/output/template-no-args.txt deleted file mode 100644 index f72f2b8..0000000 --- a/cmd/helm/testdata/output/template-no-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm template" requires at least 1 argument - -Usage: helm template [NAME] [CHART] [flags] diff --git a/cmd/helm/testdata/output/template-set.txt b/cmd/helm/testdata/output/template-set.txt deleted file mode 100644 index b0924b5..0000000 --- a/cmd/helm/testdata/output/template-set.txt +++ /dev/null @@ -1,55 +0,0 @@ ---- -# Source: subchart1/charts/subcharta/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subcharta - labels: - helm.sh/chart: "subcharta-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app.kubernetes.io/name: subcharta ---- -# Source: subchart1/charts/subchartb/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchartb - labels: - helm.sh/chart: "subchartb-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchartb ---- -# Source: subchart1/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchart1 - labels: - helm.sh/chart: "subchart1-0.1.0" - app.kubernetes.io/instance: "RELEASE-NAME" - kube-version/major: "1" - kube-version/minor: "16" - kube-version/version: "v1.16.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app.kubernetes.io/name: subchart1 diff --git a/cmd/helm/testdata/output/template-show-only-multiple.txt b/cmd/helm/testdata/output/template-show-only-multiple.txt deleted file mode 100644 index abb9a2e..0000000 --- a/cmd/helm/testdata/output/template-show-only-multiple.txt +++ /dev/null @@ -1,39 +0,0 @@ ---- -# Source: subchart1/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchart1 - labels: - helm.sh/chart: "subchart1-0.1.0" - app.kubernetes.io/instance: "RELEASE-NAME" - kube-version/major: "1" - kube-version/minor: "16" - kube-version/version: "v1.16.0" - kube-api-version/test: v1 -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchart1 ---- -# Source: subchart1/charts/subcharta/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subcharta - labels: - helm.sh/chart: "subcharta-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app.kubernetes.io/name: subcharta diff --git a/cmd/helm/testdata/output/template-show-only-one.txt b/cmd/helm/testdata/output/template-show-only-one.txt deleted file mode 100644 index f0dd083..0000000 --- a/cmd/helm/testdata/output/template-show-only-one.txt +++ /dev/null @@ -1,22 +0,0 @@ ---- -# Source: subchart1/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchart1 - labels: - helm.sh/chart: "subchart1-0.1.0" - app.kubernetes.io/instance: "RELEASE-NAME" - kube-version/major: "1" - kube-version/minor: "16" - kube-version/version: "v1.16.0" - kube-api-version/test: v1 -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchart1 diff --git a/cmd/helm/testdata/output/template-values-files.txt b/cmd/helm/testdata/output/template-values-files.txt deleted file mode 100644 index b0924b5..0000000 --- a/cmd/helm/testdata/output/template-values-files.txt +++ /dev/null @@ -1,55 +0,0 @@ ---- -# Source: subchart1/charts/subcharta/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subcharta - labels: - helm.sh/chart: "subcharta-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app.kubernetes.io/name: subcharta ---- -# Source: subchart1/charts/subchartb/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchartb - labels: - helm.sh/chart: "subchartb-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchartb ---- -# Source: subchart1/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchart1 - labels: - helm.sh/chart: "subchart1-0.1.0" - app.kubernetes.io/instance: "RELEASE-NAME" - kube-version/major: "1" - kube-version/minor: "16" - kube-version/version: "v1.16.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app.kubernetes.io/name: subchart1 diff --git a/cmd/helm/testdata/output/template-with-api-version.txt b/cmd/helm/testdata/output/template-with-api-version.txt deleted file mode 100644 index da35590..0000000 --- a/cmd/helm/testdata/output/template-with-api-version.txt +++ /dev/null @@ -1,56 +0,0 @@ ---- -# Source: subchart1/charts/subcharta/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subcharta - labels: - helm.sh/chart: "subcharta-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app.kubernetes.io/name: subcharta ---- -# Source: subchart1/charts/subchartb/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchartb - labels: - helm.sh/chart: "subchartb-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchartb ---- -# Source: subchart1/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchart1 - labels: - helm.sh/chart: "subchart1-0.1.0" - app.kubernetes.io/instance: "RELEASE-NAME" - kube-version/major: "1" - kube-version/minor: "16" - kube-version/version: "v1.16.0" - kube-api-version/test: v1 -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchart1 diff --git a/cmd/helm/testdata/output/template-with-crds.txt b/cmd/helm/testdata/output/template-with-crds.txt deleted file mode 100644 index 9fa1c7e..0000000 --- a/cmd/helm/testdata/output/template-with-crds.txt +++ /dev/null @@ -1,72 +0,0 @@ ---- -# Source: crds/crdA.yaml -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: testCRDs -spec: - group: testCRDGroups - names: - kind: TestCRD - listKind: TestCRDList - plural: TestCRDs - shortNames: - - tc - singular: authconfig - ---- -# Source: subchart1/charts/subcharta/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subcharta - labels: - helm.sh/chart: "subcharta-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app.kubernetes.io/name: subcharta ---- -# Source: subchart1/charts/subchartb/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchartb - labels: - helm.sh/chart: "subchartb-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchartb ---- -# Source: subchart1/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchart1 - labels: - helm.sh/chart: "subchart1-0.1.0" - app.kubernetes.io/instance: "RELEASE-NAME" - kube-version/major: "1" - kube-version/minor: "16" - kube-version/version: "v1.16.0" - kube-api-version/test: v1 -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchart1 diff --git a/cmd/helm/testdata/output/template.txt b/cmd/helm/testdata/output/template.txt deleted file mode 100644 index 080be61..0000000 --- a/cmd/helm/testdata/output/template.txt +++ /dev/null @@ -1,55 +0,0 @@ ---- -# Source: subchart1/charts/subcharta/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subcharta - labels: - helm.sh/chart: "subcharta-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: apache - selector: - app.kubernetes.io/name: subcharta ---- -# Source: subchart1/charts/subchartb/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchartb - labels: - helm.sh/chart: "subchartb-0.1.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchartb ---- -# Source: subchart1/templates/service.yaml -apiVersion: v1 -kind: Service -metadata: - name: subchart1 - labels: - helm.sh/chart: "subchart1-0.1.0" - app.kubernetes.io/instance: "RELEASE-NAME" - kube-version/major: "1" - kube-version/minor: "16" - kube-version/version: "v1.16.0" -spec: - type: ClusterIP - ports: - - port: 80 - targetPort: 80 - protocol: TCP - name: nginx - selector: - app.kubernetes.io/name: subchart1 diff --git a/cmd/helm/testdata/output/uninstall-keep-history.txt b/cmd/helm/testdata/output/uninstall-keep-history.txt deleted file mode 100644 index f5454b8..0000000 --- a/cmd/helm/testdata/output/uninstall-keep-history.txt +++ /dev/null @@ -1 +0,0 @@ -release "aeneas" uninstalled diff --git a/cmd/helm/testdata/output/uninstall-multiple.txt b/cmd/helm/testdata/output/uninstall-multiple.txt deleted file mode 100644 index ee1c67d..0000000 --- a/cmd/helm/testdata/output/uninstall-multiple.txt +++ /dev/null @@ -1,2 +0,0 @@ -release "aeneas" uninstalled -release "aeneas2" uninstalled diff --git a/cmd/helm/testdata/output/uninstall-no-args.txt b/cmd/helm/testdata/output/uninstall-no-args.txt deleted file mode 100644 index fc01a75..0000000 --- a/cmd/helm/testdata/output/uninstall-no-args.txt +++ /dev/null @@ -1,3 +0,0 @@ -Error: "helm uninstall" requires at least 1 argument - -Usage: helm uninstall RELEASE_NAME [...] [flags] diff --git a/cmd/helm/testdata/output/uninstall-no-hooks.txt b/cmd/helm/testdata/output/uninstall-no-hooks.txt deleted file mode 100644 index f5454b8..0000000 --- a/cmd/helm/testdata/output/uninstall-no-hooks.txt +++ /dev/null @@ -1 +0,0 @@ -release "aeneas" uninstalled diff --git a/cmd/helm/testdata/output/uninstall-timeout.txt b/cmd/helm/testdata/output/uninstall-timeout.txt deleted file mode 100644 index f5454b8..0000000 --- a/cmd/helm/testdata/output/uninstall-timeout.txt +++ /dev/null @@ -1 +0,0 @@ -release "aeneas" uninstalled diff --git a/cmd/helm/testdata/output/uninstall.txt b/cmd/helm/testdata/output/uninstall.txt deleted file mode 100644 index f5454b8..0000000 --- a/cmd/helm/testdata/output/uninstall.txt +++ /dev/null @@ -1 +0,0 @@ -release "aeneas" uninstalled diff --git a/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt b/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt deleted file mode 100644 index 6dddc73..0000000 --- a/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -Error: cannot load Chart.yaml: error converting YAML to JSON: yaml: line 6: did not find expected '-' indicator diff --git a/cmd/helm/testdata/output/upgrade-with-install-timeout.txt b/cmd/helm/testdata/output/upgrade-with-install-timeout.txt deleted file mode 100644 index 5d8d3a4..0000000 --- a/cmd/helm/testdata/output/upgrade-with-install-timeout.txt +++ /dev/null @@ -1,7 +0,0 @@ -Release "crazy-bunny" has been upgraded. Happy Helming! -NAME: crazy-bunny -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 2 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/upgrade-with-install.txt b/cmd/helm/testdata/output/upgrade-with-install.txt deleted file mode 100644 index af61212..0000000 --- a/cmd/helm/testdata/output/upgrade-with-install.txt +++ /dev/null @@ -1,7 +0,0 @@ -Release "zany-bunny" has been upgraded. Happy Helming! -NAME: zany-bunny -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 2 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/upgrade-with-missing-dependencies.txt b/cmd/helm/testdata/output/upgrade-with-missing-dependencies.txt deleted file mode 100644 index de62e1d..0000000 --- a/cmd/helm/testdata/output/upgrade-with-missing-dependencies.txt +++ /dev/null @@ -1 +0,0 @@ -Error: found in Chart.yaml, but missing in charts/ directory: reqsubchart2 diff --git a/cmd/helm/testdata/output/upgrade-with-reset-values.txt b/cmd/helm/testdata/output/upgrade-with-reset-values.txt deleted file mode 100644 index 01f1c0a..0000000 --- a/cmd/helm/testdata/output/upgrade-with-reset-values.txt +++ /dev/null @@ -1,7 +0,0 @@ -Release "funny-bunny" has been upgraded. Happy Helming! -NAME: funny-bunny -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 5 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/upgrade-with-reset-values2.txt b/cmd/helm/testdata/output/upgrade-with-reset-values2.txt deleted file mode 100644 index fdd1d2d..0000000 --- a/cmd/helm/testdata/output/upgrade-with-reset-values2.txt +++ /dev/null @@ -1,7 +0,0 @@ -Release "funny-bunny" has been upgraded. Happy Helming! -NAME: funny-bunny -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 6 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/upgrade-with-timeout.txt b/cmd/helm/testdata/output/upgrade-with-timeout.txt deleted file mode 100644 index be3a423..0000000 --- a/cmd/helm/testdata/output/upgrade-with-timeout.txt +++ /dev/null @@ -1,7 +0,0 @@ -Release "funny-bunny" has been upgraded. Happy Helming! -NAME: funny-bunny -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 4 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/upgrade-with-wait.txt b/cmd/helm/testdata/output/upgrade-with-wait.txt deleted file mode 100644 index 500d07a..0000000 --- a/cmd/helm/testdata/output/upgrade-with-wait.txt +++ /dev/null @@ -1,7 +0,0 @@ -Release "crazy-bunny" has been upgraded. Happy Helming! -NAME: crazy-bunny -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 3 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/upgrade.txt b/cmd/helm/testdata/output/upgrade.txt deleted file mode 100644 index bea42db..0000000 --- a/cmd/helm/testdata/output/upgrade.txt +++ /dev/null @@ -1,7 +0,0 @@ -Release "funny-bunny" has been upgraded. Happy Helming! -NAME: funny-bunny -LAST DEPLOYED: Fri Sep 2 22:04:05 1977 -NAMESPACE: default -STATUS: deployed -REVISION: 3 -TEST SUITE: None diff --git a/cmd/helm/testdata/output/values.json b/cmd/helm/testdata/output/values.json deleted file mode 100644 index ea83086..0000000 --- a/cmd/helm/testdata/output/values.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"value"} diff --git a/cmd/helm/testdata/output/values.yaml b/cmd/helm/testdata/output/values.yaml deleted file mode 100644 index 54ab03c..0000000 --- a/cmd/helm/testdata/output/values.yaml +++ /dev/null @@ -1 +0,0 @@ -name: value diff --git a/cmd/helm/testdata/output/version-client-shorthand.txt b/cmd/helm/testdata/output/version-client-shorthand.txt deleted file mode 100644 index 4b493d3..0000000 --- a/cmd/helm/testdata/output/version-client-shorthand.txt +++ /dev/null @@ -1 +0,0 @@ -version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:""} diff --git a/cmd/helm/testdata/output/version-client.txt b/cmd/helm/testdata/output/version-client.txt deleted file mode 100644 index 4b493d3..0000000 --- a/cmd/helm/testdata/output/version-client.txt +++ /dev/null @@ -1 +0,0 @@ -version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:""} diff --git a/cmd/helm/testdata/output/version-short.txt b/cmd/helm/testdata/output/version-short.txt deleted file mode 100644 index d9fb9c7..0000000 --- a/cmd/helm/testdata/output/version-short.txt +++ /dev/null @@ -1 +0,0 @@ -v3.0 diff --git a/cmd/helm/testdata/output/version-template.txt b/cmd/helm/testdata/output/version-template.txt deleted file mode 100644 index 776c191..0000000 --- a/cmd/helm/testdata/output/version-template.txt +++ /dev/null @@ -1 +0,0 @@ -Version: v3.0 \ No newline at end of file diff --git a/cmd/helm/testdata/output/version.txt b/cmd/helm/testdata/output/version.txt deleted file mode 100644 index 4b493d3..0000000 --- a/cmd/helm/testdata/output/version.txt +++ /dev/null @@ -1 +0,0 @@ -version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:""} diff --git a/cmd/helm/testdata/plugins.yaml b/cmd/helm/testdata/plugins.yaml deleted file mode 100644 index 6908697..0000000 --- a/cmd/helm/testdata/plugins.yaml +++ /dev/null @@ -1,3 +0,0 @@ -plugins: -- name: testplugin - url: testdata/testplugin diff --git a/cmd/helm/testdata/repositories.yaml b/cmd/helm/testdata/repositories.yaml deleted file mode 100644 index ad88dcf..0000000 --- a/cmd/helm/testdata/repositories.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -repositories: - - name: charts - url: "https://kubernetes-charts.storage.googleapis.com" diff --git a/cmd/helm/testdata/testcharts/alpine/Chart.yaml b/cmd/helm/testdata/testcharts/alpine/Chart.yaml deleted file mode 100644 index 1d6bad8..0000000 --- a/cmd/helm/testdata/testcharts/alpine/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -appVersion: "3.9" -description: Deploy a basic Alpine Linux pod -home: https://helm.sh/helm -name: alpine -sources: -- https://github.com/helm/helm -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/alpine/README.md b/cmd/helm/testdata/testcharts/alpine/README.md deleted file mode 100644 index fcf7ee0..0000000 --- a/cmd/helm/testdata/testcharts/alpine/README.md +++ /dev/null @@ -1,13 +0,0 @@ -#Alpine: A simple Helm chart - -Run a single pod of Alpine Linux. - -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.yaml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/cmd/helm/testdata/testcharts/alpine/extra_values.yaml b/cmd/helm/testdata/testcharts/alpine/extra_values.yaml deleted file mode 100644 index 468bbac..0000000 --- a/cmd/helm/testdata/testcharts/alpine/extra_values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -test: - Name: extra-values diff --git a/cmd/helm/testdata/testcharts/alpine/more_values.yaml b/cmd/helm/testdata/testcharts/alpine/more_values.yaml deleted file mode 100644 index 3d21e1f..0000000 --- a/cmd/helm/testdata/testcharts/alpine/more_values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -test: - Name: more-values diff --git a/cmd/helm/testdata/testcharts/alpine/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index a1a44e5..0000000 --- a/cmd/helm/testdata/testcharts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{.Release.Name}}-{{.Values.Name}}" - labels: - # The "app.kubernetes.io/managed-by" label is used to track which tool - # deployed a given chart. It is useful for admins who want to see what - # releases a particular tool is responsible for. - app.kubernetes.io/managed-by: {{.Release.Service | quote }} - # The "app.kubernetes.io/instance" convention makes it easy to tie a release - # to all of the Kubernetes resources that were created as part of that - # release. - app.kubernetes.io/instance: {{.Release.Name | quote }} - app.kubernetes.io/version: {{ .Chart.AppVersion }} - # This makes it easy to audit chart usage. - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" - values: {{.Values.Name}} -spec: - # This shows how to use a simple value. This will look for a passed-in value - # called restartPolicy. If it is not found, it will use the default value. - # {{default "Never" .restartPolicy}} is a slightly optimized version of the - # more conventional syntax: {{.restartPolicy | default "Never"}} - restartPolicy: {{default "Never" .Values.restartPolicy}} - containers: - - name: waiter - image: "alpine:{{ .Chart.AppVersion }}" - command: ["/bin/sleep","9000"] diff --git a/cmd/helm/testdata/testcharts/alpine/values.yaml b/cmd/helm/testdata/testcharts/alpine/values.yaml deleted file mode 100644 index 807e12a..0000000 --- a/cmd/helm/testdata/testcharts/alpine/values.yaml +++ /dev/null @@ -1 +0,0 @@ -Name: my-alpine diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/.helmignore b/cmd/helm/testdata/testcharts/chart-bad-requirements/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-requirements/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml deleted file mode 100644 index 1f445ee..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: chart-missing-deps -version: 0.1.0 -dependencies: - - name: reqsubchart - version: 0.1.0 - repository: "https://example.com/charts" diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/.helmignore b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml deleted file mode 100644 index 3561355..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: reqsubchart -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/values.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/values.yaml deleted file mode 100644 index 0f0b63f..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for reqsubchart. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/values.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/values.yaml deleted file mode 100644 index d57f76b..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-requirements/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for reqtest. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml deleted file mode 100644 index e77b5af..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: Deploy a basic Alpine Linux pod -home: https://helm.sh/helm -name: chart-bad-type -sources: - - https://github.com/helm/helm -version: 0.1.0 -type: foobar diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/README.md b/cmd/helm/testdata/testcharts/chart-bad-type/README.md deleted file mode 100644 index fcf7ee0..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-type/README.md +++ /dev/null @@ -1,13 +0,0 @@ -#Alpine: A simple Helm chart - -Run a single pod of Alpine Linux. - -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.yaml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/extra_values.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/extra_values.yaml deleted file mode 100644 index 468bbac..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-type/extra_values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -test: - Name: extra-values diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/more_values.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/more_values.yaml deleted file mode 100644 index 3d21e1f..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-type/more_values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -test: - Name: more-values diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/templates/alpine-pod.yaml deleted file mode 100644 index a40ae32..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-type/templates/alpine-pod.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{.Release.Name}}-{{.Values.Name}}" - labels: - # The "app.kubernetes.io/managed-by" label is used to track which tool - # deployed a given chart. It is useful for admins who want to see what - # releases a particular tool is responsible for. - app.kubernetes.io/managed-by: {{.Release.Service | quote }} - # The "release" convention makes it easy to tie a release to all of the - # Kubernetes resources that were created as part of that release. - app.kubernetes.io/instance: {{.Release.Name | quote }} - # This makes it easy to audit chart usage. - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" - values: {{.Values.test.Name}} -spec: - # This shows how to use a simple value. This will look for a passed-in value - # called restartPolicy. If it is not found, it will use the default value. - # {{default "Never" .restartPolicy}} is a slightly optimized version of the - # more conventional syntax: {{.restartPolicy | default "Never"}} - restartPolicy: {{default "Never" .Values.restartPolicy}} - containers: - - name: waiter - image: "alpine:3.9" - command: ["/bin/sleep","9000"] diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/values.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/values.yaml deleted file mode 100644 index 807e12a..0000000 --- a/cmd/helm/testdata/testcharts/chart-bad-type/values.yaml +++ /dev/null @@ -1 +0,0 @@ -Name: my-alpine diff --git a/cmd/helm/testdata/testcharts/chart-missing-deps/.helmignore b/cmd/helm/testdata/testcharts/chart-missing-deps/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/chart-missing-deps/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml b/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml deleted file mode 100644 index 9605636..0000000 --- a/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: chart-missing-deps -version: 0.1.0 -dependencies: - - name: reqsubchart - version: 0.1.0 - repository: "https://example.com/charts" - - name: reqsubchart2 - version: 0.2.0 - repository: "https://example.com/charts" diff --git a/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/.helmignore b/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/Chart.yaml deleted file mode 100644 index 3561355..0000000 --- a/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: reqsubchart -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/values.yaml b/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/values.yaml deleted file mode 100644 index 0f0b63f..0000000 --- a/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for reqsubchart. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/cmd/helm/testdata/testcharts/chart-missing-deps/values.yaml b/cmd/helm/testdata/testcharts/chart-missing-deps/values.yaml deleted file mode 100644 index d57f76b..0000000 --- a/cmd/helm/testdata/testcharts/chart-missing-deps/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for reqtest. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/.helmignore b/cmd/helm/testdata/testcharts/chart-with-lib-dep/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-lib-dep/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/Chart.yaml deleted file mode 100644 index 773cc9f..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-lib-dep/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for Kubernetes -name: chart-with-lib-dep -type: application -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/charts/common-0.0.5.tgz b/cmd/helm/testdata/testcharts/chart-with-lib-dep/charts/common-0.0.5.tgz deleted file mode 100644 index ca0a64a..0000000 Binary files a/cmd/helm/testdata/testcharts/chart-with-lib-dep/charts/common-0.0.5.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/NOTES.txt deleted file mode 100644 index a758b79..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/NOTES.txt +++ /dev/null @@ -1,19 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range .Values.ingress.hosts }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "chart-with-lib-dep.fullname" . }}) - export NODE_IP=$(kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get svc -w {{ template "chart-with-lib-dep.fullname" . }}' - export SERVICE_IP=$(kubectl get svc {{ template "chart-with-lib-dep.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods -l "app={{ template "chart-with-lib-dep.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/_helpers.tpl b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/_helpers.tpl deleted file mode 100644 index b8be8ca..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/_helpers.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "chart-with-lib-dep.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "chart-with-lib-dep.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "chart-with-lib-dep.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/deployment.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/deployment.yaml deleted file mode 100644 index 521fa59..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/deployment.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "chart-with-lib-dep.fullname" . }} - labels: - app.kubernetes.io/name: {{ template "chart-with-lib-dep.name" . }} - helm.sh/chart: {{ template "chart-with-lib-dep.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app.kubernetes.io/name: {{ template "chart-with-lib-dep.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ template "chart-with-lib-dep.name" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - spec: - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: -{{ toYaml .Values.resources | indent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: -{{ toYaml . | indent 8 }} - {{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/ingress.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/ingress.yaml deleted file mode 100644 index 42afd08..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/ingress.yaml +++ /dev/null @@ -1,38 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "chart-with-lib-dep.fullname" . -}} -{{- $ingressPath := .Values.ingress.path -}} -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - app.kubernetes.io/name: {{ template "chart-with-lib-dep.name" . }} - helm.sh/chart: {{ template "chart-with-lib-dep.chart" . }} - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- with .Values.ingress.annotations }} - annotations: -{{ toYaml . | indent 4 }} -{{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ . }} - http: - paths: - - path: {{ $ingressPath }} - backend: - serviceName: {{ $fullName }} - servicePort: http - {{- end }} -{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/service.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/service.yaml deleted file mode 100644 index 4c2b91a..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/service.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- template "common.service" (list . "mychart.service") -}} -{{- define "mychart.service" -}} -## Define overrides for your Service resource here, e.g. -# metadata: -# labels: -# custom: label -# spec: -# ports: -# - port: 8080 -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/values.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/values.yaml deleted file mode 100644 index a0cc07e..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-lib-dep/values.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# Default values for chart-with-lib-dep. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent - -nameOverride: "" -fullnameOverride: "" - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - path: / - hosts: - - chart-example.local - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/Chart.yaml deleted file mode 100644 index d3458f6..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: chart-with-no-templates-dir -description: an example chart -version: 199.44.12345-Alpha.1+cafe009 -icon: http://riverrun.io diff --git a/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/values.yaml b/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/values.yaml deleted file mode 100644 index ec82367..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-no-templates-dir/values.yaml +++ /dev/null @@ -1 +0,0 @@ -justAValue: "an example chart here" diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/Chart.yaml deleted file mode 100644 index 4e24c2e..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -name: chart-without-schema -description: A Helm chart for Kubernetes -type: application -version: 0.1.0 -appVersion: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/Chart.yaml deleted file mode 100644 index b5a77c5..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -name: subchart-with-schema -description: A Helm chart for Kubernetes -type: application -version: 0.1.0 -appVersion: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/templates/empty.yaml deleted file mode 100644 index c80812f..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/templates/empty.yaml +++ /dev/null @@ -1 +0,0 @@ -# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.schema.json deleted file mode 100644 index 4ff7918..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.schema.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Values", - "type": "object", - "properties": { - "age": { - "description": "Age", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "age" - ] -} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/templates/empty.yaml deleted file mode 100644 index c80812f..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/templates/empty.yaml +++ /dev/null @@ -1 +0,0 @@ -# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.schema.json deleted file mode 100644 index f309480..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.schema.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Values", - "type": "object", - "properties": { - "firstname": { - "description": "First name", - "type": "string" - }, - "lastname": { - "type": "string" - } - }, - "required": [ - "firstname", - "lastname" - ] -} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.yaml deleted file mode 100644 index c9deafc..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.yaml +++ /dev/null @@ -1 +0,0 @@ -firstname: "John" diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-negative/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-negative/Chart.yaml deleted file mode 100644 index 395d24f..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-negative/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -description: Empty testing chart -home: https://k8s.io/helm -name: empty -sources: -- https://github.com/kubernetes/helm -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-negative/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-negative/templates/empty.yaml deleted file mode 100644 index c80812f..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-negative/templates/empty.yaml +++ /dev/null @@ -1 +0,0 @@ -# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.schema.json deleted file mode 100644 index 4df89bb..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.schema.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "properties": { - "addresses": { - "description": "List of addresses", - "items": { - "properties": { - "city": { - "type": "string" - }, - "number": { - "type": "number" - }, - "street": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "age": { - "description": "Age", - "minimum": 0, - "type": "integer" - }, - "employmentInfo": { - "properties": { - "salary": { - "minimum": 0, - "type": "number" - }, - "title": { - "type": "string" - } - }, - "required": [ - "salary" - ], - "type": "object" - }, - "firstname": { - "description": "First name", - "type": "string" - }, - "lastname": { - "type": "string" - }, - "likesCoffee": { - "type": "boolean" - }, - "phoneNumbers": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "firstname", - "lastname", - "addresses", - "employmentInfo" - ], - "title": "Values", - "type": "object" -} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.yaml deleted file mode 100644 index 5a1250b..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.yaml +++ /dev/null @@ -1,14 +0,0 @@ -firstname: John -lastname: Doe -age: -5 -likesCoffee: true -addresses: - - city: Springfield - street: Main - number: 12345 - - city: New York - street: Broadway - number: 67890 -phoneNumbers: - - "(888) 888-8888" - - "(555) 555-5555" diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema/Chart.yaml deleted file mode 100644 index 395d24f..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -description: Empty testing chart -home: https://k8s.io/helm -name: empty -sources: -- https://github.com/kubernetes/helm -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/extra-values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema/extra-values.yaml deleted file mode 100644 index 76c290c..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema/extra-values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -age: -5 -employmentInfo: null diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema/templates/empty.yaml deleted file mode 100644 index c80812f..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema/templates/empty.yaml +++ /dev/null @@ -1 +0,0 @@ -# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema/values.schema.json deleted file mode 100644 index 4df89bb..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema/values.schema.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "properties": { - "addresses": { - "description": "List of addresses", - "items": { - "properties": { - "city": { - "type": "string" - }, - "number": { - "type": "number" - }, - "street": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "age": { - "description": "Age", - "minimum": 0, - "type": "integer" - }, - "employmentInfo": { - "properties": { - "salary": { - "minimum": 0, - "type": "number" - }, - "title": { - "type": "string" - } - }, - "required": [ - "salary" - ], - "type": "object" - }, - "firstname": { - "description": "First name", - "type": "string" - }, - "lastname": { - "type": "string" - }, - "likesCoffee": { - "type": "boolean" - }, - "phoneNumbers": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "firstname", - "lastname", - "addresses", - "employmentInfo" - ], - "title": "Values", - "type": "object" -} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema/values.yaml deleted file mode 100644 index 042dea6..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-schema/values.yaml +++ /dev/null @@ -1,17 +0,0 @@ -firstname: John -lastname: Doe -age: 25 -likesCoffee: true -employmentInfo: - title: Software Developer - salary: 100000 -addresses: - - city: Springfield - street: Main - number: 12345 - - city: New York - street: Broadway - number: 67890 -phoneNumbers: - - "(888) 888-8888" - - "(555) 555-5555" diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/.helmignore b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/Chart.yaml deleted file mode 100644 index de53ce5..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for Kubernetes -name: chart-with-template-lib-archive-dep -type: application -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/charts/common-0.0.5.tgz b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/charts/common-0.0.5.tgz deleted file mode 100644 index 4655178..0000000 Binary files a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/charts/common-0.0.5.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/NOTES.txt deleted file mode 100644 index 5c53ac0..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/NOTES.txt +++ /dev/null @@ -1,19 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range .Values.ingress.hosts }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "chart-with-template-lib-archive-dep.fullname" . }}) - export NODE_IP=$(kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get svc -w {{ template "chart-with-template-lib-archive-dep.fullname" . }}' - export SERVICE_IP=$(kubectl get svc {{ template "chart-with-template-lib-archive-dep.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods -l "app={{ template "chart-with-template-lib-archive-dep.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/_helpers.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/_helpers.tpl deleted file mode 100644 index 76ca56b..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/_helpers.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "chart-with-template-lib-archive-dep.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "chart-with-template-lib-archive-dep.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "chart-with-template-lib-archive-dep.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/deployment.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/deployment.yaml deleted file mode 100644 index a49572f..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/deployment.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "chart-with-template-lib-archive-dep.fullname" . }} - labels: - app: {{ template "chart-with-template-lib-archive-dep.name" . }} - chart: {{ template "chart-with-template-lib-archive-dep.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app: {{ template "chart-with-template-lib-archive-dep.name" . }} - release: {{ .Release.Name }} - template: - metadata: - labels: - app: {{ template "chart-with-template-lib-archive-dep.name" . }} - release: {{ .Release.Name }} - spec: - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: -{{ toYaml .Values.resources | indent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: -{{ toYaml . | indent 8 }} - {{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/ingress.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/ingress.yaml deleted file mode 100644 index d3325cf..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/ingress.yaml +++ /dev/null @@ -1,38 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "chart-with-template-lib-archive-dep.fullname" . -}} -{{- $ingressPath := .Values.ingress.path -}} -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - app: {{ template "chart-with-template-lib-archive-dep.name" . }} - chart: {{ template "chart-with-template-lib-archive-dep.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -{{- with .Values.ingress.annotations }} - annotations: -{{ toYaml . | indent 4 }} -{{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ . }} - http: - paths: - - path: {{ $ingressPath }} - backend: - serviceName: {{ $fullName }} - servicePort: http - {{- end }} -{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/service.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/service.yaml deleted file mode 100644 index bfcb080..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/service.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- template "common.service" (list . "chart-with-template-lib-archive-dep.service") -}} -{{- define "chart-with-template-lib-archive-dep.service" -}} -## Define overrides for your Service resource here, e.g. -# metadata: -# labels: -# custom: label -# spec: -# ports: -# - port: 8080 -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/values.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/values.yaml deleted file mode 100644 index b5474cb..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/values.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# Default values for chart-with-template-lib-archive-dep. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent - -nameOverride: "" -fullnameOverride: "" - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - path: / - hosts: - - chart-example.local - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/.helmignore b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/Chart.yaml deleted file mode 100644 index cf6fc39..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -appVersion: "1.0" -description: A Helm chart for Kubernetes -name: chart-with-template-lib-dep -type: application -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/.helmignore b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/.helmignore deleted file mode 100755 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/Chart.yaml deleted file mode 100755 index ba14ca0..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/Chart.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -appVersion: 0.0.5 -description: Common chartbuilding components and helpers -home: https://helm.sh -maintainers: -- email: technosophos@gmail.com - name: technosophos -- email: adnan@bitnami.com - name: prydonius -name: common -version: 0.0.5 -type: library diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/README.md b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/README.md deleted file mode 100755 index ca04594..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/README.md +++ /dev/null @@ -1,831 +0,0 @@ -# Common: The Helm Helper Chart - -This chart is designed to make it easier for you to build and maintain Helm -charts. - -It provides utilities that reflect best practices of Kubernetes chart development, -making it faster for you to write charts. - -## Tips - -A few tips for working with Common: - -- Be careful when using functions that generate random data (like `common.fullname.unique`). - They may trigger unwanted upgrades or have other side effects. - -In this document, we use `RELEASE-NAME` as the name of the release. - -## Resource Kinds - -Kubernetes defines a variety of resource kinds, from `Secret` to `StatefulSet`. -We define some of the most common kinds in a way that lets you easily work with -them. - -The resource kind templates are designed to make it much faster for you to -define _basic_ versions of these resources. They allow you to extend and modify -just what you need, without having to copy around lots of boilerplate. - -To make use of these templates you must define a template that will extend the -base template (though it can be empty). The name of this template is then passed -to the base template, for example: - -```yaml -{{- template "common.service" (list . "mychart.service") -}} -{{- define "mychart.service" -}} -## Define overrides for your Service resource here, e.g. -# metadata: -# labels: -# custom: label -# spec: -# ports: -# - port: 8080 -{{- end -}} -``` - -Note that the `common.service` template defines two parameters: - - - The root context (usually `.`) - - A template name containing the service definition overrides - -A limitation of the Go template library is that a template can only take a -single argument. The `list` function is used to workaround this by constructing -a list or array of arguments that is passed to the template. - -The `common.service` template is responsible for rendering the templates with -the root context and merging any overrides. As you can see, this makes it very -easy to create a basic `Service` resource without having to copy around the -standard metadata and labels. - -Each implemented base resource is described in greater detail below. - -### `common.service` - -The `common.service` template creates a basic `Service` resource with the -following defaults: - -- Service type (ClusterIP, NodePort, LoadBalancer) made configurable by `.Values.service.type` -- Named port `http` configured on port 80 -- Selector set to `app: {{ template "common.name" }}, release: {{ .Release.Name | quote }}` to match the default used in the `Deployment` resource - -Example template: - -```yaml -{{- template "common.service" (list . "mychart.mail.service") -}} -{{- define "mychart.mail.service" -}} -metadata: - name: {{ template "common.fullname" . }}-mail # overrides the default name to add a suffix - labels: # appended to the labels section - protocol: mail -spec: - ports: # composes the `ports` section of the service definition. - - name: smtp - port: 25 - targetPort: 25 - - name: imaps - port: 993 - targetPort: 993 - selector: # this is appended to the default selector - protocol: mail -{{- end -}} ---- -{{ template "common.service" (list . "mychart.web.service") -}} -{{- define "mychart.web.service" -}} -metadata: - name: {{ template "common.fullname" . }}-www # overrides the default name to add a suffix - labels: # appended to the labels section - protocol: www -spec: - ports: # composes the `ports` section of the service definition. - - name: www - port: 80 - targetPort: 8080 -{{- end -}} -``` - -The above template defines _two_ services: a web service and a mail service. - -The most important part of a service definition is the `ports` object, which -defines the ports that this service will listen on. Most of the time, -`selector` is computed for you. But you can replace it or add to it. - -The output of the example above is: - -```yaml -apiVersion: v1 -kind: Service -metadata: - labels: - app: service - chart: service-0.1.0 - heritage: Tiller - protocol: mail - release: release-name - name: release-name-service-mail -spec: - ports: - - name: smtp - port: 25 - targetPort: 25 - - name: imaps - port: 993 - targetPort: 993 - selector: - app: service - release: release-name - protocol: mail - type: ClusterIP ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app: service - chart: service-0.1.0 - heritage: Tiller - protocol: www - release: release-name - name: release-name-service-www -spec: - ports: - - name: www - port: 80 - targetPort: 8080 - type: ClusterIP -``` - -## `common.deployment` - -The `common.deployment` template defines a basic `Deployment`. Underneath the -hood, it uses `common.container` (see next section). - -By default, the pod template within the deployment defines the labels `app: {{ template "common.name" . }}` -and `release: {{ .Release.Name | quote }` as this is also used as the selector. The -standard set of labels are not used as some of these can change during upgrades, -which causes the replica sets and pods to not correctly match. - -Example use: - -```yaml -{{- template "common.deployment" (list . "mychart.deployment") -}} -{{- define "mychart.deployment" -}} -## Define overrides for your Deployment resource here, e.g. -spec: - replicas: {{ .Values.replicaCount }} -{{- end -}} -``` - -## `common.container` - -The `common.container` template creates a basic `Container` spec to be used -within a `Deployment` or `ReplicaSet`. It holds the following defaults: - -- The name is set to the chart name -- Uses `.Values.image` to describe the image to run, with the following spec: - ```yaml - image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent - ``` -- Exposes the named port `http` as port 80 -- Lays out the compute resources using `.Values.resources` - -Example use: - -```yaml -{{- template "common.deployment" (list . "mychart.deployment") -}} -{{- define "mychart.deployment" -}} -## Define overrides for your Deployment resource here, e.g. -spec: - template: - spec: - containers: - - {{ template "common.container" (list . "mychart.deployment.container") }} -{{- end -}} -{{- define "mychart.deployment.container" -}} -## Define overrides for your Container here, e.g. -livenessProbe: - httpGet: - path: / - port: 80 -readinessProbe: - httpGet: - path: / - port: 80 -{{- end -}} -``` - -The above example creates a `Deployment` resource which makes use of the -`common.container` template to populate the PodSpec's container list. The usage -of this template is similar to the other resources, you must define and -reference a template that contains overrides for the container object. - -The most important part of a container definition is the image you want to run. -As mentioned above, this is derived from `.Values.image` by default. It is a -best practice to define the image, tag and pull policy in your charts' values as -this makes it easy for an operator to change the image registry, or use a -specific tag or version. Another example of configuration that should be exposed -to chart operators is the container's required compute resources, as this is -also very specific to an operators environment. An example `values.yaml` for -your chart could look like: - -```yaml -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent -resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi -``` - -The output of running the above values through the earlier template is: - -```yaml -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - app: deployment - chart: deployment-0.1.0 - heritage: Tiller - release: release-name - name: release-name-deployment -spec: - template: - metadata: - labels: - app: deployment - spec: - containers: - - image: nginx:stable - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: / - port: 80 - name: deployment - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: / - port: 80 - resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi -``` - -## `common.configmap` - -The `common.configmap` template creates an empty `ConfigMap` resource that you -can override with your configuration. - -Example use: - -```yaml -{{- template "common.configmap" (list . "mychart.configmap") -}} -{{- define "mychart.configmap" -}} -data: - zeus: cat - athena: cat - julius: cat - one: |- - {{ .Files.Get "file1.txt" }} -{{- end -}} -``` - -Output: - -```yaml -apiVersion: v1 -data: - athena: cat - julius: cat - one: This is a file. - zeus: cat -kind: ConfigMap -metadata: - labels: - app: configmap - chart: configmap-0.1.0 - heritage: Tiller - release: release-name - name: release-name-configmap -``` - -## `common.secret` - -The `common.secret` template creates an empty `Secret` resource that you -can override with your secrets. - -Example use: - -```yaml -{{- template "common.secret" (list . "mychart.secret") -}} -{{- define "mychart.secret" -}} -data: - zeus: {{ print "cat" | b64enc }} - athena: {{ print "cat" | b64enc }} - julius: {{ print "cat" | b64enc }} - one: |- - {{ .Files.Get "file1.txt" | b64enc }} -{{- end -}} -``` - -Output: - -```yaml -apiVersion: v1 -data: - athena: Y2F0 - julius: Y2F0 - one: VGhpcyBpcyBhIGZpbGUuCg== - zeus: Y2F0 -kind: Secret -metadata: - labels: - app: secret - chart: secret-0.1.0 - heritage: Tiller - release: release-name - name: release-name-secret -type: Opaque -``` - -## `common.ingress` - -The `common.ingress` template is designed to give you a well-defined `Ingress` -resource, that can be configured using `.Values.ingress`. An example values file -that can be used to configure the `Ingress` resource is: - -```yaml -ingress: - hosts: - - chart-example.local - annotations: - kubernetes.io/ingress.class: nginx - kubernetes.io/tls-acme: "true" - tls: - - secretName: chart-example-tls - hosts: - - chart-example.local -``` - -Example use: - -```yaml -{{- template "common.ingress" (list . "mychart.ingress") -}} -{{- define "mychart.ingress" -}} -{{- end -}} -``` - -Output: - -```yaml -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - kubernetes.io/tls-acme: "true" - labels: - app: ingress - chart: ingress-0.1.0 - heritage: Tiller - release: release-name - name: release-name-ingress -spec: - rules: - - host: chart-example.local - http: - paths: - - backend: - serviceName: release-name-ingress - servicePort: 80 - path: / - tls: - - hosts: - - chart-example.local - secretName: chart-example-tls -``` - -## `common.persistentvolumeclaim` - -`common.persistentvolumeclaim` can be used to easily add a -`PersistentVolumeClaim` resource to your chart that can be configured using -`.Values.persistence`: - -| Value | Description | -| ------------------------- | ------------------------------------------------------------------------------------------------------- | -| persistence.enabled | Whether or not to claim a persistent volume. If false, `common.volume.pvc` will use an emptyDir instead | -| persistence.storageClass | `StorageClass` name | -| persistence.accessMode | Access mode for persistent volume | -| persistence.size | Size of persistent volume | -| persistence.existingClaim | If defined, `PersistentVolumeClaim` is not created and `common.volume.pvc` helper uses this claim | - -An example values file that can be used to configure the -`PersistentVolumeClaim` resource is: - -```yaml -persistence: - enabled: true - storageClass: fast - accessMode: ReadWriteOnce - size: 8Gi -``` - -Example use: - -```yaml -{{- template "common.persistentvolumeclaim" (list . "mychart.persistentvolumeclaim") -}} -{{- define "mychart.persistentvolumeclaim" -}} -{{- end -}} -``` - -Output: - -```yaml -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - labels: - app: persistentvolumeclaim - chart: persistentvolumeclaim-0.1.0 - heritage: Tiller - release: release-name - name: release-name-persistentvolumeclaim -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 8Gi - storageClassName: "fast" -``` - -## Partial API Objects - -When writing Kubernetes resources, you may find the following helpers useful to -construct parts of the spec. - -### EnvVar - -Use the EnvVar helpers within a container spec to simplify specifying key-value -environment variables or referencing secrets as values. - -Example Use: - -```yaml -{{- template "common.deployment" (list . "mychart.deployment") -}} -{{- define "mychart.deployment" -}} -spec: - template: - spec: - containers: - - {{ template "common.container" (list . "mychart.deployment.container") }} -{{- end -}} -{{- define "mychart.deployment.container" -}} -{{- $fullname := include "common.fullname" . -}} -env: -- {{ template "common.envvar.value" (list "ZEUS" "cat") }} -- {{ template "common.envvar.secret" (list "ATHENA" "secret-name" "athena") }} -{{- end -}} -``` - -Output: - -```yaml -... - spec: - containers: - - env: - - name: ZEUS - value: cat - - name: ATHENA - valueFrom: - secretKeyRef: - key: athena - name: secret-name -... -``` - -### Volume - -Use the Volume helpers within a `Deployment` spec to help define ConfigMap and -PersistentVolumeClaim volumes. - -Example Use: - -```yaml -{{- template "common.deployment" (list . "mychart.deployment") -}} -{{- define "mychart.deployment" -}} -spec: - template: - spec: - volumes: - - {{ template "common.volume.configMap" (list "config" "configmap-name") }} - - {{ template "common.volume.pvc" (list "data" "pvc-name" .Values.persistence) }} -{{- end -}} -``` - -Output: - -```yaml -... - spec: - volumes: - - configMap: - name: configmap-name - name: config - - name: data - persistentVolumeClaim: - claimName: pvc-name -... -``` - -The `common.volume.pvc` helper uses the following configuration from the `.Values.persistence` object: - -| Value | Description | -| ------------------------- | ----------------------------------------------------- | -| persistence.enabled | If false, creates an `emptyDir` instead | -| persistence.existingClaim | If set, uses this instead of the passed in claim name | - -## Utilities - -### `common.fullname` - -The `common.fullname` template generates a name suitable for the `name:` field -in Kubernetes metadata. It is used like this: - -```yaml -name: {{ template "common.fullname" . }} -``` - -The following different values can influence it: - -```yaml -# By default, fullname uses '{{ .Release.Name }}-{{ .Chart.Name }}'. This -# overrides that and uses the given string instead. -fullnameOverride: "some-name" - -# This adds a prefix -fullnamePrefix: "pre-" -# This appends a suffix -fullnameSuffix: "-suf" - -# Global versions of the above -global: - fullnamePrefix: "pp-" - fullnameSuffix: "-ps" -``` - -Example output: - -```yaml ---- -# with the values above -name: pp-pre-some-name-suf-ps - ---- -# the default, for release "happy-panda" and chart "wordpress" -name: happy-panda-wordpress -``` - -Output of this function is truncated at 54 characters, which leaves 9 additional -characters for customized overriding. Thus you can easily extend this name -in your own charts: - -```yaml -{{- define "my.fullname" -}} - {{ template "common.fullname" . }}-my-stuff -{{- end -}} -``` - -### `common.fullname.unique` - -The `common.fullname.unique` variant of fullname appends a unique seven-character -sequence to the end of the common name field. - -This takes all of the same parameters as `common.fullname` - -Example template: - -```yaml -uniqueName: {{ template "common.fullname.unique" . }} -``` - -Example output: - -```yaml -uniqueName: release-name-fullname-jl0dbwx -``` - -It is also impacted by the prefix and suffix definitions, as well as by -`.Values.fullnameOverride` - -Note that the effective maximum length of this function is 63 characters, not 54. - -### `common.name` - -The `common.name` template generates a name suitable for the `app` label. It is used like this: - -```yaml -app: {{ template "common.name" . }} -``` - -The following different values can influence it: - -```yaml -# By default, name uses '{{ .Chart.Name }}'. This -# overrides that and uses the given string instead. -nameOverride: "some-name" - -# This adds a prefix -namePrefix: "pre-" -# This appends a suffix -nameSuffix: "-suf" - -# Global versions of the above -global: - namePrefix: "pp-" - nameSuffix: "-ps" -``` - -Example output: - -```yaml ---- -# with the values above -name: pp-pre-some-name-suf-ps - ---- -# the default, for chart "wordpress" -name: wordpress -``` - -Output of this function is truncated at 54 characters, which leaves 9 additional -characters for customized overriding. Thus you can easily extend this name -in your own charts: - -```yaml -{{- define "my.name" -}} - {{ template "common.name" . }}-my-stuff -{{- end -}} -``` - -### `common.metadata` - -The `common.metadata` helper generates the `metadata:` section of a Kubernetes -resource. - -This takes three objects: - - .top: top context - - .fullnameOverride: override the fullname with this name - - .metadata - - .labels: key/value list of labels - - .annotations: key/value list of annotations - - .hook: name(s) of hook(s) - -It generates standard labels, annotations, hooks, and a name field. - -Example template: - -```yaml -{{ template "common.metadata" (dict "top" . "metadata" .Values.bio) }} ---- -{{ template "common.metadata" (dict "top" . "metadata" .Values.pet "fullnameOverride" .Values.pet.fullnameOverride) }} -``` - -Example values: - -```yaml -bio: - name: example - labels: - first: matt - last: butcher - nick: technosophos - annotations: - format: bio - destination: archive - hook: pre-install - -pet: - fullnameOverride: Zeus - -``` - -Example output: - -```yaml -metadata: - name: release-name-metadata - labels: - app: metadata - heritage: "Tiller" - release: "RELEASE-NAME" - chart: metadata-0.1.0 - first: "matt" - last: "butcher" - nick: "technosophos" - annotations: - "destination": "archive" - "format": "bio" - "helm.sh/hook": "pre-install" ---- -metadata: - name: Zeus - labels: - app: metadata - heritage: "Tiller" - release: "RELEASE-NAME" - chart: metadata-0.1.0 - annotations: -``` - -Most of the common templates that define a resource type (e.g. `common.configmap` -or `common.job`) use this to generate the metadata, which means they inherit -the same `labels`, `annotations`, `nameOverride`, and `hook` fields. - -### `common.labelize` - -`common.labelize` turns a map into a set of labels. - -Example template: - -```yaml -{{- $map := dict "first" "1" "second" "2" "third" "3" -}} -{{- template "common.labelize" $map -}} -``` - -Example output: - -```yaml -first: "1" -second: "2" -third: "3" -``` - -### `common.labels.standard` - -`common.labels.standard` prints the standard set of labels. - -Example usage: - -``` -{{ template "common.labels.standard" . }} -``` - -Example output: - -```yaml -app: labelizer -heritage: "Tiller" -release: "RELEASE-NAME" -chart: labelizer-0.1.0 -``` - -### `common.hook` - -The `common.hook` template is a convenience for defining hooks. - -Example template: - -```yaml -{{ template "common.hook" "pre-install,post-install" }} -``` - -Example output: - -```yaml -"helm.sh/hook": "pre-install,post-install" -``` - -### `common.chartref` - -The `common.chartref` helper prints the chart name and version, escaped to be -legal in a Kubernetes label field. - -Example template: - -```yaml -chartref: {{ template "common.chartref" . }} -``` - -For the chart `foo` with version `1.2.3-beta.55+1234`, this will render: - -```yaml -chartref: foo-1.2.3-beta.55_1234 -``` - -(Note that `+` is an illegal character in label values) diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_chartref.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_chartref.tpl deleted file mode 100755 index e6c1486..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_chartref.tpl +++ /dev/null @@ -1,14 +0,0 @@ -{{- /* -common.chartref prints a chart name and version. - -It does minimal escaping for use in Kubernetes labels. - -Example output: - - zookeeper-1.2.3 - wordpress-3.2.1_20170219 - -*/ -}} -{{- define "common.chartref" -}} - {{- replace "+" "_" .Chart.Version | printf "%s-%s" .Chart.Name -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_configmap.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_configmap.yaml deleted file mode 100755 index 03dbbf8..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_configmap.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- define "common.configmap.tpl" -}} -apiVersion: v1 -kind: ConfigMap -{{ template "common.metadata" . }} -data: {} -{{- end -}} -{{- define "common.configmap" -}} -{{- template "common.util.merge" (append . "common.configmap.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_container.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_container.yaml deleted file mode 100755 index 540eb0e..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_container.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- define "common.container.tpl" -}} -name: {{ .Chart.Name }} -image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" -imagePullPolicy: {{ .Values.image.pullPolicy }} -ports: -- name: http - containerPort: 80 -resources: -{{ toYaml .Values.resources | indent 2 }} -{{- end -}} -{{- define "common.container" -}} -{{- /* clear new line so indentation works correctly */ -}} -{{- println "" -}} -{{- include "common.util.merge" (append . "common.container.tpl") | indent 8 -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_deployment.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_deployment.yaml deleted file mode 100755 index c49dae3..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_deployment.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- define "common.deployment.tpl" -}} -apiVersion: extensions/v1beta1 -kind: Deployment -{{ template "common.metadata" . }} -spec: - template: - metadata: - labels: - app: {{ template "common.name" . }} - release: {{ .Release.Name | quote }} - spec: - containers: - - -{{ include "common.container.tpl" . | indent 8 }} -{{- end -}} -{{- define "common.deployment" -}} -{{- template "common.util.merge" (append . "common.deployment.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_envvar.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_envvar.tpl deleted file mode 100755 index 709251f..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_envvar.tpl +++ /dev/null @@ -1,31 +0,0 @@ -{{- define "common.envvar.value" -}} - {{- $name := index . 0 -}} - {{- $value := index . 1 -}} - - name: {{ $name }} - value: {{ default "" $value | quote }} -{{- end -}} - -{{- define "common.envvar.configmap" -}} - {{- $name := index . 0 -}} - {{- $configMapName := index . 1 -}} - {{- $configMapKey := index . 2 -}} - - name: {{ $name }} - valueFrom: - configMapKeyRef: - name: {{ $configMapName }} - key: {{ $configMapKey }} -{{- end -}} - -{{- define "common.envvar.secret" -}} - {{- $name := index . 0 -}} - {{- $secretName := index . 1 -}} - {{- $secretKey := index . 2 -}} - - name: {{ $name }} - valueFrom: - secretKeyRef: - name: {{ $secretName }} - key: {{ $secretKey }} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_fullname.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_fullname.tpl deleted file mode 100755 index 2da6cdf..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_fullname.tpl +++ /dev/null @@ -1,39 +0,0 @@ -{{- /* -fullname defines a suitably unique name for a resource by combining -the release name and the chart name. - -The prevailing wisdom is that names should only contain a-z, 0-9 plus dot (.) and dash (-), and should -not exceed 63 characters. - -Parameters: - -- .Values.fullnameOverride: Replaces the computed name with this given name -- .Values.fullnamePrefix: Prefix -- .Values.global.fullnamePrefix: Global prefix -- .Values.fullnameSuffix: Suffix -- .Values.global.fullnameSuffix: Global suffix - -The applied order is: "global prefix + prefix + name + suffix + global suffix" - -Usage: 'name: "{{- template "common.fullname" . -}}"' -*/ -}} -{{- define "common.fullname"}} - {{- $global := default (dict) .Values.global -}} - {{- $base := default (printf "%s-%s" .Release.Name .Chart.Name) .Values.fullnameOverride -}} - {{- $gpre := default "" $global.fullnamePrefix -}} - {{- $pre := default "" .Values.fullnamePrefix -}} - {{- $suf := default "" .Values.fullnameSuffix -}} - {{- $gsuf := default "" $global.fullnameSuffix -}} - {{- $name := print $gpre $pre $base $suf $gsuf -}} - {{- $name | lower | trunc 54 | trimSuffix "-" -}} -{{- end -}} - -{{- /* -common.fullname.unique adds a random suffix to the unique name. - -This takes the same parameters as common.fullname - -*/ -}} -{{- define "common.fullname.unique" -}} - {{ template "common.fullname" . }}-{{ randAlphaNum 7 | lower }} -{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_ingress.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_ingress.yaml deleted file mode 100755 index 522ab24..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_ingress.yaml +++ /dev/null @@ -1,27 +0,0 @@ -{{- define "common.ingress.tpl" -}} -apiVersion: extensions/v1beta1 -kind: Ingress -{{ template "common.metadata" . }} - {{- if .Values.ingress.annotations }} - annotations: - {{ include "common.annote" .Values.ingress.annotations | indent 4 }} - {{- end }} -spec: - rules: - {{- range $host := .Values.ingress.hosts }} - - host: {{ $host }} - http: - paths: - - path: / - backend: - serviceName: {{ template "common.fullname" $ }} - servicePort: 80 - {{- end }} - {{- if .Values.ingress.tls }} - tls: -{{ toYaml .Values.ingress.tls | indent 4 }} - {{- end -}} -{{- end -}} -{{- define "common.ingress" -}} -{{- template "common.util.merge" (append . "common.ingress.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata.yaml deleted file mode 100755 index f96ed09..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- /* -common.metadata creates a standard metadata header. -It creates a 'metadata:' section with name and labels. -*/ -}} -{{ define "common.metadata" -}} -metadata: - name: {{ template "common.fullname" . }} - labels: -{{ include "common.labels.standard" . | indent 4 -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_annotations.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_annotations.tpl deleted file mode 100755 index 0c3b61c..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_annotations.tpl +++ /dev/null @@ -1,18 +0,0 @@ -{{- /* -common.hook defines a hook. - -This is to be used in a 'metadata.annotations' section. - -This should be called as 'template "common.metadata.hook" "post-install"' - -Any valid hook may be passed in. Separate multiple hooks with a ",". -*/ -}} -{{- define "common.hook" -}} -"helm.sh/hook": {{printf "%s" . | quote}} -{{- end -}} - -{{- define "common.annote" -}} -{{- range $k, $v := . }} -{{ $k | quote }}: {{ $v | quote }} -{{- end -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_labels.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_labels.tpl deleted file mode 100755 index 15fe00c..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_labels.tpl +++ /dev/null @@ -1,28 +0,0 @@ -{{- /* -common.labelize takes a dict or map and generates labels. - -Values will be quoted. Keys will not. - -Example output: - - first: "Matt" - last: "Butcher" - -*/ -}} -{{- define "common.labelize" -}} -{{- range $k, $v := . }} -{{ $k }}: {{ $v | quote }} -{{- end -}} -{{- end -}} - -{{- /* -common.labels.standard prints the standard Helm labels. - -The standard labels are frequently used in metadata. -*/ -}} -{{- define "common.labels.standard" -}} -app: {{ template "common.name" . }} -chart: {{ template "common.chartref" . }} -heritage: {{ .Release.Service | quote }} -release: {{ .Release.Name | quote }} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_name.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_name.tpl deleted file mode 100755 index 1d42fb0..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_name.tpl +++ /dev/null @@ -1,29 +0,0 @@ -{{- /* -name defines a template for the name of the chart. It should be used for the `app` label. -This is common practice in many Kubernetes manifests, and is not Helm-specific. - -The prevailing wisdom is that names should only contain a-z, 0-9 plus dot (.) and dash (-), and should -not exceed 63 characters. - -Parameters: - -- .Values.nameOverride: Replaces the computed name with this given name -- .Values.namePrefix: Prefix -- .Values.global.namePrefix: Global prefix -- .Values.nameSuffix: Suffix -- .Values.global.nameSuffix: Global suffix - -The applied order is: "global prefix + prefix + name + suffix + global suffix" - -Usage: 'name: "{{- template "common.name" . -}}"' -*/ -}} -{{- define "common.name"}} - {{- $global := default (dict) .Values.global -}} - {{- $base := default .Chart.Name .Values.nameOverride -}} - {{- $gpre := default "" $global.namePrefix -}} - {{- $pre := default "" .Values.namePrefix -}} - {{- $suf := default "" .Values.nameSuffix -}} - {{- $gsuf := default "" $global.nameSuffix -}} - {{- $name := print $gpre $pre $base $suf $gsuf -}} - {{- $name | lower | trunc 54 | trimSuffix "-" -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_persistentvolumeclaim.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_persistentvolumeclaim.yaml deleted file mode 100755 index 6c1578c..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_persistentvolumeclaim.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- define "common.persistentvolumeclaim.tpl" -}} -apiVersion: v1 -kind: PersistentVolumeClaim -{{ template "common.metadata" . }} -spec: - accessModes: - - {{ .Values.persistence.accessMode | quote }} - resources: - requests: - storage: {{ .Values.persistence.size | quote }} -{{- if .Values.persistence.storageClass }} -{{- if (eq "-" .Values.persistence.storageClass) }} - storageClassName: "" -{{- else }} - storageClassName: "{{ .Values.persistence.storageClass }}" -{{- end }} -{{- end }} -{{- end -}} -{{- define "common.persistentvolumeclaim" -}} -{{- $top := first . -}} -{{- if and $top.Values.persistence.enabled (not $top.Values.persistence.existingClaim) -}} -{{- template "common.util.merge" (append . "common.persistentvolumeclaim.tpl") -}} -{{- end -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_secret.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_secret.yaml deleted file mode 100755 index 0615d35..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_secret.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- define "common.secret.tpl" -}} -apiVersion: v1 -kind: Secret -{{ template "common.metadata" . }} -type: Opaque -data: {} -{{- end -}} -{{- define "common.secret" -}} -{{- template "common.util.merge" (append . "common.secret.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_service.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_service.yaml deleted file mode 100755 index 6737952..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- define "common.service.tpl" -}} -apiVersion: v1 -kind: Service -{{ template "common.metadata" . }} -spec: - type: {{ .Values.service.type }} - ports: - - name: http - port: 80 - targetPort: http - selector: - app: {{ template "common.name" . }} - release: {{ .Release.Name | quote }} -{{- end -}} -{{- define "common.service" -}} -{{- template "common.util.merge" (append . "common.service.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_util.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_util.tpl deleted file mode 100755 index a7d4cc7..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_util.tpl +++ /dev/null @@ -1,15 +0,0 @@ -{{- /* -common.util.merge will merge two YAML templates and output the result. - -This takes an array of three values: -- the top context -- the template name of the overrides (destination) -- the template name of the base (source) - -*/ -}} -{{- define "common.util.merge" -}} -{{- $top := first . -}} -{{- $overrides := fromYaml (include (index . 1) $top) | default (dict ) -}} -{{- $tpl := fromYaml (include (index . 2) $top) | default (dict ) -}} -{{- toYaml (merge $overrides $tpl) -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_volume.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_volume.tpl deleted file mode 100755 index 521a1f4..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_volume.tpl +++ /dev/null @@ -1,22 +0,0 @@ -{{- define "common.volume.configMap" -}} - {{- $name := index . 0 -}} - {{- $configMapName := index . 1 -}} - - name: {{ $name }} - configMap: - name: {{ $configMapName }} -{{- end -}} - -{{- define "common.volume.pvc" -}} - {{- $name := index . 0 -}} - {{- $claimName := index . 1 -}} - {{- $persistence := index . 2 -}} - - name: {{ $name }} - {{- if $persistence.enabled }} - persistentVolumeClaim: - claimName: {{ $persistence.existingClaim | default $claimName }} - {{- else }} - emptyDir: {} - {{- end -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/configmap.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/configmap.yaml deleted file mode 100644 index b5bf1df..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/configmap.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: common-configmap -data: - myvalue: "Hello World" diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/values.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/values.yaml deleted file mode 100755 index b7cf514..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for commons. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/NOTES.txt deleted file mode 100644 index 8f6bb9b..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/NOTES.txt +++ /dev/null @@ -1,19 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range .Values.ingress.hosts }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "chart-with-template-lib-dep.fullname" . }}) - export NODE_IP=$(kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get svc -w {{ template "chart-with-template-lib-dep.fullname" . }}' - export SERVICE_IP=$(kubectl get svc {{ template "chart-with-template-lib-dep.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods -l "app={{ template "chart-with-template-lib-dep.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 -{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/_helpers.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/_helpers.tpl deleted file mode 100644 index 0ab7974..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/_helpers.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "chart-with-template-lib-dep.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "chart-with-template-lib-dep.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "chart-with-template-lib-dep.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/deployment.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/deployment.yaml deleted file mode 100644 index 6b950d1..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/deployment.yaml +++ /dev/null @@ -1,51 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ template "chart-with-template-lib-dep.fullname" . }} - labels: - app: {{ template "chart-with-template-lib-dep.name" . }} - chart: {{ template "chart-with-template-lib-dep.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - app: {{ template "chart-with-template-lib-dep.name" . }} - release: {{ .Release.Name }} - template: - metadata: - labels: - app: {{ template "chart-with-template-lib-dep.name" . }} - release: {{ .Release.Name }} - spec: - containers: - - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: -{{ toYaml .Values.resources | indent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: -{{ toYaml . | indent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: -{{ toYaml . | indent 8 }} - {{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/ingress.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/ingress.yaml deleted file mode 100644 index a978df4..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/ingress.yaml +++ /dev/null @@ -1,38 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "chart-with-template-lib-dep.fullname" . -}} -{{- $ingressPath := .Values.ingress.path -}} -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - app: {{ template "chart-with-template-lib-dep.name" . }} - chart: {{ template "chart-with-template-lib-dep.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} -{{- with .Values.ingress.annotations }} - annotations: -{{ toYaml . | indent 4 }} -{{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ . }} - http: - paths: - - path: {{ $ingressPath }} - backend: - serviceName: {{ $fullName }} - servicePort: http - {{- end }} -{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/service.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/service.yaml deleted file mode 100644 index d532bb3..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/service.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- template "common.service" (list . "chart-with-template-lib-dep.service") -}} -{{- define "chart-with-template-lib-dep.service" -}} -## Define overrides for your Service resource here, e.g. -# metadata: -# labels: -# custom: label -# spec: -# ports: -# - port: 8080 -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/values.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/values.yaml deleted file mode 100644 index d49955c..0000000 --- a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/values.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# Default values for chart-with-template-lib-dep. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent - -nameOverride: "" -fullnameOverride: "" - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - path: / - hosts: - - chart-example.local - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tar.gz b/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tar.gz deleted file mode 100644 index 3c9c24d..0000000 Binary files a/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tar.gz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tgz b/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tgz deleted file mode 100644 index 3c9c24d..0000000 Binary files a/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/compressedchart-0.2.0.tgz b/cmd/helm/testdata/testcharts/compressedchart-0.2.0.tgz deleted file mode 100644 index 16a644a..0000000 Binary files a/cmd/helm/testdata/testcharts/compressedchart-0.2.0.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/compressedchart-0.3.0.tgz b/cmd/helm/testdata/testcharts/compressedchart-0.3.0.tgz deleted file mode 100644 index 051bd6f..0000000 Binary files a/cmd/helm/testdata/testcharts/compressedchart-0.3.0.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/compressedchart-with-hyphens-0.1.0.tgz b/cmd/helm/testdata/testcharts/compressedchart-with-hyphens-0.1.0.tgz deleted file mode 100644 index 379210a..0000000 Binary files a/cmd/helm/testdata/testcharts/compressedchart-with-hyphens-0.1.0.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/corrupted-compressed-chart.tgz b/cmd/helm/testdata/testcharts/corrupted-compressed-chart.tgz deleted file mode 100644 index e69de29..0000000 diff --git a/cmd/helm/testdata/testcharts/decompressedchart/.helmignore b/cmd/helm/testdata/testcharts/decompressedchart/.helmignore deleted file mode 100644 index 435b756..0000000 --- a/cmd/helm/testdata/testcharts/decompressedchart/.helmignore +++ /dev/null @@ -1,5 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -.git diff --git a/cmd/helm/testdata/testcharts/decompressedchart/Chart.yaml b/cmd/helm/testdata/testcharts/decompressedchart/Chart.yaml deleted file mode 100644 index 92ba4d8..0000000 --- a/cmd/helm/testdata/testcharts/decompressedchart/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: decompressedchart -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/decompressedchart/values.yaml b/cmd/helm/testdata/testcharts/decompressedchart/values.yaml deleted file mode 100644 index a940d1f..0000000 --- a/cmd/helm/testdata/testcharts/decompressedchart/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for decompressedchart. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. - name: my-decompressed-chart diff --git a/cmd/helm/testdata/testcharts/deprecated/Chart.yaml b/cmd/helm/testdata/testcharts/deprecated/Chart.yaml deleted file mode 100644 index 10185be..0000000 --- a/cmd/helm/testdata/testcharts/deprecated/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: Deprecated testing chart -home: https://helm.sh/helm -name: deprecated -sources: - - https://github.com/helm/helm -version: 0.1.0 -deprecated: true diff --git a/cmd/helm/testdata/testcharts/deprecated/README.md b/cmd/helm/testdata/testcharts/deprecated/README.md deleted file mode 100644 index 0df9a8b..0000000 --- a/cmd/helm/testdata/testcharts/deprecated/README.md +++ /dev/null @@ -1,3 +0,0 @@ -#Deprecated - -This space intentionally left blank. diff --git a/cmd/helm/testdata/testcharts/empty/Chart.yaml b/cmd/helm/testdata/testcharts/empty/Chart.yaml deleted file mode 100644 index 4f1dc00..0000000 --- a/cmd/helm/testdata/testcharts/empty/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -description: Empty testing chart -home: https://helm.sh/helm -name: empty -sources: - - https://github.com/helm/helm -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/empty/README.md b/cmd/helm/testdata/testcharts/empty/README.md deleted file mode 100644 index ed73c17..0000000 --- a/cmd/helm/testdata/testcharts/empty/README.md +++ /dev/null @@ -1,3 +0,0 @@ -#Empty - -This space intentionally left blank. diff --git a/cmd/helm/testdata/testcharts/empty/templates/empty.yaml b/cmd/helm/testdata/testcharts/empty/templates/empty.yaml deleted file mode 100644 index c80812f..0000000 --- a/cmd/helm/testdata/testcharts/empty/templates/empty.yaml +++ /dev/null @@ -1 +0,0 @@ -# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/empty/values.yaml b/cmd/helm/testdata/testcharts/empty/values.yaml deleted file mode 100644 index 1f0ff00..0000000 --- a/cmd/helm/testdata/testcharts/empty/values.yaml +++ /dev/null @@ -1 +0,0 @@ -Name: my-empty diff --git a/cmd/helm/testdata/testcharts/issue1979/Chart.yaml b/cmd/helm/testdata/testcharts/issue1979/Chart.yaml deleted file mode 100644 index 5269b5c..0000000 --- a/cmd/helm/testdata/testcharts/issue1979/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -description: Deploy a basic Alpine Linux pod -home: https://helm.sh/helm -name: alpine -sources: - - https://github.com/helm/helm -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/issue1979/README.md b/cmd/helm/testdata/testcharts/issue1979/README.md deleted file mode 100644 index fcf7ee0..0000000 --- a/cmd/helm/testdata/testcharts/issue1979/README.md +++ /dev/null @@ -1,13 +0,0 @@ -#Alpine: A simple Helm chart - -Run a single pod of Alpine Linux. - -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.yaml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/cmd/helm/testdata/testcharts/issue1979/extra_values.yaml b/cmd/helm/testdata/testcharts/issue1979/extra_values.yaml deleted file mode 100644 index 468bbac..0000000 --- a/cmd/helm/testdata/testcharts/issue1979/extra_values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -test: - Name: extra-values diff --git a/cmd/helm/testdata/testcharts/issue1979/more_values.yaml b/cmd/helm/testdata/testcharts/issue1979/more_values.yaml deleted file mode 100644 index 3d21e1f..0000000 --- a/cmd/helm/testdata/testcharts/issue1979/more_values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -test: - Name: more-values diff --git a/cmd/helm/testdata/testcharts/issue1979/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/issue1979/templates/alpine-pod.yaml deleted file mode 100644 index 6f025fe..0000000 --- a/cmd/helm/testdata/testcharts/issue1979/templates/alpine-pod.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{.Release.Name}}-{{.Values.Name}}" - labels: - # The "app.kubernetes.io/managed-by" label is used to track which tool - # deployed a given chart. It is useful for admins who want to see what - # releases a particular tool is responsible for. - app.kubernetes.io/managed-by: {{.Release.Service | quote }} - # The "app.kubernetes.io/instance" convention makes it easy to tie a release - # to all of the Kubernetes resources that were created as part of that - # release. - app.kubernetes.io/instance: {{.Release.Name | quote }} - # This makes it easy to audit chart usage. - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" - values: {{.Values.test.Name}} -spec: - # This shows how to use a simple value. This will look for a passed-in value - # called restartPolicy. If it is not found, it will use the default value. - # {{default "Never" .restartPolicy}} is a slightly optimized version of the - # more conventional syntax: {{.restartPolicy | default "Never"}} - restartPolicy: {{default "Never" .Values.restartPolicy}} - containers: - - name: waiter - image: "alpine:3.9" - command: ["/bin/sleep","9000"] diff --git a/cmd/helm/testdata/testcharts/issue1979/values.yaml b/cmd/helm/testdata/testcharts/issue1979/values.yaml deleted file mode 100644 index 879d760..0000000 --- a/cmd/helm/testdata/testcharts/issue1979/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -Name: my-alpine diff --git a/cmd/helm/testdata/testcharts/lib-chart/.helmignore b/cmd/helm/testdata/testcharts/lib-chart/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/lib-chart/Chart.yaml b/cmd/helm/testdata/testcharts/lib-chart/Chart.yaml deleted file mode 100755 index 4dcddc8..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/Chart.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -description: Common chartbuilding components and helpers -name: lib-chart -version: 0.0.5 -appVersion: 0.0.5 -home: https://helm.sh -maintainers: -- name: technosophos - email: technosophos@gmail.com -- name: prydonius - email: adnan@bitnami.com -type: Library diff --git a/cmd/helm/testdata/testcharts/lib-chart/README.md b/cmd/helm/testdata/testcharts/lib-chart/README.md deleted file mode 100644 index aca2579..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/README.md +++ /dev/null @@ -1,831 +0,0 @@ -# Common: The Helm Helper Chart - -This chart is designed to make it easier for you to build and maintain Helm -charts. - -It provides utilities that reflect best practices of Kubernetes chart development, -making it faster for you to write charts. - -## Tips - -A few tips for working with Common: - -- Be careful when using functions that generate random data (like `common.fullname.unique`). - They may trigger unwanted upgrades or have other side effects. - -In this document, we use `RELEASE-NAME` as the name of the release. - -## Resource Kinds - -Kubernetes defines a variety of resource kinds, from `Secret` to `StatefulSet`. -We define some of the most common kinds in a way that lets you easily work with -them. - -The resource kind templates are designed to make it much faster for you to -define _basic_ versions of these resources. They allow you to extend and modify -just what you need, without having to copy around lots of boilerplate. - -To make use of these templates you must define a template that will extend the -base template (though it can be empty). The name of this template is then passed -to the base template, for example: - -```yaml -{{- template "common.service" (list . "mychart.service") -}} -{{- define "mychart.service" -}} -## Define overrides for your Service resource here, e.g. -# metadata: -# labels: -# custom: label -# spec: -# ports: -# - port: 8080 -{{- end -}} -``` - -Note that the `common.service` template defines two parameters: - - - The root context (usually `.`) - - A template name containing the service definition overrides - -A limitation of the Go template library is that a template can only take a -single argument. The `list` function is used to workaround this by constructing -a list or array of arguments that is passed to the template. - -The `common.service` template is responsible for rendering the templates with -the root context and merging any overrides. As you can see, this makes it very -easy to create a basic `Service` resource without having to copy around the -standard metadata and labels. - -Each implemented base resource is described in greater detail below. - -### `common.service` - -The `common.service` template creates a basic `Service` resource with the -following defaults: - -- Service type (ClusterIP, NodePort, LoadBalancer) made configurable by `.Values.service.type` -- Named port `http` configured on port 80 -- Selector set to `app.kubernetes.io/name: {{ template "common.name" }}, app.kubernetes.io/instance: {{ .Release.Name | quote }}` to match the default used in the `Deployment` resource - -Example template: - -```yaml -{{- template "common.service" (list . "mychart.mail.service") -}} -{{- define "mychart.mail.service" -}} -metadata: - name: {{ template "common.fullname" . }}-mail # overrides the default name to add a suffix - labels: # appended to the labels section - protocol: mail -spec: - ports: # composes the `ports` section of the service definition. - - name: smtp - port: 25 - targetPort: 25 - - name: imaps - port: 993 - targetPort: 993 - selector: # this is appended to the default selector - protocol: mail -{{- end -}} ---- -{{ template "common.service" (list . "mychart.web.service") -}} -{{- define "mychart.web.service" -}} -metadata: - name: {{ template "common.fullname" . }}-www # overrides the default name to add a suffix - labels: # appended to the labels section - protocol: www -spec: - ports: # composes the `ports` section of the service definition. - - name: www - port: 80 - targetPort: 8080 -{{- end -}} -``` - -The above template defines _two_ services: a web service and a mail service. - -The most important part of a service definition is the `ports` object, which -defines the ports that this service will listen on. Most of the time, -`selector` is computed for you. But you can replace it or add to it. - -The output of the example above is: - -```yaml -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/name: service - helm.sh/chart: service-0.1.0 - app.kubernetes.io/managed-by: Helm - protocol: mail - app.kubernetes.io/instance: release-name - name: release-name-service-mail -spec: - ports: - - name: smtp - port: 25 - targetPort: 25 - - name: imaps - port: 993 - targetPort: 993 - selector: - app.kubernetes.io/name: service - app.kubernetes.io/instance: release-name - protocol: mail - type: ClusterIP ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/name: service - helm.sh/chart: service-0.1.0 - app.kubernetes.io/managed-by: Helm - protocol: www - app.kubernetes.io/instance: release-name - name: release-name-service-www -spec: - ports: - - name: www - port: 80 - targetPort: 8080 - type: ClusterIP -``` - -## `common.deployment` - -The `common.deployment` template defines a basic `Deployment`. Underneath the -hood, it uses `common.container` (see next section). - -By default, the pod template within the deployment defines the labels `app: {{ template "common.name" . }}` -and `release: {{ .Release.Name | quote }` as this is also used as the selector. The -standard set of labels are not used as some of these can change during upgrades, -which causes the replica sets and pods to not correctly match. - -Example use: - -```yaml -{{- template "common.deployment" (list . "mychart.deployment") -}} -{{- define "mychart.deployment" -}} -## Define overrides for your Deployment resource here, e.g. -spec: - replicas: {{ .Values.replicaCount }} -{{- end -}} -``` - -## `common.container` - -The `common.container` template creates a basic `Container` spec to be used -within a `Deployment` or `ReplicaSet`. It holds the following defaults: - -- The name is set to the chart name -- Uses `.Values.image` to describe the image to run, with the following spec: - ```yaml - image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent - ``` -- Exposes the named port `http` as port 80 -- Lays out the compute resources using `.Values.resources` - -Example use: - -```yaml -{{- template "common.deployment" (list . "mychart.deployment") -}} -{{- define "mychart.deployment" -}} -## Define overrides for your Deployment resource here, e.g. -spec: - template: - spec: - containers: - - {{ template "common.container" (list . "mychart.deployment.container") }} -{{- end -}} -{{- define "mychart.deployment.container" -}} -## Define overrides for your Container here, e.g. -livenessProbe: - httpGet: - path: / - port: 80 -readinessProbe: - httpGet: - path: / - port: 80 -{{- end -}} -``` - -The above example creates a `Deployment` resource which makes use of the -`common.container` template to populate the PodSpec's container list. The usage -of this template is similar to the other resources, you must define and -reference a template that contains overrides for the container object. - -The most important part of a container definition is the image you want to run. -As mentioned above, this is derived from `.Values.image` by default. It is a -best practice to define the image, tag and pull policy in your charts' values as -this makes it easy for an operator to change the image registry, or use a -specific tag or version. Another example of configuration that should be exposed -to chart operators is the container's required compute resources, as this is -also very specific to an operators environment. An example `values.yaml` for -your chart could look like: - -```yaml -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent -resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi -``` - -The output of running the above values through the earlier template is: - -```yaml -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/name: deployment - helm.sh/chart: deployment-0.1.0 - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/instance: release-name - name: release-name-deployment -spec: - template: - metadata: - labels: - app.kubernetes.io/name: deployment - spec: - containers: - - image: nginx:stable - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: / - port: 80 - name: deployment - ports: - - containerPort: 80 - name: http - readinessProbe: - httpGet: - path: / - port: 80 - resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi -``` - -## `common.configmap` - -The `common.configmap` template creates an empty `ConfigMap` resource that you -can override with your configuration. - -Example use: - -```yaml -{{- template "common.configmap" (list . "mychart.configmap") -}} -{{- define "mychart.configmap" -}} -data: - zeus: cat - athena: cat - julius: cat - one: |- - {{ .Files.Get "file1.txt" }} -{{- end -}} -``` - -Output: - -```yaml -apiVersion: v1 -data: - athena: cat - julius: cat - one: This is a file. - zeus: cat -kind: ConfigMap -metadata: - labels: - app.kubernetes.io/name: configmap - helm.sh/chart: configmap-0.1.0 - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/instance: release-name - name: release-name-configmap -``` - -## `common.secret` - -The `common.secret` template creates an empty `Secret` resource that you -can override with your secrets. - -Example use: - -```yaml -{{- template "common.secret" (list . "mychart.secret") -}} -{{- define "mychart.secret" -}} -data: - zeus: {{ print "cat" | b64enc }} - athena: {{ print "cat" | b64enc }} - julius: {{ print "cat" | b64enc }} - one: |- - {{ .Files.Get "file1.txt" | b64enc }} -{{- end -}} -``` - -Output: - -```yaml -apiVersion: v1 -data: - athena: Y2F0 - julius: Y2F0 - one: VGhpcyBpcyBhIGZpbGUuCg== - zeus: Y2F0 -kind: Secret -metadata: - labels: - app.kubernetes.io/name: secret - helm.sh/chart: secret-0.1.0 - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/instance: release-name - name: release-name-secret -type: Opaque -``` - -## `common.ingress` - -The `common.ingress` template is designed to give you a well-defined `Ingress` -resource, that can be configured using `.Values.ingress`. An example values file -that can be used to configure the `Ingress` resource is: - -```yaml -ingress: - hosts: - - chart-example.local - annotations: - kubernetes.io/ingress.class: nginx - kubernetes.io/tls-acme: "true" - tls: - - secretName: chart-example-tls - hosts: - - chart-example.local -``` - -Example use: - -```yaml -{{- template "common.ingress" (list . "mychart.ingress") -}} -{{- define "mychart.ingress" -}} -{{- end -}} -``` - -Output: - -```yaml -apiVersion: extensions/v1beta1 -kind: Ingress -metadata: - annotations: - kubernetes.io/ingress.class: nginx - kubernetes.io/tls-acme: "true" - labels: - app.kubernetes.io/name: ingress - helm.sh/chart: ingress-0.1.0 - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/instance: release-name - name: release-name-ingress -spec: - rules: - - host: chart-example.local - http: - paths: - - backend: - serviceName: release-name-ingress - servicePort: 80 - path: / - tls: - - hosts: - - chart-example.local - secretName: chart-example-tls -``` - -## `common.persistentvolumeclaim` - -`common.persistentvolumeclaim` can be used to easily add a -`PersistentVolumeClaim` resource to your chart that can be configured using -`.Values.persistence`: - -| Value | Description | -| ------------------------- | ------------------------------------------------------------------------------------------------------- | -| persistence.enabled | Whether or not to claim a persistent volume. If false, `common.volume.pvc` will use an emptyDir instead | -| persistence.storageClass | `StorageClass` name | -| persistence.accessMode | Access mode for persistent volume | -| persistence.size | Size of persistent volume | -| persistence.existingClaim | If defined, `PersistentVolumeClaim` is not created and `common.volume.pvc` helper uses this claim | - -An example values file that can be used to configure the -`PersistentVolumeClaim` resource is: - -```yaml -persistence: - enabled: true - storageClass: fast - accessMode: ReadWriteOnce - size: 8Gi -``` - -Example use: - -```yaml -{{- template "common.persistentvolumeclaim" (list . "mychart.persistentvolumeclaim") -}} -{{- define "mychart.persistentvolumeclaim" -}} -{{- end -}} -``` - -Output: - -```yaml -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - labels: - app.kubernetes.io/name: persistentvolumeclaim - helm.sh/chart: persistentvolumeclaim-0.1.0 - app.kubernetes.io/managed-by: Helm - app.kubernetes.io/instance: release-name - name: release-name-persistentvolumeclaim -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 8Gi - storageClassName: "fast" -``` - -## Partial API Objects - -When writing Kubernetes resources, you may find the following helpers useful to -construct parts of the spec. - -### EnvVar - -Use the EnvVar helpers within a container spec to simplify specifying key-value -environment variables or referencing secrets as values. - -Example Use: - -```yaml -{{- template "common.deployment" (list . "mychart.deployment") -}} -{{- define "mychart.deployment" -}} -spec: - template: - spec: - containers: - - {{ template "common.container" (list . "mychart.deployment.container") }} -{{- end -}} -{{- define "mychart.deployment.container" -}} -{{- $fullname := include "common.fullname" . -}} -env: -- {{ template "common.envvar.value" (list "ZEUS" "cat") }} -- {{ template "common.envvar.secret" (list "ATHENA" "secret-name" "athena") }} -{{- end -}} -``` - -Output: - -```yaml -... - spec: - containers: - - env: - - name: ZEUS - value: cat - - name: ATHENA - valueFrom: - secretKeyRef: - key: athena - name: secret-name -... -``` - -### Volume - -Use the Volume helpers within a `Deployment` spec to help define ConfigMap and -PersistentVolumeClaim volumes. - -Example Use: - -```yaml -{{- template "common.deployment" (list . "mychart.deployment") -}} -{{- define "mychart.deployment" -}} -spec: - template: - spec: - volumes: - - {{ template "common.volume.configMap" (list "config" "configmap-name") }} - - {{ template "common.volume.pvc" (list "data" "pvc-name" .Values.persistence) }} -{{- end -}} -``` - -Output: - -```yaml -... - spec: - volumes: - - configMap: - name: configmap-name - name: config - - name: data - persistentVolumeClaim: - claimName: pvc-name -... -``` - -The `common.volume.pvc` helper uses the following configuration from the `.Values.persistence` object: - -| Value | Description | -| ------------------------- | ----------------------------------------------------- | -| persistence.enabled | If false, creates an `emptyDir` instead | -| persistence.existingClaim | If set, uses this instead of the passed in claim name | - -## Utilities - -### `common.fullname` - -The `common.fullname` template generates a name suitable for the `name:` field -in Kubernetes metadata. It is used like this: - -```yaml -name: {{ template "common.fullname" . }} -``` - -The following different values can influence it: - -```yaml -# By default, fullname uses '{{ .Release.Name }}-{{ .Chart.Name }}'. This -# overrides that and uses the given string instead. -fullnameOverride: "some-name" - -# This adds a prefix -fullnamePrefix: "pre-" -# This appends a suffix -fullnameSuffix: "-suf" - -# Global versions of the above -global: - fullnamePrefix: "pp-" - fullnameSuffix: "-ps" -``` - -Example output: - -```yaml ---- -# with the values above -name: pp-pre-some-name-suf-ps - ---- -# the default, for release "happy-panda" and chart "wordpress" -name: happy-panda-wordpress -``` - -Output of this function is truncated at 54 characters, which leaves 9 additional -characters for customized overriding. Thus you can easily extend this name -in your own charts: - -```yaml -{{- define "my.fullname" -}} - {{ template "common.fullname" . }}-my-stuff -{{- end -}} -``` - -### `common.fullname.unique` - -The `common.fullname.unique` variant of fullname appends a unique seven-character -sequence to the end of the common name field. - -This takes all of the same parameters as `common.fullname` - -Example template: - -```yaml -uniqueName: {{ template "common.fullname.unique" . }} -``` - -Example output: - -```yaml -uniqueName: release-name-fullname-jl0dbwx -``` - -It is also impacted by the prefix and suffix definitions, as well as by -`.Values.fullnameOverride` - -Note that the effective maximum length of this function is 63 characters, not 54. - -### `common.name` - -The `common.name` template generates a name suitable for the `app` label. It is used like this: - -```yaml -app: {{ template "common.name" . }} -``` - -The following different values can influence it: - -```yaml -# By default, name uses '{{ .Chart.Name }}'. This -# overrides that and uses the given string instead. -nameOverride: "some-name" - -# This adds a prefix -namePrefix: "pre-" -# This appends a suffix -nameSuffix: "-suf" - -# Global versions of the above -global: - namePrefix: "pp-" - nameSuffix: "-ps" -``` - -Example output: - -```yaml ---- -# with the values above -name: pp-pre-some-name-suf-ps - ---- -# the default, for chart "wordpress" -name: wordpress -``` - -Output of this function is truncated at 54 characters, which leaves 9 additional -characters for customized overriding. Thus you can easily extend this name -in your own charts: - -```yaml -{{- define "my.name" -}} - {{ template "common.name" . }}-my-stuff -{{- end -}} -``` - -### `common.metadata` - -The `common.metadata` helper generates the `metadata:` section of a Kubernetes -resource. - -This takes three objects: - - .top: top context - - .fullnameOverride: override the fullname with this name - - .metadata - - .labels: key/value list of labels - - .annotations: key/value list of annotations - - .hook: name(s) of hook(s) - -It generates standard labels, annotations, hooks, and a name field. - -Example template: - -```yaml -{{ template "common.metadata" (dict "top" . "metadata" .Values.bio) }} ---- -{{ template "common.metadata" (dict "top" . "metadata" .Values.pet "fullnameOverride" .Values.pet.fullnameOverride) }} -``` - -Example values: - -```yaml -bio: - name: example - labels: - first: matt - last: butcher - nick: technosophos - annotations: - format: bio - destination: archive - hook: pre-install - -pet: - fullnameOverride: Zeus - -``` - -Example output: - -```yaml -metadata: - name: release-name-metadata - labels: - app.kubernetes.io/name: metadata - app.kubernetes.io/managed-by: "Helm" - app.kubernetes.io/instance: "RELEASE-NAME" - helm.sh/chart: metadata-0.1.0 - first: "matt" - last: "butcher" - nick: "technosophos" - annotations: - "destination": "archive" - "format": "bio" - "helm.sh/hook": "pre-install" ---- -metadata: - name: Zeus - labels: - app.kubernetes.io/name: metadata - app.kubernetes.io/managed-by: "Helm" - app.kubernetes.io/instance: "RELEASE-NAME" - helm.sh/chart: metadata-0.1.0 - annotations: -``` - -Most of the common templates that define a resource type (e.g. `common.configmap` -or `common.job`) use this to generate the metadata, which means they inherit -the same `labels`, `annotations`, `nameOverride`, and `hook` fields. - -### `common.labelize` - -`common.labelize` turns a map into a set of labels. - -Example template: - -```yaml -{{- $map := dict "first" "1" "second" "2" "third" "3" -}} -{{- template "common.labelize" $map -}} -``` - -Example output: - -```yaml -first: "1" -second: "2" -third: "3" -``` - -### `common.labels.standard` - -`common.labels.standard` prints the standard set of labels. - -Example usage: - -``` -{{ template "common.labels.standard" . }} -``` - -Example output: - -```yaml -app.kubernetes.io/name: labelizer -app.kubernetes.io/managed-by: "Tiller" -app.kubernetes.io/instance: "RELEASE-NAME" -helm.sh/chart: labelizer-0.1.0 -``` - -### `common.hook` - -The `common.hook` template is a convenience for defining hooks. - -Example template: - -```yaml -{{ template "common.hook" "pre-install,post-install" }} -``` - -Example output: - -```yaml -"helm.sh/hook": "pre-install,post-install" -``` - -### `common.chartref` - -The `common.chartref` helper prints the chart name and version, escaped to be -legal in a Kubernetes label field. - -Example template: - -```yaml -chartref: {{ template "common.chartref" . }} -``` - -For the chart `foo` with version `1.2.3-beta.55+1234`, this will render: - -```yaml -chartref: foo-1.2.3-beta.55_1234 -``` - -(Note that `+` is an illegal character in label values) diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_chartref.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_chartref.tpl deleted file mode 100644 index e6c1486..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_chartref.tpl +++ /dev/null @@ -1,14 +0,0 @@ -{{- /* -common.chartref prints a chart name and version. - -It does minimal escaping for use in Kubernetes labels. - -Example output: - - zookeeper-1.2.3 - wordpress-3.2.1_20170219 - -*/ -}} -{{- define "common.chartref" -}} - {{- replace "+" "_" .Chart.Version | printf "%s-%s" .Chart.Name -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_configmap.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_configmap.yaml deleted file mode 100644 index 03dbbf8..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_configmap.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- define "common.configmap.tpl" -}} -apiVersion: v1 -kind: ConfigMap -{{ template "common.metadata" . }} -data: {} -{{- end -}} -{{- define "common.configmap" -}} -{{- template "common.util.merge" (append . "common.configmap.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_container.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_container.yaml deleted file mode 100644 index 540eb0e..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_container.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- define "common.container.tpl" -}} -name: {{ .Chart.Name }} -image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" -imagePullPolicy: {{ .Values.image.pullPolicy }} -ports: -- name: http - containerPort: 80 -resources: -{{ toYaml .Values.resources | indent 2 }} -{{- end -}} -{{- define "common.container" -}} -{{- /* clear new line so indentation works correctly */ -}} -{{- println "" -}} -{{- include "common.util.merge" (append . "common.container.tpl") | indent 8 -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_deployment.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_deployment.yaml deleted file mode 100644 index e99a8cd..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_deployment.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- define "common.deployment.tpl" -}} -apiVersion: extensions/v1beta1 -kind: Deployment -{{ template "common.metadata" . }} -spec: - template: - metadata: - labels: - app.kubernetes.io/name: {{ template "common.name" . }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - spec: - containers: - - -{{ include "common.container.tpl" . | indent 8 }} -{{- end -}} -{{- define "common.deployment" -}} -{{- template "common.util.merge" (append . "common.deployment.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_envvar.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_envvar.tpl deleted file mode 100644 index 709251f..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_envvar.tpl +++ /dev/null @@ -1,31 +0,0 @@ -{{- define "common.envvar.value" -}} - {{- $name := index . 0 -}} - {{- $value := index . 1 -}} - - name: {{ $name }} - value: {{ default "" $value | quote }} -{{- end -}} - -{{- define "common.envvar.configmap" -}} - {{- $name := index . 0 -}} - {{- $configMapName := index . 1 -}} - {{- $configMapKey := index . 2 -}} - - name: {{ $name }} - valueFrom: - configMapKeyRef: - name: {{ $configMapName }} - key: {{ $configMapKey }} -{{- end -}} - -{{- define "common.envvar.secret" -}} - {{- $name := index . 0 -}} - {{- $secretName := index . 1 -}} - {{- $secretKey := index . 2 -}} - - name: {{ $name }} - valueFrom: - secretKeyRef: - name: {{ $secretName }} - key: {{ $secretKey }} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_fullname.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_fullname.tpl deleted file mode 100644 index 2da6cdf..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_fullname.tpl +++ /dev/null @@ -1,39 +0,0 @@ -{{- /* -fullname defines a suitably unique name for a resource by combining -the release name and the chart name. - -The prevailing wisdom is that names should only contain a-z, 0-9 plus dot (.) and dash (-), and should -not exceed 63 characters. - -Parameters: - -- .Values.fullnameOverride: Replaces the computed name with this given name -- .Values.fullnamePrefix: Prefix -- .Values.global.fullnamePrefix: Global prefix -- .Values.fullnameSuffix: Suffix -- .Values.global.fullnameSuffix: Global suffix - -The applied order is: "global prefix + prefix + name + suffix + global suffix" - -Usage: 'name: "{{- template "common.fullname" . -}}"' -*/ -}} -{{- define "common.fullname"}} - {{- $global := default (dict) .Values.global -}} - {{- $base := default (printf "%s-%s" .Release.Name .Chart.Name) .Values.fullnameOverride -}} - {{- $gpre := default "" $global.fullnamePrefix -}} - {{- $pre := default "" .Values.fullnamePrefix -}} - {{- $suf := default "" .Values.fullnameSuffix -}} - {{- $gsuf := default "" $global.fullnameSuffix -}} - {{- $name := print $gpre $pre $base $suf $gsuf -}} - {{- $name | lower | trunc 54 | trimSuffix "-" -}} -{{- end -}} - -{{- /* -common.fullname.unique adds a random suffix to the unique name. - -This takes the same parameters as common.fullname - -*/ -}} -{{- define "common.fullname.unique" -}} - {{ template "common.fullname" . }}-{{ randAlphaNum 7 | lower }} -{{- end }} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_ingress.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_ingress.yaml deleted file mode 100644 index 522ab24..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_ingress.yaml +++ /dev/null @@ -1,27 +0,0 @@ -{{- define "common.ingress.tpl" -}} -apiVersion: extensions/v1beta1 -kind: Ingress -{{ template "common.metadata" . }} - {{- if .Values.ingress.annotations }} - annotations: - {{ include "common.annote" .Values.ingress.annotations | indent 4 }} - {{- end }} -spec: - rules: - {{- range $host := .Values.ingress.hosts }} - - host: {{ $host }} - http: - paths: - - path: / - backend: - serviceName: {{ template "common.fullname" $ }} - servicePort: 80 - {{- end }} - {{- if .Values.ingress.tls }} - tls: -{{ toYaml .Values.ingress.tls | indent 4 }} - {{- end -}} -{{- end -}} -{{- define "common.ingress" -}} -{{- template "common.util.merge" (append . "common.ingress.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata.yaml deleted file mode 100644 index f96ed09..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- /* -common.metadata creates a standard metadata header. -It creates a 'metadata:' section with name and labels. -*/ -}} -{{ define "common.metadata" -}} -metadata: - name: {{ template "common.fullname" . }} - labels: -{{ include "common.labels.standard" . | indent 4 -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata_annotations.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata_annotations.tpl deleted file mode 100644 index 0c3b61c..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata_annotations.tpl +++ /dev/null @@ -1,18 +0,0 @@ -{{- /* -common.hook defines a hook. - -This is to be used in a 'metadata.annotations' section. - -This should be called as 'template "common.metadata.hook" "post-install"' - -Any valid hook may be passed in. Separate multiple hooks with a ",". -*/ -}} -{{- define "common.hook" -}} -"helm.sh/hook": {{printf "%s" . | quote}} -{{- end -}} - -{{- define "common.annote" -}} -{{- range $k, $v := . }} -{{ $k | quote }}: {{ $v | quote }} -{{- end -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata_labels.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata_labels.tpl deleted file mode 100644 index bcb8cda..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata_labels.tpl +++ /dev/null @@ -1,28 +0,0 @@ -{{- /* -common.labelize takes a dict or map and generates labels. - -Values will be quoted. Keys will not. - -Example output: - - first: "Matt" - last: "Butcher" - -*/ -}} -{{- define "common.labelize" -}} -{{- range $k, $v := . }} -{{ $k }}: {{ $v | quote }} -{{- end -}} -{{- end -}} - -{{- /* -common.labels.standard prints the standard Helm labels. - -The standard labels are frequently used in metadata. -*/ -}} -{{- define "common.labels.standard" -}} -app.kubernetes.io/name: {{ template "common.name" . }} -helm.sh/chart: {{ template "common.chartref" . }} -app.kubernetes.io/managed-by: {{ .Release.Service | quote }} -app.kubernetes.io/instance: {{ .Release.Name | quote }} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_name.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_name.tpl deleted file mode 100644 index 1d42fb0..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_name.tpl +++ /dev/null @@ -1,29 +0,0 @@ -{{- /* -name defines a template for the name of the chart. It should be used for the `app` label. -This is common practice in many Kubernetes manifests, and is not Helm-specific. - -The prevailing wisdom is that names should only contain a-z, 0-9 plus dot (.) and dash (-), and should -not exceed 63 characters. - -Parameters: - -- .Values.nameOverride: Replaces the computed name with this given name -- .Values.namePrefix: Prefix -- .Values.global.namePrefix: Global prefix -- .Values.nameSuffix: Suffix -- .Values.global.nameSuffix: Global suffix - -The applied order is: "global prefix + prefix + name + suffix + global suffix" - -Usage: 'name: "{{- template "common.name" . -}}"' -*/ -}} -{{- define "common.name"}} - {{- $global := default (dict) .Values.global -}} - {{- $base := default .Chart.Name .Values.nameOverride -}} - {{- $gpre := default "" $global.namePrefix -}} - {{- $pre := default "" .Values.namePrefix -}} - {{- $suf := default "" .Values.nameSuffix -}} - {{- $gsuf := default "" $global.nameSuffix -}} - {{- $name := print $gpre $pre $base $suf $gsuf -}} - {{- $name | lower | trunc 54 | trimSuffix "-" -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_persistentvolumeclaim.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_persistentvolumeclaim.yaml deleted file mode 100644 index 6c1578c..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_persistentvolumeclaim.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- define "common.persistentvolumeclaim.tpl" -}} -apiVersion: v1 -kind: PersistentVolumeClaim -{{ template "common.metadata" . }} -spec: - accessModes: - - {{ .Values.persistence.accessMode | quote }} - resources: - requests: - storage: {{ .Values.persistence.size | quote }} -{{- if .Values.persistence.storageClass }} -{{- if (eq "-" .Values.persistence.storageClass) }} - storageClassName: "" -{{- else }} - storageClassName: "{{ .Values.persistence.storageClass }}" -{{- end }} -{{- end }} -{{- end -}} -{{- define "common.persistentvolumeclaim" -}} -{{- $top := first . -}} -{{- if and $top.Values.persistence.enabled (not $top.Values.persistence.existingClaim) -}} -{{- template "common.util.merge" (append . "common.persistentvolumeclaim.tpl") -}} -{{- end -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_secret.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_secret.yaml deleted file mode 100644 index 0615d35..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_secret.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- define "common.secret.tpl" -}} -apiVersion: v1 -kind: Secret -{{ template "common.metadata" . }} -type: Opaque -data: {} -{{- end -}} -{{- define "common.secret" -}} -{{- template "common.util.merge" (append . "common.secret.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_service.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_service.yaml deleted file mode 100644 index b9dfc37..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- define "common.service.tpl" -}} -apiVersion: v1 -kind: Service -{{ template "common.metadata" . }} -spec: - type: {{ .Values.service.type }} - ports: - - name: http - port: 80 - targetPort: http - selector: - app.kubernetes.io/name: {{ template "common.name" . }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} -{{- end -}} -{{- define "common.service" -}} -{{- template "common.util.merge" (append . "common.service.tpl") -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_util.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_util.tpl deleted file mode 100644 index a7d4cc7..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_util.tpl +++ /dev/null @@ -1,15 +0,0 @@ -{{- /* -common.util.merge will merge two YAML templates and output the result. - -This takes an array of three values: -- the top context -- the template name of the overrides (destination) -- the template name of the base (source) - -*/ -}} -{{- define "common.util.merge" -}} -{{- $top := first . -}} -{{- $overrides := fromYaml (include (index . 1) $top) | default (dict ) -}} -{{- $tpl := fromYaml (include (index . 2) $top) | default (dict ) -}} -{{- toYaml (merge $overrides $tpl) -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/templates/_volume.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_volume.tpl deleted file mode 100644 index 521a1f4..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/templates/_volume.tpl +++ /dev/null @@ -1,22 +0,0 @@ -{{- define "common.volume.configMap" -}} - {{- $name := index . 0 -}} - {{- $configMapName := index . 1 -}} - - name: {{ $name }} - configMap: - name: {{ $configMapName }} -{{- end -}} - -{{- define "common.volume.pvc" -}} - {{- $name := index . 0 -}} - {{- $claimName := index . 1 -}} - {{- $persistence := index . 2 -}} - - name: {{ $name }} - {{- if $persistence.enabled }} - persistentVolumeClaim: - claimName: {{ $persistence.existingClaim | default $claimName }} - {{- else }} - emptyDir: {} - {{- end -}} -{{- end -}} diff --git a/cmd/helm/testdata/testcharts/lib-chart/values.yaml b/cmd/helm/testdata/testcharts/lib-chart/values.yaml deleted file mode 100644 index b7cf514..0000000 --- a/cmd/helm/testdata/testcharts/lib-chart/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for commons. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/Chart.yaml b/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/Chart.yaml deleted file mode 100644 index 3b342ae..0000000 --- a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -name: multiplecharts-lint-chart-1 -version: 1 -icon: "" \ No newline at end of file diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/templates/configmap.yaml b/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/templates/configmap.yaml deleted file mode 100644 index 88ebf24..0000000 --- a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/templates/configmap.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -metadata: - name: multicharttest-chart1-configmap -data: - dat: | - {{ .Values.config | indent 4 }} diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/values.yaml b/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/values.yaml deleted file mode 100644 index aafb09e..0000000 --- a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1/values.yaml +++ /dev/null @@ -1 +0,0 @@ -config: "Test" \ No newline at end of file diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/Chart.yaml b/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/Chart.yaml deleted file mode 100644 index bd101d8..0000000 --- a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -name: multiplecharts-lint-chart-2 -version: 1 -icon: "" \ No newline at end of file diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/templates/configmap.yaml b/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/templates/configmap.yaml deleted file mode 100644 index 8484bfe..0000000 --- a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/templates/configmap.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -metadata: - name: multicharttest-chart2-configmap -data: - {{ toYaml .Values.config | indent 4 }} diff --git a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/values.yaml b/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/values.yaml deleted file mode 100644 index 9139f48..0000000 --- a/cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -config: - test: "Test" \ No newline at end of file diff --git a/cmd/helm/testdata/testcharts/novals/Chart.yaml b/cmd/helm/testdata/testcharts/novals/Chart.yaml deleted file mode 100644 index a428247..0000000 --- a/cmd/helm/testdata/testcharts/novals/Chart.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: Deploy a basic Alpine Linux pod -home: https://helm.sh/helm -name: novals -sources: - - https://github.com/helm/helm -version: 0.2.0 -appVersion: 3.3 diff --git a/cmd/helm/testdata/testcharts/novals/README.md b/cmd/helm/testdata/testcharts/novals/README.md deleted file mode 100644 index fcf7ee0..0000000 --- a/cmd/helm/testdata/testcharts/novals/README.md +++ /dev/null @@ -1,13 +0,0 @@ -#Alpine: A simple Helm chart - -Run a single pod of Alpine Linux. - -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.yaml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml deleted file mode 100644 index 96c92d6..0000000 --- a/cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{.Release.Name}}-{{.Values.Name}}" - labels: - # The "app.kubernetes.io/managed-by" label is used to track which tool - # deployed a given chart. It is useful for admins who want to see what - # releases a particular tool is responsible for. - app.kubernetes.io/managed-by: {{.Release.Service | quote }} - # The "app.kubernetes.io/instance" convention makes it easy to tie a release - # to all of the Kubernetes resources that were created as part of that - # release. - app.kubernetes.io/instance: {{.Release.Name | quote }} - app.kubernetes.io/version: {{ .Chart.AppVersion }} - # This makes it easy to audit chart usage. - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" - annotations: - "helm.sh/created": {{.Release.Time.Seconds | quote }} -spec: - # This shows how to use a simple value. This will look for a passed-in value - # called restartPolicy. If it is not found, it will use the default value. - # {{default "Never" .restartPolicy}} is a slightly optimized version of the - # more conventional syntax: {{.restartPolicy | default "Never"}} - restartPolicy: {{default "Never" .Values.restartPolicy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/cmd/helm/testdata/testcharts/object-order/Chart.yaml b/cmd/helm/testdata/testcharts/object-order/Chart.yaml deleted file mode 100644 index d2eb42f..0000000 --- a/cmd/helm/testdata/testcharts/object-order/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v2 -name: object-order -description: Test ordering of manifests in output -type: application -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/object-order/templates/01-a.yml b/cmd/helm/testdata/testcharts/object-order/templates/01-a.yml deleted file mode 100644 index 32aa4a4..0000000 --- a/cmd/helm/testdata/testcharts/object-order/templates/01-a.yml +++ /dev/null @@ -1,57 +0,0 @@ -# 1 -kind: NetworkPolicy -apiVersion: networking.k8s.io/v1 -metadata: - name: first -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 2 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: second -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 3 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: third -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 4 (Deployment should come after all NetworkPolicy manifests, since 'helm template' outputs in install order) -apiVersion: apps/v1 -kind: Deployment -metadata: - name: fourth -spec: - selector: - matchLabels: - pod: fourth - replicas: 1 - template: - metadata: - labels: - pod: fourth - spec: - containers: - - name: hello-world - image: gcr.io/google-samples/node-hello:1.0 diff --git a/cmd/helm/testdata/testcharts/object-order/templates/02-b.yml b/cmd/helm/testdata/testcharts/object-order/templates/02-b.yml deleted file mode 100644 index 895db8c..0000000 --- a/cmd/helm/testdata/testcharts/object-order/templates/02-b.yml +++ /dev/null @@ -1,143 +0,0 @@ -# 5 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: fifth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 6 (implementation detail: currently, 'helm template' outputs hook manifests last; and yes, NetworkPolicy won't make a reasonable hook, this is just a dummy unit test manifest) -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - annotations: - "helm.sh/hook": pre-install - name: sixth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 7 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: seventh -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 8 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: eighth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 9 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: ninth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 10 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: tenth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 11 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: eleventh -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 12 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: twelfth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 13 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: thirteenth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 14 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: fourteenth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress - ---- - -# 15 (11th object within 02-b.yml, in order to test `SplitManifests` which assigns `manifest-10` -# to this object which should then come *after* `manifest-9`) -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: fifteenth -spec: - podSelector: {} - policyTypes: - - Egress - - Ingress diff --git a/cmd/helm/testdata/testcharts/object-order/values.yaml b/cmd/helm/testdata/testcharts/object-order/values.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/cmd/helm/testdata/testcharts/pre-release-chart-0.1.0-alpha.tgz b/cmd/helm/testdata/testcharts/pre-release-chart-0.1.0-alpha.tgz deleted file mode 100644 index 5d5770f..0000000 Binary files a/cmd/helm/testdata/testcharts/pre-release-chart-0.1.0-alpha.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz b/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz deleted file mode 100644 index 5d8e46a..0000000 Binary files a/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/reqtest/.helmignore b/cmd/helm/testdata/testcharts/reqtest/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/reqtest/Chart.lock b/cmd/helm/testdata/testcharts/reqtest/Chart.lock deleted file mode 100755 index ab1ae8c..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/Chart.lock +++ /dev/null @@ -1,3 +0,0 @@ -dependencies: [] -digest: Not implemented -generated: 2016-09-13T17:25:17.593788787-06:00 diff --git a/cmd/helm/testdata/testcharts/reqtest/Chart.yaml b/cmd/helm/testdata/testcharts/reqtest/Chart.yaml deleted file mode 100644 index 07b6e2c..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/Chart.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: reqtest -version: 0.1.0 -dependencies: - - name: reqsubchart - version: 0.1.0 - repository: "https://example.com/charts" - - name: reqsubchart2 - version: 0.2.0 - repository: "https://example.com/charts" - - name: reqsubchart3 - version: ">=0.1.0" - repository: "https://example.com/charts" diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/.helmignore b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/Chart.yaml b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/Chart.yaml deleted file mode 100644 index 3561355..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: reqsubchart -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/values.yaml b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/values.yaml deleted file mode 100644 index 0f0b63f..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for reqsubchart. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/.helmignore b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/Chart.yaml b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/Chart.yaml deleted file mode 100644 index 5b92773..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: reqsubchart2 -version: 0.2.0 diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/values.yaml b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/values.yaml deleted file mode 100644 index 0f0b63f..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for reqsubchart. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart3-0.2.0.tgz b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart3-0.2.0.tgz deleted file mode 100644 index 37962b0..0000000 Binary files a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart3-0.2.0.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/reqtest/values.yaml b/cmd/helm/testdata/testcharts/reqtest/values.yaml deleted file mode 100644 index d57f76b..0000000 --- a/cmd/helm/testdata/testcharts/reqtest/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for reqtest. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz b/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz deleted file mode 100644 index c74e5b0..0000000 Binary files a/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz and /dev/null differ diff --git a/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz.prov b/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz.prov deleted file mode 100644 index d325bb2..0000000 --- a/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz.prov +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA512 - -apiVersion: v1 -description: A Helm chart for Kubernetes -name: signtest -version: 0.1.0 - -... -files: - signtest-0.1.0.tgz: sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55 ------BEGIN PGP SIGNATURE----- - -wsBcBAEBCgAQBQJcoosfCRCEO7+YH8GHYgAA220IALAs8T8NPgkcLvHu+5109cAN -BOCNPSZDNsqLZW/2Dc9cKoBG7Jen4Qad+i5l9351kqn3D9Gm6eRfAWcjfggRobV/ -9daZ19h0nl4O1muQNAkjvdgZt8MOP3+PB3I3/Tu2QCYjI579SLUmuXlcZR5BCFPR -PJy+e3QpV2PcdeU2KZLG4tjtlrq+3QC9ZHHEJLs+BVN9d46Dwo6CxJdHJrrrAkTw -M8MhA92vbiTTPRSCZI9x5qDAwJYhoq0oxLflpuL2tIlo3qVoCsaTSURwMESEHO32 -XwYG7BaVDMELWhAorBAGBGBwWFbJ1677qQ2gd9CN0COiVhekWlFRcnn60800r84= -=k9Y9 ------END PGP SIGNATURE----- \ No newline at end of file diff --git a/cmd/helm/testdata/testcharts/signtest/.helmignore b/cmd/helm/testdata/testcharts/signtest/.helmignore deleted file mode 100644 index 435b756..0000000 --- a/cmd/helm/testdata/testcharts/signtest/.helmignore +++ /dev/null @@ -1,5 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -.git diff --git a/cmd/helm/testdata/testcharts/signtest/Chart.yaml b/cmd/helm/testdata/testcharts/signtest/Chart.yaml deleted file mode 100644 index f1f7372..0000000 --- a/cmd/helm/testdata/testcharts/signtest/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: signtest -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/signtest/alpine/Chart.yaml b/cmd/helm/testdata/testcharts/signtest/alpine/Chart.yaml deleted file mode 100644 index eec2612..0000000 --- a/cmd/helm/testdata/testcharts/signtest/alpine/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -description: Deploy a basic Alpine Linux pod -home: https://helm.sh/helm -name: alpine -sources: -- https://github.com/helm/helm -version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/signtest/alpine/README.md b/cmd/helm/testdata/testcharts/signtest/alpine/README.md deleted file mode 100644 index 28bebae..0000000 --- a/cmd/helm/testdata/testcharts/signtest/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.yaml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/cmd/helm/testdata/testcharts/signtest/alpine/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/signtest/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 5bbae10..0000000 --- a/cmd/helm/testdata/testcharts/signtest/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/cmd/helm/testdata/testcharts/signtest/alpine/values.yaml b/cmd/helm/testdata/testcharts/signtest/alpine/values.yaml deleted file mode 100644 index bb6c06a..0000000 --- a/cmd/helm/testdata/testcharts/signtest/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: my-alpine diff --git a/cmd/helm/testdata/testcharts/signtest/templates/pod.yaml b/cmd/helm/testdata/testcharts/signtest/templates/pod.yaml deleted file mode 100644 index 9b00cca..0000000 --- a/cmd/helm/testdata/testcharts/signtest/templates/pod.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: signtest -spec: - restartPolicy: Never - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/cmd/helm/testdata/testcharts/signtest/values.yaml b/cmd/helm/testdata/testcharts/signtest/values.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/cmd/helm/testdata/testcharts/upgradetest/templates/configmap.yaml b/cmd/helm/testdata/testcharts/upgradetest/templates/configmap.yaml deleted file mode 100644 index b6b90ef..0000000 --- a/cmd/helm/testdata/testcharts/upgradetest/templates/configmap.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: "{{ .Release.Name }}-configmap" -data: - myvalue: "Hello World" - drink: {{ .Values.favoriteDrink }} \ No newline at end of file diff --git a/cmd/helm/testdata/testcharts/upgradetest/values.yaml b/cmd/helm/testdata/testcharts/upgradetest/values.yaml deleted file mode 100644 index c429f41..0000000 --- a/cmd/helm/testdata/testcharts/upgradetest/values.yaml +++ /dev/null @@ -1 +0,0 @@ -favoriteDrink: beer \ No newline at end of file diff --git a/cmd/helm/testdata/testplugin/plugin.yaml b/cmd/helm/testdata/testplugin/plugin.yaml deleted file mode 100644 index 890292c..0000000 --- a/cmd/helm/testdata/testplugin/plugin.yaml +++ /dev/null @@ -1,4 +0,0 @@ -name: testplugin -usage: "echo test" -description: "This echos test" -command: "echo test" diff --git a/cmd/helm/testdata/testserver/index.yaml b/cmd/helm/testdata/testserver/index.yaml deleted file mode 100644 index 9cde8e8..0000000 --- a/cmd/helm/testdata/testserver/index.yaml +++ /dev/null @@ -1 +0,0 @@ -apiVersion: v1 diff --git a/cmd/helm/testdata/testserver/repository/repositories.yaml b/cmd/helm/testdata/testserver/repository/repositories.yaml deleted file mode 100644 index 271301c..0000000 --- a/cmd/helm/testdata/testserver/repository/repositories.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -generated: 2016-10-04T13:50:02.87649685-06:00 -repositories: -- cache: "" - name: test - url: http://127.0.0.1:49216 diff --git a/cmd/helm/uninstall.go b/cmd/helm/uninstall.go deleted file mode 100644 index 85fa822..0000000 --- a/cmd/helm/uninstall.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "time" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" -) - -const uninstallDesc = ` -This command takes a release name and uninstalls the release. - -It removes all of the resources associated with the last release of the chart -as well as the release history, freeing it up for future use. - -Use the '--dry-run' flag to see which releases will be uninstalled without actually -uninstalling them. -` - -func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewUninstall(cfg) - - cmd := &cobra.Command{ - Use: "uninstall RELEASE_NAME [...]", - Aliases: []string{"del", "delete", "un"}, - SuggestFor: []string{"remove", "rm"}, - Short: "uninstall a release", - Long: uninstallDesc, - Args: require.MinimumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - for i := 0; i < len(args); i++ { - - res, err := client.Run(args[i]) - if err != nil { - return err - } - if res != nil && res.Info != "" { - fmt.Fprintln(out, res.Info) - } - - fmt.Fprintf(out, "release \"%s\" uninstalled\n", args[i]) - } - return nil - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) != 0 { - return nil, completion.BashCompDirectiveNoFileComp - } - return compListReleases(toComplete, cfg) - }) - - f := cmd.Flags() - f.BoolVar(&client.DryRun, "dry-run", false, "simulate a uninstall") - f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during uninstallation") - f.BoolVar(&client.KeepHistory, "keep-history", false, "remove all associated resources and mark the release as deleted, but retain the release history") - f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") - f.StringVar(&client.Description, "description", "", "add a custom description") - - return cmd -} diff --git a/cmd/helm/uninstall_test.go b/cmd/helm/uninstall_test.go deleted file mode 100644 index a349340..0000000 --- a/cmd/helm/uninstall_test.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" - - "helm.sh/helm/v3/pkg/release" -) - -func TestUninstall(t *testing.T) { - tests := []cmdTestCase{ - { - name: "basic uninstall", - cmd: "uninstall aeneas", - golden: "output/uninstall.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})}, - }, - { - name: "multiple uninstall", - cmd: "uninstall aeneas aeneas2", - golden: "output/uninstall-multiple.txt", - rels: []*release.Release{ - release.Mock(&release.MockReleaseOptions{Name: "aeneas"}), - release.Mock(&release.MockReleaseOptions{Name: "aeneas2"}), - }, - }, - { - name: "uninstall with timeout", - cmd: "uninstall aeneas --timeout 120s", - golden: "output/uninstall-timeout.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})}, - }, - { - name: "uninstall without hooks", - cmd: "uninstall aeneas --no-hooks", - golden: "output/uninstall-no-hooks.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})}, - }, - { - name: "keep history", - cmd: "uninstall aeneas --keep-history", - golden: "output/uninstall-keep-history.txt", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})}, - }, - { - name: "uninstall without release", - cmd: "uninstall", - golden: "output/uninstall-no-args.txt", - wantError: true, - }, - } - runTestCmd(t, tests) -} diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go deleted file mode 100644 index 6c967b7..0000000 --- a/cmd/helm/upgrade.go +++ /dev/null @@ -1,181 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "time" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/completion" - "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/cli/output" - "helm.sh/helm/v3/pkg/cli/values" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/storage/driver" -) - -const upgradeDesc = ` -This command upgrades a release to a new version of a chart. - -The upgrade arguments must be a release and chart. The chart -argument can be either: a chart reference('example/mariadb'), a path to a chart directory, -a packaged chart, or a fully qualified URL. For chart references, the latest -version will be specified unless the '--version' flag is set. - -To override values in a chart, use either the '--values' flag and pass in a file -or use the '--set' flag and pass configuration from the command line, to force string -values, use '--set-string'. In case a value is large and therefore -you want not to use neither '--values' nor '--set', use '--set-file' to read the -single large value from file. - -You can specify the '--values'/'-f' flag multiple times. The priority will be given to the -last (right-most) file specified. For example, if both myvalues.yaml and override.yaml -contained a key called 'Test', the value set in override.yaml would take precedence: - - $ helm upgrade -f myvalues.yaml -f override.yaml redis ./redis - -You can specify the '--set' flag multiple times. The priority will be given to the -last (right-most) set specified. For example, if both 'bar' and 'newbar' values are -set for a key called 'foo', the 'newbar' value would take precedence: - - $ helm upgrade --set foo=bar --set foo=newbar redis ./redis -` - -func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewUpgrade(cfg) - valueOpts := &values.Options{} - var outfmt output.Format - - cmd := &cobra.Command{ - Use: "upgrade [RELEASE] [CHART]", - Short: "upgrade a release", - Long: upgradeDesc, - Args: require.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - client.Namespace = settings.Namespace() - - if client.Version == "" && client.Devel { - debug("setting version to >0.0.0-0") - client.Version = ">0.0.0-0" - } - - vals, err := valueOpts.MergeValues(getter.All(settings)) - if err != nil { - return err - } - - chartPath, err := client.ChartPathOptions.LocateChart(args[1], settings) - if err != nil { - return err - } - - if client.Install { - // If a release does not exist, install it. If another error occurs during - // the check, ignore the error and continue with the upgrade. - histClient := action.NewHistory(cfg) - histClient.Max = 1 - if _, err := histClient.Run(args[0]); err == driver.ErrReleaseNotFound { - // Only print this to stdout for table output - if outfmt == output.Table { - fmt.Fprintf(out, "Release %q does not exist. Installing it now.\n", args[0]) - } - instClient := action.NewInstall(cfg) - instClient.ChartPathOptions = client.ChartPathOptions - instClient.DryRun = client.DryRun - instClient.DisableHooks = client.DisableHooks - instClient.Timeout = client.Timeout - instClient.Wait = client.Wait - instClient.Devel = client.Devel - instClient.Namespace = client.Namespace - instClient.Atomic = client.Atomic - - rel, err := runInstall(args, instClient, valueOpts, out) - if err != nil { - return err - } - return outfmt.Write(out, &statusPrinter{rel, settings.Debug}) - } - } - - // Check chart dependencies to make sure all are present in /charts - ch, err := loader.Load(chartPath) - if err != nil { - return err - } - if req := ch.Metadata.Dependencies; req != nil { - if err := action.CheckDependencies(ch, req); err != nil { - return err - } - } - - if ch.Metadata.Deprecated { - fmt.Fprintln(out, "WARNING: This chart is deprecated") - } - - rel, err := client.Run(args[0], ch, vals) - if err != nil { - return errors.Wrap(err, "UPGRADE FAILED") - } - - if outfmt == output.Table { - fmt.Fprintf(out, "Release %q has been upgraded. Happy Helming!\n", args[0]) - } - - return outfmt.Write(out, &statusPrinter{rel, settings.Debug}) - }, - } - - // Function providing dynamic auto-completion - completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { - if len(args) == 0 { - return compListReleases(toComplete, cfg) - } - if len(args) == 1 { - return compListCharts(toComplete, true) - } - return nil, completion.BashCompDirectiveNoFileComp - }) - - f := cmd.Flags() - f.BoolVarP(&client.Install, "install", "i", false, "if a release by this name doesn't already exist, run an install") - f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") - f.BoolVar(&client.DryRun, "dry-run", false, "simulate an upgrade") - f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") - f.MarkDeprecated("recreate-pods", "functionality will no longer be updated. Consult the documentation for other methods to recreate pods") - f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy") - f.BoolVar(&client.DisableHooks, "no-hooks", false, "disable pre/post upgrade hooks") - f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") - f.BoolVar(&client.ResetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart") - f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored") - f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout") - f.BoolVar(&client.Atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade. The --wait flag will be set automatically if --atomic is used") - f.IntVar(&client.MaxHistory, "history-max", 10, "limit the maximum number of revisions saved per release. Use 0 for no limit") - f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails") - f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") - f.StringVar(&client.Description, "description", "", "add a custom description") - addChartPathOptionsFlags(f, &client.ChartPathOptions) - addValueOptionsFlags(f, valueOpts) - bindOutputFlag(cmd, &outfmt) - - return cmd -} diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go deleted file mode 100644 index 3cecbe6..0000000 --- a/cmd/helm/upgrade_test.go +++ /dev/null @@ -1,264 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io/ioutil" - "path/filepath" - "strings" - "testing" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/release" -) - -func TestUpgradeCmd(t *testing.T) { - tmpChart := ensure.TempDir(t) - cfile := &chart.Chart{ - Metadata: &chart.Metadata{ - APIVersion: chart.APIVersionV1, - Name: "testUpgradeChart", - Description: "A Helm chart for Kubernetes", - Version: "0.1.0", - }, - } - chartPath := filepath.Join(tmpChart, cfile.Metadata.Name) - if err := chartutil.SaveDir(cfile, tmpChart); err != nil { - t.Fatalf("Error creating chart for upgrade: %v", err) - } - ch, err := loader.Load(chartPath) - if err != nil { - t.Fatalf("Error loading chart: %v", err) - } - _ = release.Mock(&release.MockReleaseOptions{ - Name: "funny-bunny", - Chart: ch, - }) - - // update chart version - cfile.Metadata.Version = "0.1.2" - - if err := chartutil.SaveDir(cfile, tmpChart); err != nil { - t.Fatalf("Error creating chart: %v", err) - } - ch, err = loader.Load(chartPath) - if err != nil { - t.Fatalf("Error loading updated chart: %v", err) - } - - // update chart version again - cfile.Metadata.Version = "0.1.3" - - if err := chartutil.SaveDir(cfile, tmpChart); err != nil { - t.Fatalf("Error creating chart: %v", err) - } - var ch2 *chart.Chart - ch2, err = loader.Load(chartPath) - if err != nil { - t.Fatalf("Error loading updated chart: %v", err) - } - - missingDepsPath := "testdata/testcharts/chart-missing-deps" - badDepsPath := "testdata/testcharts/chart-bad-requirements" - - relMock := func(n string, v int, ch *chart.Chart) *release.Release { - return release.Mock(&release.MockReleaseOptions{Name: n, Version: v, Chart: ch}) - } - - tests := []cmdTestCase{ - { - name: "upgrade a release", - cmd: fmt.Sprintf("upgrade funny-bunny '%s'", chartPath), - golden: "output/upgrade.txt", - rels: []*release.Release{relMock("funny-bunny", 2, ch)}, - }, - { - name: "upgrade a release with timeout", - cmd: fmt.Sprintf("upgrade funny-bunny --timeout 120s '%s'", chartPath), - golden: "output/upgrade-with-timeout.txt", - rels: []*release.Release{relMock("funny-bunny", 3, ch2)}, - }, - { - name: "upgrade a release with --reset-values", - cmd: fmt.Sprintf("upgrade funny-bunny --reset-values '%s'", chartPath), - golden: "output/upgrade-with-reset-values.txt", - rels: []*release.Release{relMock("funny-bunny", 4, ch2)}, - }, - { - name: "upgrade a release with --reuse-values", - cmd: fmt.Sprintf("upgrade funny-bunny --reuse-values '%s'", chartPath), - golden: "output/upgrade-with-reset-values2.txt", - rels: []*release.Release{relMock("funny-bunny", 5, ch2)}, - }, - { - name: "install a release with 'upgrade --install'", - cmd: fmt.Sprintf("upgrade zany-bunny -i '%s'", chartPath), - golden: "output/upgrade-with-install.txt", - rels: []*release.Release{relMock("zany-bunny", 1, ch)}, - }, - { - name: "install a release with 'upgrade --install' and timeout", - cmd: fmt.Sprintf("upgrade crazy-bunny -i --timeout 120s '%s'", chartPath), - golden: "output/upgrade-with-install-timeout.txt", - rels: []*release.Release{relMock("crazy-bunny", 1, ch)}, - }, - { - name: "upgrade a release with wait", - cmd: fmt.Sprintf("upgrade crazy-bunny --wait '%s'", chartPath), - golden: "output/upgrade-with-wait.txt", - rels: []*release.Release{relMock("crazy-bunny", 2, ch2)}, - }, - { - name: "upgrade a release with missing dependencies", - cmd: fmt.Sprintf("upgrade bonkers-bunny %s", missingDepsPath), - golden: "output/upgrade-with-missing-dependencies.txt", - wantError: true, - }, - { - name: "upgrade a release with bad dependencies", - cmd: fmt.Sprintf("upgrade bonkers-bunny '%s'", badDepsPath), - golden: "output/upgrade-with-bad-dependencies.txt", - wantError: true, - }, - } - runTestCmd(t, tests) -} - -func TestUpgradeWithValue(t *testing.T) { - releaseName := "funny-bunny-v2" - relMock, ch, chartPath := prepareMockRelease(releaseName, t) - - defer resetEnv()() - - store := storageFixture() - - store.Create(relMock(releaseName, 3, ch)) - - cmd := fmt.Sprintf("upgrade %s --set favoriteDrink=tea '%s'", releaseName, chartPath) - _, _, err := executeActionCommandC(store, cmd) - if err != nil { - t.Errorf("unexpected error, got '%v'", err) - } - - updatedRel, err := store.Get(releaseName, 4) - if err != nil { - t.Errorf("unexpected error, got '%v'", err) - } - - if !strings.Contains(updatedRel.Manifest, "drink: tea") { - t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest) - } - -} - -func TestUpgradeWithStringValue(t *testing.T) { - releaseName := "funny-bunny-v3" - relMock, ch, chartPath := prepareMockRelease(releaseName, t) - - defer resetEnv()() - - store := storageFixture() - - store.Create(relMock(releaseName, 3, ch)) - - cmd := fmt.Sprintf("upgrade %s --set-string favoriteDrink=coffee '%s'", releaseName, chartPath) - _, _, err := executeActionCommandC(store, cmd) - if err != nil { - t.Errorf("unexpected error, got '%v'", err) - } - - updatedRel, err := store.Get(releaseName, 4) - if err != nil { - t.Errorf("unexpected error, got '%v'", err) - } - - if !strings.Contains(updatedRel.Manifest, "drink: coffee") { - t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest) - } - -} - -func TestUpgradeWithValuesFile(t *testing.T) { - - releaseName := "funny-bunny-v4" - relMock, ch, chartPath := prepareMockRelease(releaseName, t) - - defer resetEnv()() - - store := storageFixture() - - store.Create(relMock(releaseName, 3, ch)) - - cmd := fmt.Sprintf("upgrade %s --values testdata/testcharts/upgradetest/values.yaml '%s'", releaseName, chartPath) - _, _, err := executeActionCommandC(store, cmd) - if err != nil { - t.Errorf("unexpected error, got '%v'", err) - } - - updatedRel, err := store.Get(releaseName, 4) - if err != nil { - t.Errorf("unexpected error, got '%v'", err) - } - - if !strings.Contains(updatedRel.Manifest, "drink: beer") { - t.Errorf("The value is not set correctly. manifest: %s", updatedRel.Manifest) - } - -} - -func prepareMockRelease(releaseName string, t *testing.T) (func(n string, v int, ch *chart.Chart) *release.Release, *chart.Chart, string) { - tmpChart := ensure.TempDir(t) - configmapData, err := ioutil.ReadFile("testdata/testcharts/upgradetest/templates/configmap.yaml") - if err != nil { - t.Fatalf("Error loading template yaml %v", err) - } - cfile := &chart.Chart{ - Metadata: &chart.Metadata{ - APIVersion: chart.APIVersionV1, - Name: "testUpgradeChart", - Description: "A Helm chart for Kubernetes", - Version: "0.1.0", - }, - Templates: []*chart.File{{Name: "templates/configmap.yaml", Data: configmapData}}, - } - chartPath := filepath.Join(tmpChart, cfile.Metadata.Name) - if err := chartutil.SaveDir(cfile, tmpChart); err != nil { - t.Fatalf("Error creating chart for upgrade: %v", err) - } - ch, err := loader.Load(chartPath) - if err != nil { - t.Fatalf("Error loading chart: %v", err) - } - _ = release.Mock(&release.MockReleaseOptions{ - Name: releaseName, - Chart: ch, - }) - - relMock := func(n string, v int, ch *chart.Chart) *release.Release { - return release.Mock(&release.MockReleaseOptions{Name: n, Version: v, Chart: ch}) - } - - return relMock, ch, chartPath -} - -func TestUpgradeOutputCompletion(t *testing.T) { - outputFlagCompletionTest(t, "upgrade") -} diff --git a/cmd/helm/verify.go b/cmd/helm/verify.go deleted file mode 100644 index d3ae517..0000000 --- a/cmd/helm/verify.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "io" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/action" -) - -const verifyDesc = ` -Verify that the given chart has a valid provenance file. - -Provenance files provide cryptographic verification that a chart has not been -tampered with, and was packaged by a trusted provider. - -This command can be used to verify a local chart. Several other commands provide -'--verify' flags that run the same validation. To generate a signed package, use -the 'helm package --sign' command. -` - -func newVerifyCmd(out io.Writer) *cobra.Command { - client := action.NewVerify() - - cmd := &cobra.Command{ - Use: "verify PATH", - Short: "verify that a chart at the given path has been signed and is valid", - Long: verifyDesc, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return client.Run(args[0]) - }, - } - - cmd.Flags().StringVar(&client.Keyring, "keyring", defaultKeyring(), "keyring containing public keys") - - return cmd -} diff --git a/cmd/helm/verify_test.go b/cmd/helm/verify_test.go deleted file mode 100644 index a70051f..0000000 --- a/cmd/helm/verify_test.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "runtime" - "testing" -) - -func TestVerifyCmd(t *testing.T) { - - statExe := "stat" - statPathMsg := "no such file or directory" - statFileMsg := statPathMsg - if runtime.GOOS == "windows" { - statExe = "FindFirstFile" - statPathMsg = "The system cannot find the path specified." - statFileMsg = "The system cannot find the file specified." - } - - tests := []struct { - name string - cmd string - expect string - wantError bool - }{ - { - name: "verify requires a chart", - cmd: "verify", - expect: "\"helm verify\" requires 1 argument\n\nUsage: helm verify PATH [flags]", - wantError: true, - }, - { - name: "verify requires that chart exists", - cmd: "verify no/such/file", - expect: fmt.Sprintf("%s no/such/file: %s", statExe, statPathMsg), - wantError: true, - }, - { - name: "verify requires that chart is not a directory", - cmd: "verify testdata/testcharts/signtest", - expect: "unpacked charts cannot be verified", - wantError: true, - }, - { - name: "verify requires that chart has prov file", - cmd: "verify testdata/testcharts/compressedchart-0.1.0.tgz", - expect: fmt.Sprintf("could not load provenance file testdata/testcharts/compressedchart-0.1.0.tgz.prov: %s testdata/testcharts/compressedchart-0.1.0.tgz.prov: %s", statExe, statFileMsg), - wantError: true, - }, - { - name: "verify validates a properly signed chart", - cmd: "verify testdata/testcharts/signtest-0.1.0.tgz --keyring testdata/helm-test-key.pub", - expect: "", - wantError: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, out, err := executeActionCommand(tt.cmd) - if tt.wantError { - if err == nil { - t.Errorf("Expected error, but got none: %q", out) - } - if err.Error() != tt.expect { - t.Errorf("Expected error %q, got %q", tt.expect, err) - } - return - } else if err != nil { - t.Errorf("Unexpected error: %s", err) - } - if out != tt.expect { - t.Errorf("Expected %q, got %q", tt.expect, out) - } - }) - } -} diff --git a/cmd/helm/version.go b/cmd/helm/version.go deleted file mode 100644 index 3067f72..0000000 --- a/cmd/helm/version.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "text/template" - - "github.com/spf13/cobra" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/internal/version" -) - -const versionDesc = ` -Show the version for Helm. - -This will print a representation the version of Helm. -The output will look something like this: - -version.BuildInfo{Version:"v2.0.0", GitCommit:"ff52399e51bb880526e9cd0ed8386f6433b74da1", GitTreeState:"clean"} - -- Version is the semantic version of the release. -- GitCommit is the SHA for the commit that this version was built from. -- GitTreeState is "clean" if there are no local code changes when this binary was - built, and "dirty" if the binary was built from locally modified code. -` - -type versionOptions struct { - short bool - template string -} - -func newVersionCmd(out io.Writer) *cobra.Command { - o := &versionOptions{} - - cmd := &cobra.Command{ - Use: "version", - Short: "print the client version information", - Long: versionDesc, - Args: require.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out) - }, - } - f := cmd.Flags() - f.BoolVar(&o.short, "short", false, "print the version number") - f.StringVar(&o.template, "template", "", "template for version string format") - f.BoolP("client", "c", true, "display client version information") - f.MarkHidden("client") - - return cmd -} - -func (o *versionOptions) run(out io.Writer) error { - if o.template != "" { - tt, err := template.New("_").Parse(o.template) - if err != nil { - return err - } - return tt.Execute(out, version.Get()) - } - fmt.Fprintln(out, formatVersion(o.short)) - return nil -} - -func formatVersion(short bool) string { - v := version.Get() - if short { - if len(v.GitCommit) >= 7 { - return fmt.Sprintf("%s+g%s", v.Version, v.GitCommit[:7]) - } - return version.GetVersion() - } - return fmt.Sprintf("%#v", v) -} diff --git a/cmd/helm/version_test.go b/cmd/helm/version_test.go deleted file mode 100644 index 1344019..0000000 --- a/cmd/helm/version_test.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "testing" -) - -func TestVersion(t *testing.T) { - tests := []cmdTestCase{{ - name: "default", - cmd: "version", - golden: "output/version.txt", - }, { - name: "short", - cmd: "version --short", - golden: "output/version-short.txt", - }, { - name: "template", - cmd: "version --template='Version: {{.Version}}'", - golden: "output/version-template.txt", - }, { - name: "client", - cmd: "version --client", - golden: "output/version-client.txt", - }, { - name: "client shorthand", - cmd: "version -c", - golden: "output/version-client-shorthand.txt", - }} - runTestCmd(t, tests) -} diff --git a/cmd/service/service.go b/cmd/service/service.go index 8452e26..1462acd 100644 --- a/cmd/service/service.go +++ b/cmd/service/service.go @@ -6,10 +6,11 @@ import ( "github.com/gorilla/mux" + "albatross/pkg/api" + "albatross/pkg/api/logger" + "albatross/pkg/servercontext" + "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/api" - "helm.sh/helm/v3/pkg/api/logger" - "helm.sh/helm/v3/pkg/servercontext" ) func main() { diff --git a/code-of-conduct.md b/code-of-conduct.md deleted file mode 100644 index 91ccaf0..0000000 --- a/code-of-conduct.md +++ /dev/null @@ -1,3 +0,0 @@ -# Community Code of Conduct - -Helm follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). diff --git a/go.mod b/go.mod index 2ebd22d..8ce6aa2 100644 --- a/go.mod +++ b/go.mod @@ -1,85 +1,11 @@ -module helm.sh/helm/v3 +module albatross go 1.13 require ( - github.com/BurntSushi/toml v0.3.1 - github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect - github.com/Masterminds/semver/v3 v3.0.3 - github.com/Masterminds/sprig/v3 v3.0.2 - github.com/Masterminds/vcs v1.13.0 - github.com/Microsoft/go-winio v0.4.12 // indirect - github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a - github.com/containerd/containerd v1.3.0 - github.com/cyphar/filepath-securejoin v0.2.2 - github.com/deislabs/oras v0.7.0 - github.com/docker/distribution v2.7.1+incompatible - github.com/docker/docker v1.4.2-0.20181221150755-2cb26cfe9cbf - github.com/docker/go-units v0.4.0 - github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect - github.com/emicklei/go-restful v2.11.1+incompatible // indirect - github.com/evanphx/json-patch v4.5.0+incompatible - github.com/go-openapi/spec v0.19.4 // indirect - github.com/gobwas/glob v0.2.3 - github.com/gofrs/flock v0.7.1 - github.com/gogo/protobuf v1.3.1 // indirect - github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect - github.com/google/go-cmp v0.3.1 // indirect - github.com/googleapis/gnostic v0.3.1 // indirect - github.com/gorilla/mux v1.7.4 - github.com/gosuri/uitable v0.0.1 - github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f // indirect - github.com/hashicorp/golang-lru v0.5.3 // indirect - github.com/imdario/mergo v0.3.8 // indirect - github.com/mattn/go-runewidth v0.0.4 // indirect - github.com/mattn/go-shellwords v1.0.5 - github.com/mitchellh/copystructure v1.0.0 - github.com/opencontainers/go-digest v1.0.0-rc1 - github.com/opencontainers/image-spec v1.0.1 - github.com/pkg/errors v0.8.1 - github.com/prometheus/client_golang v1.2.1 // indirect - github.com/sirupsen/logrus v1.4.2 - github.com/spf13/cobra v0.0.5 - github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.4.0 - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xeipuuv/gojsonschema v1.1.0 - github.com/xenolf/lego v0.3.2-0.20160613233155-a9d8cec0e656 // indirect + github.com/gorilla/mux v1.7.2 + github.com/stretchr/testify v1.5.1 go.uber.org/zap v1.10.0 - golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 - golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 // indirect - golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934 // indirect - golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect - google.golang.org/appengine v1.6.5 // indirect - google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 // indirect - google.golang.org/grpc v1.24.0 // indirect gotest.tools v2.2.0+incompatible - k8s.io/api v0.17.1 - k8s.io/apiextensions-apiserver v0.17.1 - k8s.io/apimachinery v0.17.1 - k8s.io/cli-runtime v0.17.1 - k8s.io/client-go v0.17.1 - k8s.io/klog v1.0.0 - k8s.io/kubectl v0.17.1 - sigs.k8s.io/yaml v1.1.0 -) - -replace ( - // github.com/Azure/go-autorest/autorest has different versions for the Go - // modules than it does for releases on the repository. Note the correct - // version when updating. - github.com/Azure/go-autorest/autorest => github.com/Azure/go-autorest/autorest v0.9.0 - github.com/docker/docker => github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 - - // Kubernetes imports github.com/miekg/dns at a newer version but it is used - // by a package Helm does not need. Go modules resolves all packages rather - // than just those in use (like Glide and dep do). This sets the version - // to the one oras needs. If oras is updated the version should be updated - // as well. - github.com/miekg/dns => github.com/miekg/dns v0.0.0-20181005163659-0d29b283ac0f - gopkg.in/inf.v0 v0.9.1 => github.com/go-inf/inf v0.9.1 - gopkg.in/square/go-jose.v2 v2.3.0 => github.com/square/go-jose v2.3.0+incompatible - - rsc.io/letsencrypt => github.com/dmcgowan/letsencrypt v0.0.0-20160928181947-1847a81d2087 + helm.sh/helm/v3 v3.2.4 ) diff --git a/go.sum b/go.sum index 1bf26ec..debf41b 100644 --- a/go.sum +++ b/go.sum @@ -1,43 +1,35 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= -github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e h1:eb0Pzkt15Bm7f2FFYv7sjY7NPFi3cPkS3tv1CcrFBWA= -github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14= -github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8= -github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU= -github.com/Masterminds/vcs v1.13.0 h1:USF5TvZGYgIpcbNAEMLfFhHqP08tFZVlUVrmTSpqnyA= -github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc= -github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= +github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= +github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y= +github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= +github.com/Masterminds/squirrel v1.2.0 h1:K1NhbTO21BWG47IVR0OnIZuE0LZcXAYqywrC3Ko53KI= +github.com/Masterminds/squirrel v1.2.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= +github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -45,215 +37,194 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/bugsnag/bugsnag-go v1.5.0 h1:tP8hiPv1pGGW3LA6LKy5lW6WG+y9J2xWUdPd3WC452k= -github.com/bugsnag/bugsnag-go v1.5.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABFQA= -github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= -github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/containerd/containerd v1.3.0-beta.2.0.20190823190603-4a2f61c4f2b4 h1:aMyA5J7j6D07U7pf8BFEY67BKoDcz0zWleAbQj3zVng= -github.com/containerd/containerd v1.3.0-beta.2.0.20190823190603-4a2f61c4f2b4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0 h1:xjvXQWABwS2uiv3TWgQt5Uth60Gu86LTGZXMJkjc7rY= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M= -github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F0UxetA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea h1:n2Ltr3SrfQlf/9nOna1DoGKxLx3qTSI8Ttl6Xrqp6mw= github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= -github.com/deislabs/oras v0.7.0 h1:RnDoFd3tQYODMiUqxgQ8JxlrlWL0/VMKIKRD01MmNYk= -github.com/deislabs/oras v0.7.0/go.mod h1:sqMKPG3tMyIX9xwXUBRLhZ24o+uT4y6jgBD2RzUTKDM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/deislabs/oras v0.8.1 h1:If674KraJVpujYR00rzdi0QAmW4BxzMJPVAZJKuhQ0c= +github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As= +github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dmcgowan/letsencrypt v0.0.0-20160928181947-1847a81d2087 h1:8AJxBXuUPcBVAvoz6fi3fpSyozBxvF2DgQ0f/yn9nkE= -github.com/dmcgowan/letsencrypt v0.0.0-20160928181947-1847a81d2087/go.mod h1:pRqVcLnLZeet910LRIAzx73MR48LxCRA84OcsivAkSs= -github.com/docker/cli v0.0.0-20190506213505-d88565df0c2d h1:qdD+BtyCE1XXpDyhvn0yZVcZOLILdj9Cw4pKu0kQbPQ= -github.com/docker/cli v0.0.0-20190506213505-d88565df0c2d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 h1:FwssHbCDJD025h+BchanCwE1Q8fyMgqDr2mOQAWOLGw= +github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker-credential-helpers v0.6.1 h1:Dq4iIfcM7cNtddhLVWe9h4QDjsi4OER3Z8voPu/I52g= -github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce h1:KXS1Jg+ddGcWA8e1N7cupxaHHZhit5rB9tfDU+mfjyY= +github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zFu83v/M79DuBn84IL/Syx1SY6Y5ZEMA= -github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 h1:yWHOI+vFjEsAakUTSrtqc/SAHrhSkmn48pqjidZX3QA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= -github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= -github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.11.1+incompatible h1:CjKsv3uWcCMvySPQYKxO8XX3f9zD4FeZRsW4G0B4ffE= -github.com/emicklei/go-restful v2.11.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc= -github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-inf/inf v0.9.1 h1:F4sloU4SED74gTeM3mWLrf8yyMAgVCV0puw3vhtKWrk= -github.com/go-inf/inf v0.9.1/go.mod h1:ZWwB6rTV+0pO94RdIMKue59tExzQp6/pj/BMuPQkXaA= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2 h1:ophLETFestFZHk3ji7niPEL4d466QjW+0Tdg5VyDq7E= github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2 h1:rf5ArTHmIJxyV5Oiks+Su0mUens1+AjpkPoWr5xFRcI= github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0 h1:sU6pp4dSV2sGlNKKyHxZzi1m1kG4WnYtWcJ+HYbygjE= github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo= -github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2 h1:ky5l57HjyVRrsJfd2+Ro5Z9PjGuKbsmftwyMtk8H7js= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -264,13 +235,12 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -278,69 +248,70 @@ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= +github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= +github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA= -github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= -github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gosuri/uitable v0.0.1 h1:M9sMNgSZPyAu1FJZJLpJ16ofL8q5ko2EDUkICsynvlY= -github.com/gosuri/uitable v0.0.1/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f h1:ShTPMJQes6tubcjzGMODIVG5hlrCeImaBnZzKF2N8SM= -github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= -github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro= -github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= @@ -348,218 +319,235 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-shellwords v1.0.5 h1:JhhFTIOslh5ZsPrpa3Wdg8bF0WI3b44EMblmU9wIsXc= -github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v0.0.0-20181005163659-0d29b283ac0f h1:wVzAD6PG9MIDNQMZ6zc2YpzE/9hhJ3EN+b+a4B1thVs= -github.com/miekg/dns v0.0.0-20181005163659-0d29b283ac0f/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309 h1:cvy4lBOYN3gKfKj8Lzz5Q9TfviP+L7koMHY7SvkyTKs= -github.com/moby/moby v0.7.3-0.20190826074503-38ab9da00309/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/phayes/freeport v0.0.0-20171002181615-b8543db493a5 h1:rZQtoozkfsiNs36c7Tdv/gyGNzD1X1XWKO8rptVNZuM= -github.com/phayes/freeport v0.0.0-20171002181615-b8543db493a5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190129233650-316cf8ccfec5 h1:Etei0Wx6pooT/DeOKcGTr1M/01ggz95Ajq8BBwCOKBU= -github.com/prometheus/procfs v0.0.0-20190129233650-316cf8ccfec5/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3 h1:xkBtI5JktwbW/vf4vopBbhYsRFTGfQWHYXzC0/qYwxI= +github.com/rubenv/sql-migrate v0.0.0-20200212082348-64f95ea68aa3/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc= github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg= github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xenolf/lego v0.0.0-20160613233155-a9d8cec0e656/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY= -github.com/xenolf/lego v0.3.2-0.20160613233155-a9d8cec0e656 h1:BTvU+npm3/yjuBd53EvgiFLl5+YLikf2WvHsjRQ4KrY= -github.com/xenolf/lego v0.3.2-0.20160613233155-a9d8cec0e656/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 h1:p7OofyZ509h8DmPLh8Hn+EIIZm/xYhdZHJ9GnXHdr6U= -github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.6 h1:qMJQYPNdtJ7UNYHjX38KXZtltKTqimMuoQjNnSVIuJg= -github.com/yvasiyarov/gorelic v0.0.6/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 h1:ZC1Xn5A1nlpSmQCIva4bZ3ob3lmhYIefc+GU+DLg1Ow= -golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss= -golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -567,11 +555,12 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -580,136 +569,132 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934 h1:u/E0NqCIWRDAo9WCFo6Ko49njPFDLSd3z+X1HgWDMpE= -golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190128161407-8ac453e89fca/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 h1:UXl+Zk3jqqcbEVV7ace5lrt4YdA4tXiz3f/KbmD29Vo= -google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw= +gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v1 v1.1.2 h1:/5jmADZB+RiKtZGr4HxsEFOEfbfsjTKsVnqpThUpE30= -gopkg.in/square/go-jose.v1 v1.1.2/go.mod h1:QpYS+a4WhS+DTlyQIi6Ka7MS3SuR9a055rgXNEe6EiA= -gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +helm.sh/helm/v3 v3.2.4 h1:lz/0ZRkSgyIF+pCo6pjFzap1udCARB1IN6CRfqkpcOg= +helm.sh/helm/v3 v3.2.4/go.mod h1:ZaXz/vzktgwjyGGFbUWtIQkscfE7WYoRGP2szqAFHR0= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.17.1 h1:i46MidoDOE9tvQ0TTEYggf3ka/pziP1+tHI/GFVeJao= -k8s.io/api v0.17.1/go.mod h1:zxiAc5y8Ngn4fmhWUtSxuUlkfz1ixT7j9wESokELzOg= -k8s.io/apiextensions-apiserver v0.17.1 h1:Gw6zQgmKyyNrFMtVpRBNEKE8p35sDBI7Tq1ImxGS+zU= -k8s.io/apiextensions-apiserver v0.17.1/go.mod h1:DRIFH5x3jalE4rE7JP0MQKby9zdYk9lUJQuMmp+M/L0= -k8s.io/apimachinery v0.17.1 h1:zUjS3szTxoUjTDYNvdFkYt2uMEXLcthcbp+7uZvWhYM= -k8s.io/apimachinery v0.17.1/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= -k8s.io/apiserver v0.17.1/go.mod h1:BQEUObJv8H6ZYO7DeKI5vb50tjk6paRJ4ZhSyJsiSco= -k8s.io/cli-runtime v0.17.1 h1:VoZRWJNRyrxuM5SIRozYhT/EtcZ6jiS+KBCxRw66p1g= -k8s.io/cli-runtime v0.17.1/go.mod h1:e5847Iy85W9uWH3rZofXTG/9nOUyGKGTVnObYF7zSik= -k8s.io/client-go v0.17.1 h1:LbbuZ5tI7OYx4et5DfRFcJuoojvpYO0c7vps2rgJsHY= -k8s.io/client-go v0.17.1/go.mod h1:HZtHJSC/VuSHcETN9QA5QDZky1tXiYrkF/7t7vRpO1A= -k8s.io/code-generator v0.17.1/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= -k8s.io/component-base v0.17.1 h1:lK/lUzZZQK+DlH0XD+gq610OUEmjWOyDuUYOTGetw10= -k8s.io/component-base v0.17.1/go.mod h1:LrBPZkXtlvGjBzDJa0+b7E5Ij4VoAAKrOGudRC5z2eY= +k8s.io/api v0.18.0 h1:lwYk8Vt7rsVTwjRU6pzEsa9YNhThbmbocQlKvNBB4EQ= +k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= +k8s.io/apiextensions-apiserver v0.18.0 h1:HN4/P8vpGZFvB5SOMuPPH2Wt9Y/ryX+KRvIyAkchu1Q= +k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= +k8s.io/apimachinery v0.18.0 h1:fuPfYpk3cs1Okp/515pAf0dNhL66+8zk8RLbSX+EgAE= +k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= +k8s.io/cli-runtime v0.18.0 h1:jG8XpSqQ5TrV0N+EZ3PFz6+gqlCk71dkggWCCq9Mq34= +k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ= +k8s.io/client-go v0.18.0 h1:yqKw4cTUQraZK3fcVCMeSa+lqKwcjZ5wtcOIPnxQno4= +k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= +k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/component-base v0.18.0 h1:I+lP0fNfsEdTDpHaL61bCAqTZLoiWjEEP304Mo5ZQgE= +k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM= -k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kubectl v0.17.1 h1:+gI5hPZVEXN5wWybrzX3tu3f9af54sUNcALhg86upCY= -k8s.io/kubectl v0.17.1/go.mod h1:ZmbAdEQm+SLA/3s3eWJ3g+liXb5eT6mA85jYj52LMXw= -k8s.io/metrics v0.17.1/go.mod h1:dphDhzjA1KR/nQXtXEQzoQyQXk5ViSJO85Ky8QKwBPM= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c h1:/KUFqjjqAcY4Us6luF5RDNZ16KJtb49HfR3ZHB9qYXM= +k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= +k8s.io/kubectl v0.18.0 h1:hu52Ndq/d099YW+3sS3VARxFz61Wheiq8K9S7oa82Dk= +k8s.io/kubectl v0.18.0/go.mod h1:LOkWx9Z5DXMEg5KtOjHhRiC1fqJPLyCr3KtQgEolCkU= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/metrics v0.18.0/go.mod h1:8aYTW18koXqjLVKL7Ds05RPMX9ipJZI3mywYvBOxXd4= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU= +k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= +sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/internal/completion/complete.go b/internal/completion/complete.go deleted file mode 100644 index a24390f..0000000 --- a/internal/completion/complete.go +++ /dev/null @@ -1,388 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package completion - -import ( - "errors" - "fmt" - "io" - "log" - "os" - "strings" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "helm.sh/helm/v3/cmd/helm/require" - "helm.sh/helm/v3/pkg/cli" -) - -// ================================================================================== -// The below code supports dynamic shell completion in Go. -// This should ultimately be pushed down into Cobra. -// ================================================================================== - -// compRequestCmd Hidden command to request completion results from the program. -// Used by the shell completion script. -const compRequestCmd = "__complete" - -// Global map allowing to find completion functions for commands or flags. -var validArgsFunctions = map[interface{}]func(cmd *cobra.Command, args []string, toComplete string) ([]string, BashCompDirective){} - -// BashCompDirective is a bit map representing the different behaviors the shell -// can be instructed to have once completions have been provided. -type BashCompDirective int - -const ( - // BashCompDirectiveError indicates an error occurred and completions should be ignored. - BashCompDirectiveError BashCompDirective = 1 << iota - - // BashCompDirectiveNoSpace indicates that the shell should not add a space - // after the completion even if there is a single completion provided. - BashCompDirectiveNoSpace - - // BashCompDirectiveNoFileComp indicates that the shell should not provide - // file completion even when no completion is provided. - // This currently does not work for zsh or bash < 4 - BashCompDirectiveNoFileComp - - // BashCompDirectiveDefault indicates to let the shell perform its default - // behavior after completions have been provided. - BashCompDirectiveDefault BashCompDirective = 0 -) - -// GetBashCustomFunction returns the bash code to handle custom go completion -// This should eventually be provided by Cobra -func GetBashCustomFunction() string { - return fmt.Sprintf(` -__helm_custom_func() -{ - __helm_debug "${FUNCNAME[0]}: c is $c, words[@] is ${words[@]}, #words[@] is ${#words[@]}" - __helm_debug "${FUNCNAME[0]}: cur is ${cur}, cword is ${cword}, words is ${words}" - - local out requestComp lastParam lastChar - requestComp="${words[0]} %[1]s ${words[@]:1}" - - lastParam=${words[$((${#words[@]}-1))]} - lastChar=${lastParam:$((${#lastParam}-1)):1} - __helm_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}" - - if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then - # If the last parameter is complete (there is a space following it) - # We add an extra empty parameter so we can indicate this to the go method. - __helm_debug "${FUNCNAME[0]}: Adding extra empty parameter" - requestComp="${requestComp} \"\"" - fi - - __helm_debug "${FUNCNAME[0]}: calling ${requestComp}" - # Use eval to handle any environment variables and such - out=$(eval ${requestComp} 2>/dev/null) - - # Extract the directive int at the very end of the output following a : - directive=${out##*:} - # Remove the directive - out=${out%%:*} - if [ "${directive}" = "${out}" ]; then - # There is not directive specified - directive=0 - fi - __helm_debug "${FUNCNAME[0]}: the completion directive is: ${directive}" - __helm_debug "${FUNCNAME[0]}: the completions are: ${out[*]}" - - if [ $((${directive} & %[2]d)) -ne 0 ]; then - __helm_debug "${FUNCNAME[0]}: received error, completion failed" - else - if [ $((${directive} & %[3]d)) -ne 0 ]; then - if [[ $(type -t compopt) = "builtin" ]]; then - __helm_debug "${FUNCNAME[0]}: activating no space" - compopt -o nospace - fi - fi - if [ $((${directive} & %[4]d)) -ne 0 ]; then - if [[ $(type -t compopt) = "builtin" ]]; then - __helm_debug "${FUNCNAME[0]}: activating no file completion" - compopt +o default - fi - fi - - while IFS='' read -r comp; do - COMPREPLY+=("$comp") - done < <(compgen -W "${out[*]}" -- "$cur") - fi -} -`, compRequestCmd, BashCompDirectiveError, BashCompDirectiveNoSpace, BashCompDirectiveNoFileComp) -} - -// RegisterValidArgsFunc should be called to register a function to provide argument completion for a command -func RegisterValidArgsFunc(cmd *cobra.Command, f func(cmd *cobra.Command, args []string, toComplete string) ([]string, BashCompDirective)) { - if _, exists := validArgsFunctions[cmd]; exists { - log.Fatal(fmt.Sprintf("RegisterValidArgsFunc: command '%s' already registered", cmd.Name())) - } - validArgsFunctions[cmd] = f -} - -// RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag -func RegisterFlagCompletionFunc(flag *pflag.Flag, f func(cmd *cobra.Command, args []string, toComplete string) ([]string, BashCompDirective)) { - if _, exists := validArgsFunctions[flag]; exists { - log.Fatal(fmt.Sprintf("RegisterFlagCompletionFunc: flag '%s' already registered", flag.Name)) - } - validArgsFunctions[flag] = f - - // Make sure the completion script call the __helm_custom_func for the registered flag. - // This is essential to make the = form work. E.g., helm -n= or helm status --output= - if flag.Annotations == nil { - flag.Annotations = map[string][]string{} - } - flag.Annotations[cobra.BashCompCustom] = []string{"__helm_custom_func"} -} - -var debug = true - -// Returns a string listing the different directive enabled in the specified parameter -func (d BashCompDirective) string() string { - var directives []string - if d&BashCompDirectiveError != 0 { - directives = append(directives, "BashCompDirectiveError") - } - if d&BashCompDirectiveNoSpace != 0 { - directives = append(directives, "BashCompDirectiveNoSpace") - } - if d&BashCompDirectiveNoFileComp != 0 { - directives = append(directives, "BashCompDirectiveNoFileComp") - } - if len(directives) == 0 { - directives = append(directives, "BashCompDirectiveDefault") - } - - if d > BashCompDirectiveError+BashCompDirectiveNoSpace+BashCompDirectiveNoFileComp { - return fmt.Sprintf("ERROR: unexpected BashCompDirective value: %d", d) - } - return strings.Join(directives, ", ") -} - -// NewCompleteCmd add a special hidden command that an be used to request completions -func NewCompleteCmd(settings *cli.EnvSettings, out io.Writer) *cobra.Command { - debug = settings.Debug - return &cobra.Command{ - Use: fmt.Sprintf("%s [command-line]", compRequestCmd), - DisableFlagsInUseLine: true, - Hidden: true, - DisableFlagParsing: true, - Args: require.MinimumNArgs(1), - Short: "Request shell completion choices for the specified command-line", - Long: fmt.Sprintf("%s is a special command that is used by the shell completion logic\n%s", - compRequestCmd, "to request completion choices for the specified command-line."), - Run: func(cmd *cobra.Command, args []string) { - CompDebugln(fmt.Sprintf("%s was called with args %v", cmd.Name(), args)) - - flag, trimmedArgs, toComplete, err := checkIfFlagCompletion(cmd.Root(), args[:len(args)-1], args[len(args)-1]) - if err != nil { - // Error while attempting to parse flags - CompErrorln(err.Error()) - return - } - // Find the real command for which completion must be performed - finalCmd, finalArgs, err := cmd.Root().Find(trimmedArgs) - if err != nil { - // Unable to find the real command. E.g., helm invalidCmd - return - } - - CompDebugln(fmt.Sprintf("Found final command '%s', with finalArgs %v", finalCmd.Name(), finalArgs)) - - // Parse the flags and extract the arguments to prepare for calling the completion function - if err = finalCmd.ParseFlags(finalArgs); err != nil { - CompErrorln(fmt.Sprintf("Error while parsing flags from args %v: %s", finalArgs, err.Error())) - return - } - argsWoFlags := finalCmd.Flags().Args() - CompDebugln(fmt.Sprintf("Args without flags are '%v' with length %d", argsWoFlags, len(argsWoFlags))) - - var key interface{} - var keyStr string - if flag != nil { - key = flag - keyStr = flag.Name - } else { - key = finalCmd - keyStr = finalCmd.Name() - } - - // Find completion function for the flag or command - completionFn, ok := validArgsFunctions[key] - if !ok { - CompErrorln(fmt.Sprintf("Dynamic completion not supported/needed for flag or command: %s", keyStr)) - return - } - - CompDebugln(fmt.Sprintf("Calling completion method for subcommand '%s' with args '%v' and toComplete '%s'", finalCmd.Name(), argsWoFlags, toComplete)) - completions, directive := completionFn(finalCmd, argsWoFlags, toComplete) - for _, comp := range completions { - // Print each possible completion to stdout for the completion script to consume. - fmt.Fprintln(out, comp) - } - - // As the last printout, print the completion directive for the - // completion script to parse. - // The directive integer must be that last character following a single : - // The completion script expects :directive - fmt.Fprintln(out, fmt.Sprintf(":%d", directive)) - - // Print some helpful info to stderr for the user to understand. - // Output from stderr should be ignored from the completion script. - fmt.Fprintf(os.Stderr, "Completion ended with directive: %s\n", directive.string()) - }, - } -} - -func isFlag(arg string) bool { - return len(arg) > 0 && arg[0] == '-' -} - -func checkIfFlagCompletion(rootCmd *cobra.Command, args []string, lastArg string) (*pflag.Flag, []string, string, error) { - var flagName string - trimmedArgs := args - flagWithEqual := false - if isFlag(lastArg) { - if index := strings.Index(lastArg, "="); index >= 0 { - flagName = strings.TrimLeft(lastArg[:index], "-") - lastArg = lastArg[index+1:] - flagWithEqual = true - } else { - return nil, nil, "", errors.New("Unexpected completion request for flag") - } - } - - if len(flagName) == 0 { - if len(args) > 0 { - prevArg := args[len(args)-1] - if isFlag(prevArg) { - // If the flag contains an = it means it has already been fully processed - if index := strings.Index(prevArg, "="); index < 0 { - flagName = strings.TrimLeft(prevArg, "-") - - // Remove the uncompleted flag or else Cobra could complain about - // an invalid value for that flag e.g., helm status --output j - trimmedArgs = args[:len(args)-1] - } - } - } - } - - if len(flagName) == 0 { - // Not doing flag completion - return nil, trimmedArgs, lastArg, nil - } - - // Find the real command for which completion must be performed - finalCmd, _, err := rootCmd.Find(trimmedArgs) - if err != nil { - // Unable to find the real command. E.g., helm invalidCmd - return nil, nil, "", errors.New("Unable to find final command for completion") - } - - CompDebugln(fmt.Sprintf("checkIfFlagCompletion: found final command '%s'", finalCmd.Name())) - - flag := findFlag(finalCmd, flagName) - if flag == nil { - // Flag not supported by this command, nothing to complete - err = fmt.Errorf("Subcommand '%s' does not support flag '%s'", finalCmd.Name(), flagName) - return nil, nil, "", err - } - - if !flagWithEqual { - if len(flag.NoOptDefVal) != 0 { - // We had assumed dealing with a two-word flag but the flag is a boolean flag. - // In that case, there is no value following it, so we are not really doing flag completion. - // Reset everything to do argument completion. - trimmedArgs = args - flag = nil - } - } - - return flag, trimmedArgs, lastArg, nil -} - -func findFlag(cmd *cobra.Command, name string) *pflag.Flag { - flagSet := cmd.Flags() - if len(name) == 1 { - // First convert the short flag into a long flag - // as the cmd.Flag() search only accepts long flags - if short := flagSet.ShorthandLookup(name); short != nil { - CompDebugln(fmt.Sprintf("checkIfFlagCompletion: found flag '%s' which we will change to '%s'", name, short.Name)) - name = short.Name - } else { - set := cmd.InheritedFlags() - if short = set.ShorthandLookup(name); short != nil { - CompDebugln(fmt.Sprintf("checkIfFlagCompletion: found inherited flag '%s' which we will change to '%s'", name, short.Name)) - name = short.Name - } else { - return nil - } - } - } - return cmd.Flag(name) -} - -// CompDebug prints the specified string to the same file as where the -// completion script prints its logs. -// Note that completion printouts should never be on stdout as they would -// be wrongly interpreted as actual completion choices by the completion script. -func CompDebug(msg string) { - msg = fmt.Sprintf("[Debug] %s", msg) - - // Such logs are only printed when the user has set the environment - // variable BASH_COMP_DEBUG_FILE to the path of some file to be used. - if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" { - f, err := os.OpenFile(path, - os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err == nil { - defer f.Close() - f.WriteString(msg) - } - } - - if debug { - // Must print to stderr for this not to be read by the completion script. - fmt.Fprintf(os.Stderr, msg) - } -} - -// CompDebugln prints the specified string with a newline at the end -// to the same file as where the completion script prints its logs. -// Such logs are only printed when the user has set the environment -// variable BASH_COMP_DEBUG_FILE to the path of some file to be used. -func CompDebugln(msg string) { - CompDebug(fmt.Sprintf("%s\n", msg)) -} - -// CompError prints the specified completion message to stderr. -func CompError(msg string) { - msg = fmt.Sprintf("[Error] %s", msg) - - CompDebug(msg) - - // If not already printed by the call to CompDebug(). - if !debug { - // Must print to stderr for this not to be read by the completion script. - fmt.Fprintf(os.Stderr, msg) - } -} - -// CompErrorln prints the specified completion message to stderr with a newline at the end. -func CompErrorln(msg string) { - CompError(fmt.Sprintf("%s\n", msg)) -} diff --git a/internal/experimental/registry/authorizer.go b/internal/experimental/registry/authorizer.go deleted file mode 100644 index 918a999..0000000 --- a/internal/experimental/registry/authorizer.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -import ( - "github.com/deislabs/oras/pkg/auth" -) - -type ( - // Authorizer handles registry auth operations - Authorizer struct { - auth.Client - } -) diff --git a/internal/experimental/registry/cache.go b/internal/experimental/registry/cache.go deleted file mode 100644 index fbd6256..0000000 --- a/internal/experimental/registry/cache.go +++ /dev/null @@ -1,366 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "time" - - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" - orascontent "github.com/deislabs/oras/pkg/content" - digest "github.com/opencontainers/go-digest" - specs "github.com/opencontainers/image-spec/specs-go" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" -) - -const ( - // CacheRootDir is the root directory for a cache - CacheRootDir = "cache" -) - -type ( - // Cache handles local/in-memory storage of Helm charts, compliant with OCI Layout - Cache struct { - debug bool - out io.Writer - rootDir string - ociStore *orascontent.OCIStore - memoryStore *orascontent.Memorystore - } - - // CacheRefSummary contains as much info as available describing a chart reference in cache - // Note: fields here are sorted by the order in which they are set in FetchReference method - CacheRefSummary struct { - Name string - Repo string - Tag string - Exists bool - Manifest *ocispec.Descriptor - Config *ocispec.Descriptor - ContentLayer *ocispec.Descriptor - Size int64 - Digest digest.Digest - CreatedAt time.Time - Chart *chart.Chart - } -) - -// NewCache returns a new OCI Layout-compliant cache with config -func NewCache(opts ...CacheOption) (*Cache, error) { - cache := &Cache{ - out: ioutil.Discard, - } - for _, opt := range opts { - opt(cache) - } - // validate - if cache.rootDir == "" { - return nil, errors.New("must set cache root dir on initialization") - } - return cache, nil -} - -// FetchReference retrieves a chart ref from cache -func (cache *Cache) FetchReference(ref *Reference) (*CacheRefSummary, error) { - if err := cache.init(); err != nil { - return nil, err - } - r := CacheRefSummary{ - Name: ref.FullName(), - Repo: ref.Repo, - Tag: ref.Tag, - } - for _, desc := range cache.ociStore.ListReferences() { - if desc.Annotations[ocispec.AnnotationRefName] == r.Name { - r.Exists = true - manifestBytes, err := cache.fetchBlob(&desc) - if err != nil { - return &r, err - } - var manifest ocispec.Manifest - err = json.Unmarshal(manifestBytes, &manifest) - if err != nil { - return &r, err - } - r.Manifest = &desc - r.Config = &manifest.Config - numLayers := len(manifest.Layers) - if numLayers != 1 { - return &r, errors.New( - fmt.Sprintf("manifest does not contain exactly 1 layer (total: %d)", numLayers)) - } - var contentLayer *ocispec.Descriptor - for _, layer := range manifest.Layers { - switch layer.MediaType { - case HelmChartContentLayerMediaType: - contentLayer = &layer - } - } - if contentLayer == nil { - return &r, errors.New( - fmt.Sprintf("manifest does not contain a layer with mediatype %s", HelmChartContentLayerMediaType)) - } - if contentLayer.Size == 0 { - return &r, errors.New( - fmt.Sprintf("manifest layer with mediatype %s is of size 0", HelmChartContentLayerMediaType)) - } - r.ContentLayer = contentLayer - info, err := cache.ociStore.Info(ctx(cache.out, cache.debug), contentLayer.Digest) - if err != nil { - return &r, err - } - r.Size = info.Size - r.Digest = info.Digest - r.CreatedAt = info.CreatedAt - contentBytes, err := cache.fetchBlob(contentLayer) - if err != nil { - return &r, err - } - ch, err := loader.LoadArchive(bytes.NewBuffer(contentBytes)) - if err != nil { - return &r, err - } - r.Chart = ch - } - } - return &r, nil -} - -// StoreReference stores a chart ref in cache -func (cache *Cache) StoreReference(ref *Reference, ch *chart.Chart) (*CacheRefSummary, error) { - if err := cache.init(); err != nil { - return nil, err - } - r := CacheRefSummary{ - Name: ref.FullName(), - Repo: ref.Repo, - Tag: ref.Tag, - Chart: ch, - } - existing, _ := cache.FetchReference(ref) - r.Exists = existing.Exists - config, _, err := cache.saveChartConfig(ch) - if err != nil { - return &r, err - } - r.Config = config - contentLayer, _, err := cache.saveChartContentLayer(ch) - if err != nil { - return &r, err - } - r.ContentLayer = contentLayer - info, err := cache.ociStore.Info(ctx(cache.out, cache.debug), contentLayer.Digest) - if err != nil { - return &r, err - } - r.Size = info.Size - r.Digest = info.Digest - r.CreatedAt = info.CreatedAt - manifest, _, err := cache.saveChartManifest(config, contentLayer) - if err != nil { - return &r, err - } - r.Manifest = manifest - return &r, nil -} - -// DeleteReference deletes a chart ref from cache -// TODO: garbage collection, only manifest removed -func (cache *Cache) DeleteReference(ref *Reference) (*CacheRefSummary, error) { - if err := cache.init(); err != nil { - return nil, err - } - r, err := cache.FetchReference(ref) - if err != nil || !r.Exists { - return r, err - } - cache.ociStore.DeleteReference(r.Name) - err = cache.ociStore.SaveIndex() - return r, err -} - -// ListReferences lists all chart refs in a cache -func (cache *Cache) ListReferences() ([]*CacheRefSummary, error) { - if err := cache.init(); err != nil { - return nil, err - } - var rr []*CacheRefSummary - for _, desc := range cache.ociStore.ListReferences() { - name := desc.Annotations[ocispec.AnnotationRefName] - if name == "" { - if cache.debug { - fmt.Fprintf(cache.out, "warning: found manifest without name: %s", desc.Digest.Hex()) - } - continue - } - ref, err := ParseReference(name) - if err != nil { - return rr, err - } - r, err := cache.FetchReference(ref) - if err != nil { - return rr, err - } - rr = append(rr, r) - } - return rr, nil -} - -// AddManifest provides a manifest to the cache index.json -func (cache *Cache) AddManifest(ref *Reference, manifest *ocispec.Descriptor) error { - if err := cache.init(); err != nil { - return err - } - cache.ociStore.AddReference(ref.FullName(), *manifest) - err := cache.ociStore.SaveIndex() - return err -} - -// Provider provides a valid containerd Provider -func (cache *Cache) Provider() content.Provider { - return content.Provider(cache.ociStore) -} - -// Ingester provides a valid containerd Ingester -func (cache *Cache) Ingester() content.Ingester { - return content.Ingester(cache.ociStore) -} - -// ProvideIngester provides a valid oras ProvideIngester -func (cache *Cache) ProvideIngester() orascontent.ProvideIngester { - return orascontent.ProvideIngester(cache.ociStore) -} - -// init creates files needed necessary for OCI layout store -func (cache *Cache) init() error { - if cache.ociStore == nil { - ociStore, err := orascontent.NewOCIStore(cache.rootDir) - if err != nil { - return err - } - cache.ociStore = ociStore - cache.memoryStore = orascontent.NewMemoryStore() - } - return nil -} - -// saveChartConfig stores the Chart.yaml as json blob and returns a descriptor -func (cache *Cache) saveChartConfig(ch *chart.Chart) (*ocispec.Descriptor, bool, error) { - configBytes, err := json.Marshal(ch.Metadata) - if err != nil { - return nil, false, err - } - configExists, err := cache.storeBlob(configBytes) - if err != nil { - return nil, configExists, err - } - descriptor := cache.memoryStore.Add("", HelmChartConfigMediaType, configBytes) - return &descriptor, configExists, nil -} - -// saveChartContentLayer stores the chart as tarball blob and returns a descriptor -func (cache *Cache) saveChartContentLayer(ch *chart.Chart) (*ocispec.Descriptor, bool, error) { - destDir := filepath.Join(cache.rootDir, ".build") - os.MkdirAll(destDir, 0755) - tmpFile, err := chartutil.Save(ch, destDir) - defer os.Remove(tmpFile) - if err != nil { - return nil, false, errors.Wrap(err, "failed to save") - } - contentBytes, err := ioutil.ReadFile(tmpFile) - if err != nil { - return nil, false, err - } - contentExists, err := cache.storeBlob(contentBytes) - if err != nil { - return nil, contentExists, err - } - descriptor := cache.memoryStore.Add("", HelmChartContentLayerMediaType, contentBytes) - return &descriptor, contentExists, nil -} - -// saveChartManifest stores the chart manifest as json blob and returns a descriptor -func (cache *Cache) saveChartManifest(config *ocispec.Descriptor, contentLayer *ocispec.Descriptor) (*ocispec.Descriptor, bool, error) { - manifest := ocispec.Manifest{ - Versioned: specs.Versioned{SchemaVersion: 2}, - Config: *config, - Layers: []ocispec.Descriptor{*contentLayer}, - } - manifestBytes, err := json.Marshal(manifest) - if err != nil { - return nil, false, err - } - manifestExists, err := cache.storeBlob(manifestBytes) - if err != nil { - return nil, manifestExists, err - } - descriptor := ocispec.Descriptor{ - MediaType: ocispec.MediaTypeImageManifest, - Digest: digest.FromBytes(manifestBytes), - Size: int64(len(manifestBytes)), - } - return &descriptor, manifestExists, nil -} - -// storeBlob stores a blob on filesystem -func (cache *Cache) storeBlob(blobBytes []byte) (bool, error) { - var exists bool - writer, err := cache.ociStore.Store.Writer(ctx(cache.out, cache.debug), - content.WithRef(digest.FromBytes(blobBytes).Hex())) - if err != nil { - return exists, err - } - _, err = writer.Write(blobBytes) - if err != nil { - return exists, err - } - err = writer.Commit(ctx(cache.out, cache.debug), 0, writer.Digest()) - if err != nil { - if !errdefs.IsAlreadyExists(err) { - return exists, err - } - exists = true - } - err = writer.Close() - return exists, err -} - -// fetchBlob retrieves a blob from filesystem -func (cache *Cache) fetchBlob(desc *ocispec.Descriptor) ([]byte, error) { - reader, err := cache.ociStore.ReaderAt(ctx(cache.out, cache.debug), *desc) - if err != nil { - return nil, err - } - bytes := make([]byte, desc.Size) - _, err = reader.ReadAt(bytes, 0) - if err != nil { - return nil, err - } - return bytes, nil -} diff --git a/internal/experimental/registry/cache_opts.go b/internal/experimental/registry/cache_opts.go deleted file mode 100644 index 6851ae8..0000000 --- a/internal/experimental/registry/cache_opts.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -import ( - "io" -) - -type ( - // CacheOption allows specifying various settings configurable by the user for overriding the defaults - // used when creating a new default cache - CacheOption func(*Cache) -) - -// CacheOptDebug returns a function that sets the debug setting on cache options set -func CacheOptDebug(debug bool) CacheOption { - return func(cache *Cache) { - cache.debug = debug - } -} - -// CacheOptWriter returns a function that sets the writer setting on cache options set -func CacheOptWriter(out io.Writer) CacheOption { - return func(cache *Cache) { - cache.out = out - } -} - -// CacheOptRoot returns a function that sets the root directory setting on cache options set -func CacheOptRoot(rootDir string) CacheOption { - return func(cache *Cache) { - cache.rootDir = rootDir - } -} diff --git a/internal/experimental/registry/client.go b/internal/experimental/registry/client.go deleted file mode 100644 index d52d9f3..0000000 --- a/internal/experimental/registry/client.go +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -import ( - "context" - "fmt" - "io" - "io/ioutil" - "sort" - - auth "github.com/deislabs/oras/pkg/auth/docker" - "github.com/deislabs/oras/pkg/oras" - "github.com/gosuri/uitable" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/helmpath" -) - -const ( - // CredentialsFileBasename is the filename for auth credentials file - CredentialsFileBasename = "config.json" -) - -type ( - // Client works with OCI-compliant registries and local Helm chart cache - Client struct { - debug bool - out io.Writer - authorizer *Authorizer - resolver *Resolver - cache *Cache - } -) - -// NewClient returns a new registry client with config -func NewClient(opts ...ClientOption) (*Client, error) { - client := &Client{ - out: ioutil.Discard, - } - for _, opt := range opts { - opt(client) - } - // set defaults if fields are missing - if client.authorizer == nil { - credentialsFile := helmpath.CachePath("registry", CredentialsFileBasename) - authClient, err := auth.NewClient(credentialsFile) - if err != nil { - return nil, err - } - client.authorizer = &Authorizer{ - Client: authClient, - } - } - if client.resolver == nil { - resolver, err := client.authorizer.Resolver(context.Background()) - if err != nil { - return nil, err - } - client.resolver = &Resolver{ - Resolver: resolver, - } - } - if client.cache == nil { - cache, err := NewCache( - CacheOptDebug(client.debug), - CacheOptWriter(client.out), - CacheOptRoot(helmpath.CachePath("registry", CacheRootDir)), - ) - if err != nil { - return nil, err - } - client.cache = cache - } - return client, nil -} - -// Login logs into a registry -func (c *Client) Login(hostname string, username string, password string, insecure bool) error { - err := c.authorizer.Login(ctx(c.out, c.debug), hostname, username, password, insecure) - if err != nil { - return err - } - fmt.Fprintf(c.out, "Login succeeded\n") - return nil -} - -// Logout logs out of a registry -func (c *Client) Logout(hostname string) error { - err := c.authorizer.Logout(ctx(c.out, c.debug), hostname) - if err != nil { - return err - } - fmt.Fprintln(c.out, "Logout succeeded") - return nil -} - -// PushChart uploads a chart to a registry -func (c *Client) PushChart(ref *Reference) error { - r, err := c.cache.FetchReference(ref) - if err != nil { - return err - } - if !r.Exists { - return errors.New(fmt.Sprintf("Chart not found: %s", r.Name)) - } - fmt.Fprintf(c.out, "The push refers to repository [%s]\n", r.Repo) - c.printCacheRefSummary(r) - layers := []ocispec.Descriptor{*r.ContentLayer} - _, err = oras.Push(ctx(c.out, c.debug), c.resolver, r.Name, c.cache.Provider(), layers, - oras.WithConfig(*r.Config), oras.WithNameValidation(nil)) - if err != nil { - return err - } - s := "" - numLayers := len(layers) - if 1 < numLayers { - s = "s" - } - fmt.Fprintf(c.out, - "%s: pushed to remote (%d layer%s, %s total)\n", r.Tag, numLayers, s, byteCountBinary(r.Size)) - return nil -} - -// PullChart downloads a chart from a registry -func (c *Client) PullChart(ref *Reference) error { - if ref.Tag == "" { - return errors.New("tag explicitly required") - } - existing, err := c.cache.FetchReference(ref) - if err != nil { - return err - } - fmt.Fprintf(c.out, "%s: Pulling from %s\n", ref.Tag, ref.Repo) - manifest, _, err := oras.Pull(ctx(c.out, c.debug), c.resolver, ref.FullName(), c.cache.Ingester(), - oras.WithPullEmptyNameAllowed(), - oras.WithAllowedMediaTypes(KnownMediaTypes()), - oras.WithContentProvideIngester(c.cache.ProvideIngester())) - if err != nil { - return err - } - err = c.cache.AddManifest(ref, &manifest) - if err != nil { - return err - } - r, err := c.cache.FetchReference(ref) - if err != nil { - return err - } - if !r.Exists { - return errors.New(fmt.Sprintf("Chart not found: %s", r.Name)) - } - c.printCacheRefSummary(r) - if !existing.Exists { - fmt.Fprintf(c.out, "Status: Downloaded newer chart for %s\n", ref.FullName()) - } else { - fmt.Fprintf(c.out, "Status: Chart is up to date for %s\n", ref.FullName()) - } - return err -} - -// SaveChart stores a copy of chart in local cache -func (c *Client) SaveChart(ch *chart.Chart, ref *Reference) error { - r, err := c.cache.StoreReference(ref, ch) - if err != nil { - return err - } - c.printCacheRefSummary(r) - err = c.cache.AddManifest(ref, r.Manifest) - if err != nil { - return err - } - fmt.Fprintf(c.out, "%s: saved\n", r.Tag) - return nil -} - -// LoadChart retrieves a chart object by reference -func (c *Client) LoadChart(ref *Reference) (*chart.Chart, error) { - r, err := c.cache.FetchReference(ref) - if err != nil { - return nil, err - } - if !r.Exists { - return nil, errors.New(fmt.Sprintf("Chart not found: %s", ref.FullName())) - } - c.printCacheRefSummary(r) - return r.Chart, nil -} - -// RemoveChart deletes a locally saved chart -func (c *Client) RemoveChart(ref *Reference) error { - r, err := c.cache.DeleteReference(ref) - if err != nil { - return err - } - if !r.Exists { - return errors.New(fmt.Sprintf("Chart not found: %s", ref.FullName())) - } - fmt.Fprintf(c.out, "%s: removed\n", r.Tag) - return nil -} - -// PrintChartTable prints a list of locally stored charts -func (c *Client) PrintChartTable() error { - table := uitable.New() - table.MaxColWidth = 60 - table.AddRow("REF", "NAME", "VERSION", "DIGEST", "SIZE", "CREATED") - rows, err := c.getChartTableRows() - if err != nil { - return err - } - for _, row := range rows { - table.AddRow(row...) - } - fmt.Fprintln(c.out, table.String()) - return nil -} - -// printCacheRefSummary prints out chart ref summary -func (c *Client) printCacheRefSummary(r *CacheRefSummary) { - fmt.Fprintf(c.out, "ref: %s\n", r.Name) - fmt.Fprintf(c.out, "digest: %s\n", r.Digest.Hex()) - fmt.Fprintf(c.out, "size: %s\n", byteCountBinary(r.Size)) - fmt.Fprintf(c.out, "name: %s\n", r.Chart.Metadata.Name) - fmt.Fprintf(c.out, "version: %s\n", r.Chart.Metadata.Version) -} - -// getChartTableRows returns rows in uitable-friendly format -func (c *Client) getChartTableRows() ([][]interface{}, error) { - rr, err := c.cache.ListReferences() - if err != nil { - return nil, err - } - refsMap := map[string]map[string]string{} - for _, r := range rr { - refsMap[r.Name] = map[string]string{ - "name": r.Chart.Metadata.Name, - "version": r.Chart.Metadata.Version, - "digest": shortDigest(r.Digest.Hex()), - "size": byteCountBinary(r.Size), - "created": timeAgo(r.CreatedAt), - } - } - // Sort and convert to format expected by uitable - rows := make([][]interface{}, len(refsMap)) - keys := make([]string, 0, len(refsMap)) - for key := range refsMap { - keys = append(keys, key) - } - sort.Strings(keys) - for i, key := range keys { - rows[i] = make([]interface{}, 6) - rows[i][0] = key - ref := refsMap[key] - for j, k := range []string{"name", "version", "digest", "size", "created"} { - rows[i][j+1] = ref[k] - } - } - return rows, nil -} diff --git a/internal/experimental/registry/client_opts.go b/internal/experimental/registry/client_opts.go deleted file mode 100644 index cd29581..0000000 --- a/internal/experimental/registry/client_opts.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -import ( - "io" -) - -type ( - // ClientOption allows specifying various settings configurable by the user for overriding the defaults - // used when creating a new default client - ClientOption func(*Client) -) - -// ClientOptDebug returns a function that sets the debug setting on client options set -func ClientOptDebug(debug bool) ClientOption { - return func(client *Client) { - client.debug = debug - } -} - -// ClientOptWriter returns a function that sets the writer setting on client options set -func ClientOptWriter(out io.Writer) ClientOption { - return func(client *Client) { - client.out = out - } -} - -// ClientOptResolver returns a function that sets the resolver setting on client options set -func ClientOptResolver(resolver *Resolver) ClientOption { - return func(client *Client) { - client.resolver = resolver - } -} - -// ClientOptAuthorizer returns a function that sets the authorizer setting on client options set -func ClientOptAuthorizer(authorizer *Authorizer) ClientOption { - return func(client *Client) { - client.authorizer = authorizer - } -} - -// ClientOptCache returns a function that sets the cache setting on a client options set -func ClientOptCache(cache *Cache) ClientOption { - return func(client *Client) { - client.cache = cache - } -} diff --git a/internal/experimental/registry/client_test.go b/internal/experimental/registry/client_test.go deleted file mode 100644 index 0861c89..0000000 --- a/internal/experimental/registry/client_test.go +++ /dev/null @@ -1,251 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry - -import ( - "bytes" - "context" - "fmt" - "io" - "io/ioutil" - "net" - "os" - "path/filepath" - "testing" - "time" - - auth "github.com/deislabs/oras/pkg/auth/docker" - "github.com/docker/distribution/configuration" - "github.com/docker/distribution/registry" - _ "github.com/docker/distribution/registry/auth/htpasswd" - _ "github.com/docker/distribution/registry/storage/driver/inmemory" - "github.com/stretchr/testify/suite" - "golang.org/x/crypto/bcrypt" - - "helm.sh/helm/v3/pkg/chart" -) - -var ( - testCacheRootDir = "helm-registry-test" - testHtpasswdFileBasename = "authtest.htpasswd" - testUsername = "myuser" - testPassword = "mypass" -) - -type RegistryClientTestSuite struct { - suite.Suite - Out io.Writer - DockerRegistryHost string - CacheRootDir string - RegistryClient *Client -} - -func (suite *RegistryClientTestSuite) SetupSuite() { - suite.CacheRootDir = testCacheRootDir - os.RemoveAll(suite.CacheRootDir) - os.Mkdir(suite.CacheRootDir, 0700) - - var out bytes.Buffer - suite.Out = &out - credentialsFile := filepath.Join(suite.CacheRootDir, CredentialsFileBasename) - - client, err := auth.NewClient(credentialsFile) - suite.Nil(err, "no error creating auth client") - - resolver, err := client.Resolver(context.Background()) - suite.Nil(err, "no error creating resolver") - - // create cache - cache, err := NewCache( - CacheOptDebug(true), - CacheOptWriter(suite.Out), - CacheOptRoot(filepath.Join(suite.CacheRootDir, CacheRootDir)), - ) - suite.Nil(err, "no error creating cache") - - // init test client - suite.RegistryClient, err = NewClient( - ClientOptDebug(true), - ClientOptWriter(suite.Out), - ClientOptAuthorizer(&Authorizer{ - Client: client, - }), - ClientOptResolver(&Resolver{ - Resolver: resolver, - }), - ClientOptCache(cache), - ) - suite.Nil(err, "no error creating registry client") - - // create htpasswd file (w BCrypt, which is required) - pwBytes, err := bcrypt.GenerateFromPassword([]byte(testPassword), bcrypt.DefaultCost) - suite.Nil(err, "no error generating bcrypt password for test htpasswd file") - htpasswdPath := filepath.Join(suite.CacheRootDir, testHtpasswdFileBasename) - err = ioutil.WriteFile(htpasswdPath, []byte(fmt.Sprintf("%s:%s\n", testUsername, string(pwBytes))), 0644) - suite.Nil(err, "no error creating test htpasswd file") - - // Registry config - config := &configuration.Configuration{} - port, err := getFreePort() - suite.Nil(err, "no error finding free port for test registry") - suite.DockerRegistryHost = fmt.Sprintf("localhost:%d", port) - config.HTTP.Addr = fmt.Sprintf(":%d", port) - config.HTTP.DrainTimeout = time.Duration(10) * time.Second - config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}} - config.Auth = configuration.Auth{ - "htpasswd": configuration.Parameters{ - "realm": "localhost", - "path": htpasswdPath, - }, - } - dockerRegistry, err := registry.NewRegistry(context.Background(), config) - suite.Nil(err, "no error creating test registry") - - // Start Docker registry - go dockerRegistry.ListenAndServe() -} - -func (suite *RegistryClientTestSuite) TearDownSuite() { - os.RemoveAll(suite.CacheRootDir) -} - -func (suite *RegistryClientTestSuite) Test_0_Login() { - err := suite.RegistryClient.Login(suite.DockerRegistryHost, "badverybad", "ohsobad", false) - suite.NotNil(err, "error logging into registry with bad credentials") - - err = suite.RegistryClient.Login(suite.DockerRegistryHost, "badverybad", "ohsobad", true) - suite.NotNil(err, "error logging into registry with bad credentials, insecure mode") - - err = suite.RegistryClient.Login(suite.DockerRegistryHost, testUsername, testPassword, false) - suite.Nil(err, "no error logging into registry with good credentials") - - err = suite.RegistryClient.Login(suite.DockerRegistryHost, testUsername, testPassword, true) - suite.Nil(err, "no error logging into registry with good credentials, insecure mode") -} - -func (suite *RegistryClientTestSuite) Test_1_SaveChart() { - ref, err := ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) - suite.Nil(err) - - // empty chart - err = suite.RegistryClient.SaveChart(&chart.Chart{}, ref) - suite.NotNil(err) - - // valid chart - ch := &chart.Chart{} - ch.Metadata = &chart.Metadata{ - APIVersion: "v1", - Name: "testchart", - Version: "1.2.3", - } - err = suite.RegistryClient.SaveChart(ch, ref) - suite.Nil(err) -} - -func (suite *RegistryClientTestSuite) Test_2_LoadChart() { - - // non-existent ref - ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost)) - suite.Nil(err) - ch, err := suite.RegistryClient.LoadChart(ref) - suite.NotNil(err) - - // existing ref - ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) - suite.Nil(err) - ch, err = suite.RegistryClient.LoadChart(ref) - suite.Nil(err) - suite.Equal("testchart", ch.Metadata.Name) - suite.Equal("1.2.3", ch.Metadata.Version) -} - -func (suite *RegistryClientTestSuite) Test_3_PushChart() { - - // non-existent ref - ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost)) - suite.Nil(err) - err = suite.RegistryClient.PushChart(ref) - suite.NotNil(err) - - // existing ref - ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) - suite.Nil(err) - err = suite.RegistryClient.PushChart(ref) - suite.Nil(err) -} - -func (suite *RegistryClientTestSuite) Test_4_PullChart() { - - // non-existent ref - ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost)) - suite.Nil(err) - err = suite.RegistryClient.PullChart(ref) - suite.NotNil(err) - - // existing ref - ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) - suite.Nil(err) - err = suite.RegistryClient.PullChart(ref) - suite.Nil(err) -} - -func (suite *RegistryClientTestSuite) Test_5_PrintChartTable() { - err := suite.RegistryClient.PrintChartTable() - suite.Nil(err) -} - -func (suite *RegistryClientTestSuite) Test_6_RemoveChart() { - - // non-existent ref - ref, err := ParseReference(fmt.Sprintf("%s/testrepo/whodis:9.9.9", suite.DockerRegistryHost)) - suite.Nil(err) - err = suite.RegistryClient.RemoveChart(ref) - suite.NotNil(err) - - // existing ref - ref, err = ParseReference(fmt.Sprintf("%s/testrepo/testchart:1.2.3", suite.DockerRegistryHost)) - suite.Nil(err) - err = suite.RegistryClient.RemoveChart(ref) - suite.Nil(err) -} - -func (suite *RegistryClientTestSuite) Test_7_Logout() { - err := suite.RegistryClient.Logout("this-host-aint-real:5000") - suite.NotNil(err, "error logging out of registry that has no entry") - - err = suite.RegistryClient.Logout(suite.DockerRegistryHost) - suite.Nil(err, "no error logging out of registry") -} - -func TestRegistryClientTestSuite(t *testing.T) { - suite.Run(t, new(RegistryClientTestSuite)) -} - -// borrowed from https://github.com/phayes/freeport -func getFreePort() (int, error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - if err != nil { - return 0, err - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, err - } - defer l.Close() - return l.Addr().(*net.TCPAddr).Port, nil -} diff --git a/internal/experimental/registry/constants.go b/internal/experimental/registry/constants.go deleted file mode 100644 index dafb3c9..0000000 --- a/internal/experimental/registry/constants.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -const ( - // HelmChartConfigMediaType is the reserved media type for the Helm chart manifest config - HelmChartConfigMediaType = "application/vnd.cncf.helm.config.v1+json" - - // HelmChartContentLayerMediaType is the reserved media type for Helm chart package content - HelmChartContentLayerMediaType = "application/tar+gzip" -) - -// KnownMediaTypes returns a list of layer mediaTypes that the Helm client knows about -func KnownMediaTypes() []string { - return []string{ - HelmChartConfigMediaType, - HelmChartContentLayerMediaType, - } -} diff --git a/internal/experimental/registry/constants_test.go b/internal/experimental/registry/constants_test.go deleted file mode 100644 index 9f078e6..0000000 --- a/internal/experimental/registry/constants_test.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestConstants(t *testing.T) { - knownMediaTypes := KnownMediaTypes() - assert.Contains(t, knownMediaTypes, HelmChartConfigMediaType) - assert.Contains(t, knownMediaTypes, HelmChartContentLayerMediaType) -} diff --git a/internal/experimental/registry/reference.go b/internal/experimental/registry/reference.go deleted file mode 100644 index ced6cf3..0000000 --- a/internal/experimental/registry/reference.go +++ /dev/null @@ -1,145 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -import ( - "errors" - "fmt" - "net/url" - "regexp" - "strconv" - "strings" -) - -var ( - validPortRegEx = regexp.MustCompile(`^([1-9]\d{0,3}|0|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$`) // adapted from https://stackoverflow.com/a/12968117 - // TODO: Currently we don't support digests, so we are only splitting on the - // colon. However, when we add support for digests, we'll need to use the - // regexp anyway to split on both colons and @, so leaving it like this for - // now - referenceDelimiter = regexp.MustCompile(`[:]`) - errEmptyRepo = errors.New("parsed repo was empty") - errTooManyColons = errors.New("ref may only contain a single colon character (:) unless specifying a port number") -) - -type ( - // Reference defines the main components of a reference specification - Reference struct { - Tag string - Repo string - } -) - -// ParseReference converts a string to a Reference -func ParseReference(s string) (*Reference, error) { - if s == "" { - return nil, errEmptyRepo - } - // Split the components of the string on the colon or @, if it is more than 3, - // immediately return an error. Other validation will be performed later in - // the function - splitComponents := fixSplitComponents(referenceDelimiter.Split(s, -1)) - if len(splitComponents) > 3 { - return nil, errTooManyColons - } - - var ref *Reference - switch len(splitComponents) { - case 1: - ref = &Reference{Repo: splitComponents[0]} - case 2: - ref = &Reference{Repo: splitComponents[0], Tag: splitComponents[1]} - case 3: - ref = &Reference{Repo: strings.Join(splitComponents[:2], ":"), Tag: splitComponents[2]} - } - - // ensure the reference is valid - err := ref.validate() - if err != nil { - return nil, err - } - - return ref, nil -} - -// FullName the full name of a reference (repo:tag) -func (ref *Reference) FullName() string { - if ref.Tag == "" { - return ref.Repo - } - return fmt.Sprintf("%s:%s", ref.Repo, ref.Tag) -} - -// validate makes sure the ref meets our criteria -func (ref *Reference) validate() error { - err := ref.validateRepo() - if err != nil { - return err - } - return ref.validateNumColons() -} - -// validateRepo checks that the Repo field is non-empty -func (ref *Reference) validateRepo() error { - if ref.Repo == "" { - return errEmptyRepo - } - // Makes sure the repo results in a parsable URL (similar to what is done - // with containerd reference parsing) - _, err := url.Parse(ref.Repo) - return err -} - -// validateNumColon ensures the ref only contains a single colon character (:) -// (or potentially two, there might be a port number specified i.e. :5000) -func (ref *Reference) validateNumColons() error { - if strings.Contains(ref.Tag, ":") { - return errTooManyColons - } - parts := strings.Split(ref.Repo, ":") - lastIndex := len(parts) - 1 - if 1 < lastIndex { - return errTooManyColons - } - if 0 < lastIndex { - port := strings.Split(parts[lastIndex], "/")[0] - if !isValidPort(port) { - return errTooManyColons - } - } - return nil -} - -// isValidPort returns whether or not a string looks like a valid port -func isValidPort(s string) bool { - return validPortRegEx.MatchString(s) -} - -// fixSplitComponents this will modify reference parts based on presence of port -// Example: {localhost, 5000/x/y/z, 0.1.0} => {localhost:5000/x/y/z, 0.1.0} -func fixSplitComponents(c []string) []string { - if len(c) <= 1 { - return c - } - possiblePortParts := strings.Split(c[1], "/") - if _, err := strconv.Atoi(possiblePortParts[0]); err == nil { - components := []string{strings.Join(c[:2], ":")} - components = append(components, c[2:]...) - return components - } - return c -} diff --git a/internal/experimental/registry/reference_test.go b/internal/experimental/registry/reference_test.go deleted file mode 100644 index bb53eba..0000000 --- a/internal/experimental/registry/reference_test.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestParseReference(t *testing.T) { - is := assert.New(t) - - // bad refs - s := "" - _, err := ParseReference(s) - is.Error(err, "empty ref") - - s = "my:bad:ref" - _, err = ParseReference(s) - is.Error(err, "ref contains too many colons (2)") - - s = "my:really:bad:ref" - _, err = ParseReference(s) - is.Error(err, "ref contains too many colons (3)") - - // good refs - s = "mychart" - ref, err := ParseReference(s) - is.NoError(err) - is.Equal("mychart", ref.Repo) - is.Equal("", ref.Tag) - is.Equal("mychart", ref.FullName()) - - s = "mychart:1.5.0" - ref, err = ParseReference(s) - is.NoError(err) - is.Equal("mychart", ref.Repo) - is.Equal("1.5.0", ref.Tag) - is.Equal("mychart:1.5.0", ref.FullName()) - - s = "myrepo/mychart" - ref, err = ParseReference(s) - is.NoError(err) - is.Equal("myrepo/mychart", ref.Repo) - is.Equal("", ref.Tag) - is.Equal("myrepo/mychart", ref.FullName()) - - s = "myrepo/mychart:1.5.0" - ref, err = ParseReference(s) - is.NoError(err) - is.Equal("myrepo/mychart", ref.Repo) - is.Equal("1.5.0", ref.Tag) - is.Equal("myrepo/mychart:1.5.0", ref.FullName()) - - s = "mychart:5001:1.5.0" - ref, err = ParseReference(s) - is.NoError(err) - is.Equal("mychart:5001", ref.Repo) - is.Equal("1.5.0", ref.Tag) - is.Equal("mychart:5001:1.5.0", ref.FullName()) - - s = "myrepo:5001/mychart:1.5.0" - ref, err = ParseReference(s) - is.NoError(err) - is.Equal("myrepo:5001/mychart", ref.Repo) - is.Equal("1.5.0", ref.Tag) - is.Equal("myrepo:5001/mychart:1.5.0", ref.FullName()) - - s = "localhost:5000/mychart:latest" - ref, err = ParseReference(s) - is.NoError(err) - is.Equal("localhost:5000/mychart", ref.Repo) - is.Equal("latest", ref.Tag) - is.Equal("localhost:5000/mychart:latest", ref.FullName()) - - s = "my.host.com/my/nested/repo:1.2.3" - ref, err = ParseReference(s) - is.NoError(err) - is.Equal("my.host.com/my/nested/repo", ref.Repo) - is.Equal("1.2.3", ref.Tag) - is.Equal("my.host.com/my/nested/repo:1.2.3", ref.FullName()) - - s = "localhost:5000/x/y/z" - ref, err = ParseReference(s) - is.NoError(err) - is.Equal("localhost:5000/x/y/z", ref.Repo) - is.Equal("", ref.Tag) - is.Equal("localhost:5000/x/y/z", ref.FullName()) - - s = "localhost:5000/x/y/z:123" - ref, err = ParseReference(s) - is.NoError(err) - is.Equal("localhost:5000/x/y/z", ref.Repo) - is.Equal("123", ref.Tag) - is.Equal("localhost:5000/x/y/z:123", ref.FullName()) - - s = "localhost:5000/x/y/z:123:x" - _, err = ParseReference(s) - is.Error(err, "ref contains too many colons (3)") - - s = "localhost:5000/x/y/z:123:x:y" - _, err = ParseReference(s) - is.Error(err, "ref contains too many colons (4)") -} diff --git a/internal/experimental/registry/resolver.go b/internal/experimental/registry/resolver.go deleted file mode 100644 index ff8a826..0000000 --- a/internal/experimental/registry/resolver.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -import ( - "github.com/containerd/containerd/remotes" -) - -type ( - // Resolver provides remotes based on a locator - Resolver struct { - remotes.Resolver - } -) diff --git a/internal/experimental/registry/util.go b/internal/experimental/registry/util.go deleted file mode 100644 index 697a890..0000000 --- a/internal/experimental/registry/util.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package registry // import "helm.sh/helm/v3/internal/experimental/registry" - -import ( - "context" - "fmt" - "io" - "time" - - orascontext "github.com/deislabs/oras/pkg/context" - units "github.com/docker/go-units" - "github.com/sirupsen/logrus" -) - -// byteCountBinary produces a human-readable file size -func byteCountBinary(b int64) string { - const unit = 1024 - if b < unit { - return fmt.Sprintf("%d B", b) - } - div, exp := int64(unit), 0 - for n := b / unit; n >= unit; n /= unit { - div *= unit - exp++ - } - return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp]) -} - -// shortDigest returns first 7 characters of a sha256 digest -func shortDigest(digest string) string { - if len(digest) == 64 { - return digest[:7] - } - return digest -} - -// timeAgo returns a human-readable timestamp representing time that has passed -func timeAgo(t time.Time) string { - return units.HumanDuration(time.Now().UTC().Sub(t)) -} - -// ctx retrieves a fresh context. -// disable verbose logging coming from ORAS (unless debug is enabled) -func ctx(out io.Writer, debug bool) context.Context { - if !debug { - return orascontext.Background() - } - ctx := orascontext.WithLoggerFromWriter(context.Background(), out) - orascontext.GetLogger(ctx).Logger.SetLevel(logrus.DebugLevel) - return ctx -} diff --git a/internal/ignore/doc.go b/internal/ignore/doc.go deleted file mode 100644 index e6a6a6c..0000000 --- a/internal/ignore/doc.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package ignore provides tools for writing ignore files (a la .gitignore). - -This provides both an ignore parser and a file-aware processor. - -The format of ignore files closely follows, but does not exactly match, the -format for .gitignore files (https://git-scm.com/docs/gitignore). - -The formatting rules are as follows: - - - Parsing is line-by-line - - Empty lines are ignored - - Lines the begin with # (comments) will be ignored - - Leading and trailing spaces are always ignored - - Inline comments are NOT supported ('foo* # Any foo' does not contain a comment) - - There is no support for multi-line patterns - - Shell glob patterns are supported. See Go's "path/filepath".Match - - If a pattern begins with a leading !, the match will be negated. - - If a pattern begins with a leading /, only paths relatively rooted will match. - - If the pattern ends with a trailing /, only directories will match - - If a pattern contains no slashes, file basenames are tested (not paths) - - The pattern sequence "**", while legal in a glob, will cause an error here - (to indicate incompatibility with .gitignore). - -Example: - - # Match any file named foo.txt - foo.txt - - # Match any text file - *.txt - - # Match only directories named mydir - mydir/ - - # Match only text files in the top-level directory - /*.txt - - # Match only the file foo.txt in the top-level directory - /foo.txt - - # Match any file named ab.txt, ac.txt, or ad.txt - a[b-d].txt - -Notable differences from .gitignore: - - The '**' syntax is not supported. - - The globbing library is Go's 'filepath.Match', not fnmatch(3) - - Trailing spaces are always ignored (there is no supported escape sequence) - - The evaluation of escape sequences has not been tested for compatibility - - There is no support for '\!' as a special leading sequence. -*/ -package ignore // import "helm.sh/helm/v3/internal/ignore" diff --git a/internal/ignore/rules.go b/internal/ignore/rules.go deleted file mode 100644 index 9049aff..0000000 --- a/internal/ignore/rules.go +++ /dev/null @@ -1,217 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package ignore - -import ( - "bufio" - "io" - "log" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" -) - -// HelmIgnore default name of an ignorefile. -const HelmIgnore = ".helmignore" - -// Rules is a collection of path matching rules. -// -// Parse() and ParseFile() will construct and populate new Rules. -// Empty() will create an immutable empty ruleset. -type Rules struct { - patterns []*pattern -} - -// Empty builds an empty ruleset. -func Empty() *Rules { - return &Rules{patterns: []*pattern{}} -} - -// AddDefaults adds default ignore patterns. -// -// Ignore all dotfiles in "templates/" -func (r *Rules) AddDefaults() { - r.parseRule(`templates/.?*`) -} - -// ParseFile parses a helmignore file and returns the *Rules. -func ParseFile(file string) (*Rules, error) { - f, err := os.Open(file) - if err != nil { - return nil, err - } - defer f.Close() - return Parse(f) -} - -// Parse parses a rules file -func Parse(file io.Reader) (*Rules, error) { - r := &Rules{patterns: []*pattern{}} - - s := bufio.NewScanner(file) - for s.Scan() { - if err := r.parseRule(s.Text()); err != nil { - return r, err - } - } - return r, s.Err() -} - -// Ignore evaluates the file at the given path, and returns true if it should be ignored. -// -// Ignore evaluates path against the rules in order. Evaluation stops when a match -// is found. Matching a negative rule will stop evaluation. -func (r *Rules) Ignore(path string, fi os.FileInfo) bool { - // Don't match on empty dirs. - if path == "" { - return false - } - - // Disallow ignoring the current working directory. - // See issue: - // 1776 (New York City) Hamilton: "Pardon me, are you Aaron Burr, sir?" - if path == "." || path == "./" { - return false - } - for _, p := range r.patterns { - if p.match == nil { - log.Printf("ignore: no matcher supplied for %q", p.raw) - return false - } - - // For negative rules, we need to capture and return non-matches, - // and continue for matches. - if p.negate { - if p.mustDir && !fi.IsDir() { - return true - } - if !p.match(path, fi) { - return true - } - continue - } - - // If the rule is looking for directories, and this is not a directory, - // skip it. - if p.mustDir && !fi.IsDir() { - continue - } - if p.match(path, fi) { - return true - } - } - return false -} - -// parseRule parses a rule string and creates a pattern, which is then stored in the Rules object. -func (r *Rules) parseRule(rule string) error { - rule = strings.TrimSpace(rule) - - // Ignore blank lines - if rule == "" { - return nil - } - // Comment - if strings.HasPrefix(rule, "#") { - return nil - } - - // Fail any rules that contain ** - if strings.Contains(rule, "**") { - return errors.New("double-star (**) syntax is not supported") - } - - // Fail any patterns that can't compile. A non-empty string must be - // given to Match() to avoid optimization that skips rule evaluation. - if _, err := filepath.Match(rule, "abc"); err != nil { - return err - } - - p := &pattern{raw: rule} - - // Negation is handled at a higher level, so strip the leading ! from the - // string. - if strings.HasPrefix(rule, "!") { - p.negate = true - rule = rule[1:] - } - - // Directory verification is handled by a higher level, so the trailing / - // is removed from the rule. That way, a directory named "foo" matches, - // even if the supplied string does not contain a literal slash character. - if strings.HasSuffix(rule, "/") { - p.mustDir = true - rule = strings.TrimSuffix(rule, "/") - } - - if strings.HasPrefix(rule, "/") { - // Require path matches the root path. - p.match = func(n string, fi os.FileInfo) bool { - rule = strings.TrimPrefix(rule, "/") - ok, err := filepath.Match(rule, n) - if err != nil { - log.Printf("Failed to compile %q: %s", rule, err) - return false - } - return ok - } - } else if strings.Contains(rule, "/") { - // require structural match. - p.match = func(n string, fi os.FileInfo) bool { - ok, err := filepath.Match(rule, n) - if err != nil { - log.Printf("Failed to compile %q: %s", rule, err) - return false - } - return ok - } - } else { - p.match = func(n string, fi os.FileInfo) bool { - // When there is no slash in the pattern, we evaluate ONLY the - // filename. - n = filepath.Base(n) - ok, err := filepath.Match(rule, n) - if err != nil { - log.Printf("Failed to compile %q: %s", rule, err) - return false - } - return ok - } - } - - r.patterns = append(r.patterns, p) - return nil -} - -// matcher is a function capable of computing a match. -// -// It returns true if the rule matches. -type matcher func(name string, fi os.FileInfo) bool - -// pattern describes a pattern to be matched in a rule set. -type pattern struct { - // raw is the unparsed string, with nothing stripped. - raw string - // match is the matcher function. - match matcher - // negate indicates that the rule's outcome should be negated. - negate bool - // mustDir indicates that the matched file must be a directory. - mustDir bool -} diff --git a/internal/ignore/rules_test.go b/internal/ignore/rules_test.go deleted file mode 100644 index 9581cf0..0000000 --- a/internal/ignore/rules_test.go +++ /dev/null @@ -1,155 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package ignore - -import ( - "bytes" - "os" - "path/filepath" - "testing" -) - -var testdata = "./testdata" - -func TestParse(t *testing.T) { - rules := `#ignore - - #ignore -foo -bar/* -baz/bar/foo.txt - -one/more -` - r, err := parseString(rules) - if err != nil { - t.Fatalf("Error parsing rules: %s", err) - } - - if len(r.patterns) != 4 { - t.Errorf("Expected 4 rules, got %d", len(r.patterns)) - } - - expects := []string{"foo", "bar/*", "baz/bar/foo.txt", "one/more"} - for i, p := range r.patterns { - if p.raw != expects[i] { - t.Errorf("Expected %q, got %q", expects[i], p.raw) - } - if p.match == nil { - t.Errorf("Expected %s to have a matcher function.", p.raw) - } - } -} - -func TestParseFail(t *testing.T) { - shouldFail := []string{"foo/**/bar", "[z-"} - for _, fail := range shouldFail { - _, err := parseString(fail) - if err == nil { - t.Errorf("Rule %q should have failed", fail) - } - } -} - -func TestParseFile(t *testing.T) { - f := filepath.Join(testdata, HelmIgnore) - if _, err := os.Stat(f); err != nil { - t.Fatalf("Fixture %s missing: %s", f, err) - } - - r, err := ParseFile(f) - if err != nil { - t.Fatalf("Failed to parse rules file: %s", err) - } - - if len(r.patterns) != 3 { - t.Errorf("Expected 3 patterns, got %d", len(r.patterns)) - } -} - -func TestIgnore(t *testing.T) { - // Test table: Given pattern and name, Ignore should return expect. - tests := []struct { - pattern string - name string - expect bool - }{ - // Glob tests - {`helm.txt`, "helm.txt", true}, - {`helm.*`, "helm.txt", true}, - {`helm.*`, "rudder.txt", false}, - {`*.txt`, "tiller.txt", true}, - {`*.txt`, "cargo/a.txt", true}, - {`cargo/*.txt`, "cargo/a.txt", true}, - {`cargo/*.*`, "cargo/a.txt", true}, - {`cargo/*.txt`, "mast/a.txt", false}, - {`ru[c-e]?er.txt`, "rudder.txt", true}, - {`templates/.?*`, "templates/.dotfile", true}, - // "." should never get ignored. https://github.com/helm/helm/issues/1776 - {`.*`, ".", false}, - {`.*`, "./", false}, - {`.*`, ".joonix", true}, - {`.*`, "helm.txt", false}, - {`.*`, "", false}, - - // Directory tests - {`cargo/`, "cargo", true}, - {`cargo/`, "cargo/", true}, - {`cargo/`, "mast/", false}, - {`helm.txt/`, "helm.txt", false}, - - // Negation tests - {`!helm.txt`, "helm.txt", false}, - {`!helm.txt`, "tiller.txt", true}, - {`!*.txt`, "cargo", true}, - {`!cargo/`, "mast/", true}, - - // Absolute path tests - {`/a.txt`, "a.txt", true}, - {`/a.txt`, "cargo/a.txt", false}, - {`/cargo/a.txt`, "cargo/a.txt", true}, - } - - for _, test := range tests { - r, err := parseString(test.pattern) - if err != nil { - t.Fatalf("Failed to parse: %s", err) - } - fi, err := os.Stat(filepath.Join(testdata, test.name)) - if err != nil { - t.Fatalf("Fixture missing: %s", err) - } - - if r.Ignore(test.name, fi) != test.expect { - t.Errorf("Expected %q to be %v for pattern %q", test.name, test.expect, test.pattern) - } - } -} - -func TestAddDefaults(t *testing.T) { - r := Rules{} - r.AddDefaults() - - if len(r.patterns) != 1 { - t.Errorf("Expected 1 default patterns, got %d", len(r.patterns)) - } -} - -func parseString(str string) (*Rules, error) { - b := bytes.NewBuffer([]byte(str)) - return Parse(b) -} diff --git a/internal/ignore/testdata/.helmignore b/internal/ignore/testdata/.helmignore deleted file mode 100644 index b2693ba..0000000 --- a/internal/ignore/testdata/.helmignore +++ /dev/null @@ -1,3 +0,0 @@ -mast/a.txt -.DS_Store -.git diff --git a/internal/ignore/testdata/.joonix b/internal/ignore/testdata/.joonix deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/a.txt b/internal/ignore/testdata/a.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/cargo/a.txt b/internal/ignore/testdata/cargo/a.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/cargo/b.txt b/internal/ignore/testdata/cargo/b.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/cargo/c.txt b/internal/ignore/testdata/cargo/c.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/helm.txt b/internal/ignore/testdata/helm.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/mast/a.txt b/internal/ignore/testdata/mast/a.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/mast/b.txt b/internal/ignore/testdata/mast/b.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/mast/c.txt b/internal/ignore/testdata/mast/c.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/rudder.txt b/internal/ignore/testdata/rudder.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/templates/.dotfile b/internal/ignore/testdata/templates/.dotfile deleted file mode 100644 index e69de29..0000000 diff --git a/internal/ignore/testdata/tiller.txt b/internal/ignore/testdata/tiller.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/monocular/client.go b/internal/monocular/client.go deleted file mode 100644 index 88a2564..0000000 --- a/internal/monocular/client.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package monocular - -import ( - "errors" - "net/url" -) - -// ErrHostnameNotProvided indicates the url is missing a hostname -var ErrHostnameNotProvided = errors.New("no hostname provided") - -// Client represents a client capable of communicating with the Monocular API. -type Client struct { - - // The base URL for requests - BaseURL string - - // The internal logger to use - Log func(string, ...interface{}) -} - -// New creates a new client -func New(u string) (*Client, error) { - - // Validate we have a URL - if err := validate(u); err != nil { - return nil, err - } - - return &Client{ - BaseURL: u, - Log: nopLogger, - }, nil -} - -var nopLogger = func(_ string, _ ...interface{}) {} - -// Validate if the base URL for monocular is valid. -func validate(u string) error { - - // Check if it is parsable - p, err := url.Parse(u) - if err != nil { - return err - } - - // Check that a host is attached - if p.Hostname() == "" { - return ErrHostnameNotProvided - } - - return nil -} diff --git a/internal/monocular/client_test.go b/internal/monocular/client_test.go deleted file mode 100644 index abf914e..0000000 --- a/internal/monocular/client_test.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package monocular - -import ( - "testing" -) - -func TestNew(t *testing.T) { - c, err := New("https://hub.helm.sh") - if err != nil { - t.Errorf("error creating client: %s", err) - } - if c.BaseURL != "https://hub.helm.sh" { - t.Errorf("incorrect BaseURL. Expected \"https://hub.helm.sh\" but got %q", c.BaseURL) - } -} diff --git a/internal/monocular/doc.go b/internal/monocular/doc.go deleted file mode 100644 index 485cfdd..0000000 --- a/internal/monocular/doc.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package monocular contains the logic for interacting with monocular instances -// like the Helm Hub. -// -// This is a library for interacting with monocular -package monocular diff --git a/internal/monocular/search.go b/internal/monocular/search.go deleted file mode 100644 index 10e1f21..0000000 --- a/internal/monocular/search.go +++ /dev/null @@ -1,139 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package monocular - -import ( - "encoding/json" - "fmt" - "net/http" - "net/url" - "path" - "time" - - "helm.sh/helm/v3/internal/version" - "helm.sh/helm/v3/pkg/chart" -) - -// SearchPath is the url path to the search API in monocular. -const SearchPath = "api/chartsvc/v1/charts/search" - -// The structs below represent the structure of the response from the monocular -// search API. The structs were not imported from monocular because monocular -// imports from Helm v2 (avoiding circular version dependency) and the mappings -// are slightly different (monocular search results do not directly reflect -// the struct definitions). - -// SearchResult represents an individual chart result -type SearchResult struct { - ID string `json:"id"` - Type string `json:"type"` - Attributes Chart `json:"attributes"` - Links Links `json:"links"` - Relationships Relationships `json:"relationships"` -} - -// Chart is the attributes for the chart -type Chart struct { - Name string `json:"name"` - Repo Repo `json:"repo"` - Description string `json:"description"` - Home string `json:"home"` - Keywords []string `json:"keywords"` - Maintainers []chart.Maintainer `json:"maintainers"` - Sources []string `json:"sources"` - Icon string `json:"icon"` -} - -// Repo contains the name in monocular the url for the repository -type Repo struct { - Name string `json:"name"` - URL string `json:"url"` -} - -// Links provides a set of links relative to the chartsvc base -type Links struct { - Self string `json:"self"` -} - -// Relationships provides information on the latest version of the chart -type Relationships struct { - LatestChartVersion LatestChartVersion `json:"latestChartVersion"` -} - -// LatestChartVersion provides the details on the latest version of the chart -type LatestChartVersion struct { - Data ChartVersion `json:"data"` - Links Links `json:"links"` -} - -// ChartVersion provides the specific data on the chart version -type ChartVersion struct { - Version string `json:"version"` - AppVersion string `json:"app_version"` - Created time.Time `json:"created"` - Digest string `json:"digest"` - Urls []string `json:"urls"` - Readme string `json:"readme"` - Values string `json:"values"` -} - -// Search performs a search against the monocular search API -func (c *Client) Search(term string) ([]SearchResult, error) { - - // Create the URL to the search endpoint - // Note, this is currently an internal API for the Hub. This should be - // formatted without showing how monocular operates. - p, err := url.Parse(c.BaseURL) - if err != nil { - return nil, err - } - - // Set the path to the monocular API endpoint for search - p.Path = path.Join(p.Path, SearchPath) - - p.RawQuery = "q=" + url.QueryEscape(term) - - // Create request - req, err := http.NewRequest("GET", p.String(), nil) - if err != nil { - return nil, err - } - - // Set the user agent so that monocular can identify where the request - // is coming from - req.Header.Set("User-Agent", version.GetUserAgent()) - - res, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer res.Body.Close() - - if res.StatusCode != 200 { - return nil, fmt.Errorf("failed to fetch %s : %s", p.String(), res.Status) - } - - result := &searchResponse{} - - json.NewDecoder(res.Body).Decode(result) - - return result.Data, nil -} - -type searchResponse struct { - Data []SearchResult `json:"data"` -} diff --git a/internal/monocular/search_test.go b/internal/monocular/search_test.go deleted file mode 100644 index 3e296f2..0000000 --- a/internal/monocular/search_test.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package monocular - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" -) - -// A search response for phpmyadmin containing 2 results -var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://kubernetes-charts.storage.googleapis.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://kubernetes-charts.storage.googleapis.com/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}` - -func TestSearch(t *testing.T) { - - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, searchResult) - })) - defer ts.Close() - - c, err := New(ts.URL) - if err != nil { - t.Errorf("unable to create monocular client: %s", err) - } - - results, err := c.Search("phpmyadmin") - if err != nil { - t.Errorf("unable to search monocular: %s", err) - } - - if len(results) != 2 { - t.Error("Did not receive the expected number of results") - } -} diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go deleted file mode 100644 index e4dbab2..0000000 --- a/internal/resolver/resolver.go +++ /dev/null @@ -1,183 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resolver - -import ( - "bytes" - "encoding/json" - "os" - "path/filepath" - "strings" - "time" - - "github.com/Masterminds/semver/v3" - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/provenance" - "helm.sh/helm/v3/pkg/repo" -) - -// Resolver resolves dependencies from semantic version ranges to a particular version. -type Resolver struct { - chartpath string - cachepath string -} - -// New creates a new resolver for a given chart and a given helm home. -func New(chartpath, cachepath string) *Resolver { - return &Resolver{ - chartpath: chartpath, - cachepath: cachepath, - } -} - -// Resolve resolves dependencies and returns a lock file with the resolution. -func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string) (*chart.Lock, error) { - - // Now we clone the dependencies, locking as we go. - locked := make([]*chart.Dependency, len(reqs)) - missing := []string{} - for i, d := range reqs { - if d.Repository == "" { - // Local chart subfolder - if _, err := GetLocalPath(filepath.Join("charts", d.Name), r.chartpath); err != nil { - return nil, err - } - - locked[i] = &chart.Dependency{ - Name: d.Name, - Repository: "", - Version: d.Version, - } - continue - } - if strings.HasPrefix(d.Repository, "file://") { - - if _, err := GetLocalPath(d.Repository, r.chartpath); err != nil { - return nil, err - } - - locked[i] = &chart.Dependency{ - Name: d.Name, - Repository: d.Repository, - Version: d.Version, - } - continue - } - constraint, err := semver.NewConstraint(d.Version) - if err != nil { - return nil, errors.Wrapf(err, "dependency %q has an invalid version/constraint format", d.Name) - } - - repoName := repoNames[d.Name] - // if the repository was not defined, but the dependency defines a repository url, bypass the cache - if repoName == "" && d.Repository != "" { - locked[i] = &chart.Dependency{ - Name: d.Name, - Repository: d.Repository, - Version: d.Version, - } - continue - } - - repoIndex, err := repo.LoadIndexFile(filepath.Join(r.cachepath, helmpath.CacheIndexFile(repoName))) - if err != nil { - return nil, errors.Wrapf(err, "no cached repository for %s found. (try 'helm repo update')", repoName) - } - - vs, ok := repoIndex.Entries[d.Name] - if !ok { - return nil, errors.Errorf("%s chart not found in repo %s", d.Name, d.Repository) - } - - locked[i] = &chart.Dependency{ - Name: d.Name, - Repository: d.Repository, - } - found := false - // The version are already sorted and hence the first one to satisfy the constraint is used - for _, ver := range vs { - v, err := semver.NewVersion(ver.Version) - if err != nil || len(ver.URLs) == 0 { - // Not a legit entry. - continue - } - if constraint.Check(v) { - found = true - locked[i].Version = v.Original() - break - } - } - - if !found { - missing = append(missing, d.Name) - } - } - if len(missing) > 0 { - return nil, errors.Errorf("can't get a valid version for repositories %s. Try changing the version constraint in Chart.yaml", strings.Join(missing, ", ")) - } - - digest, err := HashReq(reqs, locked) - if err != nil { - return nil, err - } - - return &chart.Lock{ - Generated: time.Now(), - Digest: digest, - Dependencies: locked, - }, nil -} - -// HashReq generates a hash of the dependencies. -// -// This should be used only to compare against another hash generated by this -// function. -func HashReq(req, lock []*chart.Dependency) (string, error) { - data, err := json.Marshal([2][]*chart.Dependency{req, lock}) - if err != nil { - return "", err - } - s, err := provenance.Digest(bytes.NewBuffer(data)) - return "sha256:" + s, err -} - -// GetLocalPath generates absolute local path when use -// "file://" in repository of dependencies -func GetLocalPath(repo, chartpath string) (string, error) { - var depPath string - var err error - p := strings.TrimPrefix(repo, "file://") - - // root path is absolute - if strings.HasPrefix(p, "/") { - if depPath, err = filepath.Abs(p); err != nil { - return "", err - } - } else { - depPath = filepath.Join(chartpath, p) - } - - if _, err = os.Stat(depPath); os.IsNotExist(err) { - return "", errors.Errorf("directory %s not found", depPath) - } else if err != nil { - return "", err - } - - return depPath, nil -} diff --git a/internal/resolver/resolver_test.go b/internal/resolver/resolver_test.go deleted file mode 100644 index d93d616..0000000 --- a/internal/resolver/resolver_test.go +++ /dev/null @@ -1,276 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resolver - -import ( - "testing" - - "helm.sh/helm/v3/pkg/chart" -) - -func TestResolve(t *testing.T) { - tests := []struct { - name string - req []*chart.Dependency - expect *chart.Lock - err bool - }{ - { - name: "version failure", - req: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com", Version: ">a1"}, - }, - err: true, - }, - { - name: "cache index failure", - req: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com", Version: "1.0.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com", Version: "1.0.0"}, - }, - }, - }, - { - name: "chart not found failure", - req: []*chart.Dependency{ - {Name: "redis", Repository: "http://example.com", Version: "1.0.0"}, - }, - err: true, - }, - { - name: "constraint not satisfied failure", - req: []*chart.Dependency{ - {Name: "alpine", Repository: "http://example.com", Version: ">=1.0.0"}, - }, - err: true, - }, - { - name: "valid lock", - req: []*chart.Dependency{ - {Name: "alpine", Repository: "http://example.com", Version: ">=0.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "alpine", Repository: "http://example.com", Version: "0.2.0"}, - }, - }, - }, - { - name: "repo from valid local path", - req: []*chart.Dependency{ - {Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, - }, - }, - }, - { - name: "repo from invalid local path", - req: []*chart.Dependency{ - {Name: "notexist", Repository: "file://../testdata/notexist", Version: "0.1.0"}, - }, - err: true, - }, - { - name: "repo from valid path under charts path", - req: []*chart.Dependency{ - {Name: "localdependency", Repository: "", Version: "0.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "localdependency", Repository: "", Version: "0.1.0"}, - }, - }, - }, - { - name: "repo from invalid path under charts path", - req: []*chart.Dependency{ - {Name: "nonexistentdependency", Repository: "", Version: "0.1.0"}, - }, - expect: &chart.Lock{ - Dependencies: []*chart.Dependency{ - {Name: "nonexistentlocaldependency", Repository: "", Version: "0.1.0"}, - }, - }, - err: true, - }, - } - - repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"} - r := New("testdata/chartpath", "testdata/repository") - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - l, err := r.Resolve(tt.req, repoNames) - if err != nil { - if tt.err { - return - } - t.Fatal(err) - } - - if tt.err { - t.Fatalf("Expected error in test %q", tt.name) - } - - if h, err := HashReq(tt.req, tt.expect.Dependencies); err != nil { - t.Fatal(err) - } else if h != l.Digest { - t.Errorf("%q: hashes don't match.", tt.name) - } - - // Check fields. - if len(l.Dependencies) != len(tt.req) { - t.Errorf("%s: wrong number of dependencies in lock", tt.name) - } - d0 := l.Dependencies[0] - e0 := tt.expect.Dependencies[0] - if d0.Name != e0.Name { - t.Errorf("%s: expected name %s, got %s", tt.name, e0.Name, d0.Name) - } - if d0.Repository != e0.Repository { - t.Errorf("%s: expected repo %s, got %s", tt.name, e0.Repository, d0.Repository) - } - if d0.Version != e0.Version { - t.Errorf("%s: expected version %s, got %s", tt.name, e0.Version, d0.Version) - } - }) - } -} - -func TestHashReq(t *testing.T) { - expect := "sha256:fb239e836325c5fa14b29d1540a13b7d3ba13151b67fe719f820e0ef6d66aaaf" - - tests := []struct { - name string - chartVersion string - lockVersion string - wantError bool - }{ - { - name: "chart with the expected digest", - chartVersion: "0.1.0", - lockVersion: "0.1.0", - wantError: false, - }, - { - name: "ranged version but same resolved lock version", - chartVersion: "^0.1.0", - lockVersion: "0.1.0", - wantError: true, - }, - { - name: "ranged version resolved as higher version", - chartVersion: "^0.1.0", - lockVersion: "0.1.2", - wantError: true, - }, - { - name: "different version", - chartVersion: "0.1.2", - lockVersion: "0.1.2", - wantError: true, - }, - { - name: "different version with a range", - chartVersion: "^0.1.2", - lockVersion: "0.1.2", - wantError: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - req := []*chart.Dependency{ - {Name: "alpine", Version: tt.chartVersion, Repository: "http://localhost:8879/charts"}, - } - lock := []*chart.Dependency{ - {Name: "alpine", Version: tt.lockVersion, Repository: "http://localhost:8879/charts"}, - } - h, err := HashReq(req, lock) - if err != nil { - t.Fatal(err) - } - if !tt.wantError && expect != h { - t.Errorf("Expected %q, got %q", expect, h) - } else if tt.wantError && expect == h { - t.Errorf("Expected not %q, but same", expect) - } - }) - } -} - -func TestGetLocalPath(t *testing.T) { - tests := []struct { - name string - repo string - chartpath string - expect string - err bool - }{ - { - name: "absolute path", - repo: "file:////proc", - expect: "/proc", - }, - { - name: "relative path", - repo: "file://../../../../cmd/helm/testdata/testcharts/signtest", - chartpath: "foo/bar", - expect: "../../cmd/helm/testdata/testcharts/signtest", - }, - { - name: "current directory path", - repo: "../charts/localdependency", - chartpath: "testdata/chartpath/charts", - expect: "testdata/chartpath/charts/localdependency", - }, - { - name: "invalid local path", - repo: "file://../testdata/notexist", - chartpath: "testdata/chartpath", - err: true, - }, - { - name: "invalid path under current directory", - repo: "../charts/nonexistentdependency", - chartpath: "testdata/chartpath/charts", - err: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p, err := GetLocalPath(tt.repo, tt.chartpath) - if err != nil { - if tt.err { - return - } - t.Fatal(err) - } - if tt.err { - t.Fatalf("Expected error in test %q", tt.name) - } - if p != tt.expect { - t.Errorf("%q: expected %q, got %q", tt.name, tt.expect, p) - } - }) - } -} diff --git a/internal/resolver/testdata/chartpath/charts/localdependency/Chart.yaml b/internal/resolver/testdata/chartpath/charts/localdependency/Chart.yaml deleted file mode 100644 index 083c51e..0000000 --- a/internal/resolver/testdata/chartpath/charts/localdependency/Chart.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: A Helm chart for Kubernetes -name: localdependency -version: 0.1.0 diff --git a/internal/resolver/testdata/repository/kubernetes-charts-index.yaml b/internal/resolver/testdata/repository/kubernetes-charts-index.yaml deleted file mode 100644 index 98370fc..0000000 --- a/internal/resolver/testdata/repository/kubernetes-charts-index.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: v1 -entries: - alpine: - - name: alpine - urls: - - https://kubernetes-charts.storage.googleapis.com/alpine-0.1.0.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.2.0 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - - name: alpine - urls: - - https://kubernetes-charts.storage.googleapis.com/alpine-0.2.0.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.1.0 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - mariadb: - - name: mariadb - urls: - - https://kubernetes-charts.storage.googleapis.com/mariadb-0.3.0.tgz - checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56 - home: https://mariadb.org - sources: - - https://github.com/bitnami/bitnami-docker-mariadb - version: 0.3.0 - description: Chart for MariaDB - keywords: - - mariadb - - mysql - - database - - sql - maintainers: - - name: Bitnami - email: containers@bitnami.com - icon: "" diff --git a/internal/sympath/walk.go b/internal/sympath/walk.go deleted file mode 100644 index 752526f..0000000 --- a/internal/sympath/walk.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright (c) for portions of walk.go are held by The Go Authors, 2009 and are -provided under the BSD license. - -https://github.com/golang/go/blob/master/LICENSE - -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package sympath - -import ( - "log" - "os" - "path/filepath" - "sort" - - "github.com/pkg/errors" -) - -// Walk walks the file tree rooted at root, calling walkFn for each file or directory -// in the tree, including root. All errors that arise visiting files and directories -// are filtered by walkFn. The files are walked in lexical order, which makes the -// output deterministic but means that for very large directories Walk can be -// inefficient. Walk follows symbolic links. -func Walk(root string, walkFn filepath.WalkFunc) error { - info, err := os.Lstat(root) - if err != nil { - err = walkFn(root, nil, err) - } else { - err = symwalk(root, info, walkFn) - } - if err == filepath.SkipDir { - return nil - } - return err -} - -// readDirNames reads the directory named by dirname and returns -// a sorted list of directory entries. -func readDirNames(dirname string) ([]string, error) { - f, err := os.Open(dirname) - if err != nil { - return nil, err - } - names, err := f.Readdirnames(-1) - f.Close() - if err != nil { - return nil, err - } - sort.Strings(names) - return names, nil -} - -// symwalk recursively descends path, calling walkFn. -func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { - // Recursively walk symlinked directories. - if IsSymlink(info) { - resolved, err := filepath.EvalSymlinks(path) - if err != nil { - return errors.Wrapf(err, "error evaluating symlink %s", path) - } - log.Printf("found symbolic link in path: %s resolves to %s", path, resolved) - if info, err = os.Lstat(resolved); err != nil { - return err - } - if err := symwalk(path, info, walkFn); err != nil && err != filepath.SkipDir { - return err - } - return nil - } - - if err := walkFn(path, info, nil); err != nil { - return err - } - - if !info.IsDir() { - return nil - } - - names, err := readDirNames(path) - if err != nil { - return walkFn(path, info, err) - } - - for _, name := range names { - filename := filepath.Join(path, name) - fileInfo, err := os.Lstat(filename) - if err != nil { - if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { - return err - } - } else { - err = symwalk(filename, fileInfo, walkFn) - if err != nil { - if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || err != filepath.SkipDir { - return err - } - } - } - } - return nil -} - -// IsSymlink is used to determine if the fileinfo is a symbolic link. -func IsSymlink(fi os.FileInfo) bool { - return fi.Mode()&os.ModeSymlink != 0 -} diff --git a/internal/sympath/walk_test.go b/internal/sympath/walk_test.go deleted file mode 100644 index 25f7371..0000000 --- a/internal/sympath/walk_test.go +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright (c) for portions of walk_test.go are held by The Go Authors, 2009 and are -provided under the BSD license. - -https://github.com/golang/go/blob/master/LICENSE - -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package sympath - -import ( - "os" - "path/filepath" - "testing" -) - -type Node struct { - name string - entries []*Node // nil if the entry is a file - marks int - expectedMarks int - symLinkedTo string -} - -var tree = &Node{ - "testdata", - []*Node{ - {"a", nil, 0, 1, ""}, - {"b", []*Node{}, 0, 1, ""}, - {"c", nil, 0, 2, ""}, - {"d", nil, 0, 0, "c"}, - { - "e", - []*Node{ - {"x", nil, 0, 1, ""}, - {"y", []*Node{}, 0, 1, ""}, - { - "z", - []*Node{ - {"u", nil, 0, 1, ""}, - {"v", nil, 0, 1, ""}, - {"w", nil, 0, 1, ""}, - }, - 0, - 1, - "", - }, - }, - 0, - 1, - "", - }, - }, - 0, - 1, - "", -} - -func walkTree(n *Node, path string, f func(path string, n *Node)) { - f(path, n) - for _, e := range n.entries { - walkTree(e, filepath.Join(path, e.name), f) - } -} - -func makeTree(t *testing.T) { - walkTree(tree, tree.name, func(path string, n *Node) { - if n.entries == nil { - if n.symLinkedTo != "" { - if err := os.Symlink(n.symLinkedTo, path); err != nil { - t.Fatalf("makeTree: %v", err) - } - } else { - fd, err := os.Create(path) - if err != nil { - t.Fatalf("makeTree: %v", err) - return - } - fd.Close() - } - } else { - if err := os.Mkdir(path, 0770); err != nil { - t.Fatalf("makeTree: %v", err) - } - } - }) -} - -func checkMarks(t *testing.T, report bool) { - walkTree(tree, tree.name, func(path string, n *Node) { - if n.marks != n.expectedMarks && report { - t.Errorf("node %s mark = %d; expected %d", path, n.marks, n.expectedMarks) - } - n.marks = 0 - }) -} - -// Assumes that each node name is unique. Good enough for a test. -// If clear is true, any incoming error is cleared before return. The errors -// are always accumulated, though. -func mark(info os.FileInfo, err error, errors *[]error, clear bool) error { - if err != nil { - *errors = append(*errors, err) - if clear { - return nil - } - return err - } - name := info.Name() - walkTree(tree, tree.name, func(path string, n *Node) { - if n.name == name { - n.marks++ - } - }) - return nil -} - -func TestWalk(t *testing.T) { - makeTree(t) - errors := make([]error, 0, 10) - clear := true - markFn := func(path string, info os.FileInfo, err error) error { - return mark(info, err, &errors, clear) - } - // Expect no errors. - err := Walk(tree.name, markFn) - if err != nil { - t.Fatalf("no error expected, found: %s", err) - } - if len(errors) != 0 { - t.Fatalf("unexpected errors: %s", errors) - } - checkMarks(t, true) - - // cleanup - if err := os.RemoveAll(tree.name); err != nil { - t.Errorf("removeTree: %v", err) - } -} diff --git a/internal/test/ensure/ensure.go b/internal/test/ensure/ensure.go deleted file mode 100644 index b4775df..0000000 --- a/internal/test/ensure/ensure.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package ensure - -import ( - "io/ioutil" - "os" - "testing" - - "helm.sh/helm/v3/pkg/helmpath/xdg" -) - -// HelmHome sets up a Helm Home in a temp dir. -func HelmHome(t *testing.T) func() { - t.Helper() - base := TempDir(t) - os.Setenv(xdg.CacheHomeEnvVar, base) - os.Setenv(xdg.ConfigHomeEnvVar, base) - os.Setenv(xdg.DataHomeEnvVar, base) - return func() { - os.RemoveAll(base) - } -} - -// TempDir ensures a scratch test directory for unit testing purposes. -func TempDir(t *testing.T) string { - t.Helper() - d, err := ioutil.TempDir("", "helm") - if err != nil { - t.Fatal(err) - } - return d -} diff --git a/internal/test/test.go b/internal/test/test.go deleted file mode 100644 index 480b466..0000000 --- a/internal/test/test.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package test - -import ( - "bytes" - "flag" - "io/ioutil" - "path/filepath" - - "github.com/pkg/errors" -) - -// UpdateGolden writes out the golden files with the latest values, rather than failing the test. -var updateGolden = flag.Bool("update", false, "update golden files") - -// TestingT describes a testing object compatible with the critical functions from the testing.T type -type TestingT interface { - Fatal(...interface{}) - Fatalf(string, ...interface{}) - HelperT -} - -// HelperT describes a test with a helper function -type HelperT interface { - Helper() -} - -// AssertGoldenBytes asserts that the give actual content matches the contents of the given filename -func AssertGoldenBytes(t TestingT, actual []byte, filename string) { - t.Helper() - - if err := compare(actual, path(filename)); err != nil { - t.Fatalf("%v", err) - } -} - -// AssertGoldenString asserts that the given string matches the contents of the given file. -func AssertGoldenString(t TestingT, actual, filename string) { - t.Helper() - - if err := compare([]byte(actual), path(filename)); err != nil { - t.Fatalf("%v", err) - } -} - -// AssertGoldenFile assers that the content of the actual file matches the contents of the expected file -func AssertGoldenFile(t TestingT, actualFileName string, expectedFilename string) { - t.Helper() - - actual, err := ioutil.ReadFile(actualFileName) - if err != nil { - t.Fatalf("%v", err) - } - AssertGoldenBytes(t, actual, expectedFilename) -} - -func path(filename string) string { - if filepath.IsAbs(filename) { - return filename - } - return filepath.Join("testdata", filename) -} - -func compare(actual []byte, filename string) error { - if err := update(filename, actual); err != nil { - return err - } - - expected, err := ioutil.ReadFile(filename) - if err != nil { - return errors.Wrapf(err, "unable to read testdata %s", filename) - } - if !bytes.Equal(expected, actual) { - return errors.Errorf("does not match golden file %s\n\nWANT:\n'%s'\n\nGOT:\n'%s'\n", filename, expected, actual) - } - return nil -} - -func update(filename string, in []byte) error { - if !*updateGolden { - return nil - } - return ioutil.WriteFile(filename, normalize(in), 0666) -} - -func normalize(in []byte) []byte { - return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1) -} diff --git a/internal/third_party/dep/fs/fs.go b/internal/third_party/dep/fs/fs.go deleted file mode 100644 index 8325921..0000000 --- a/internal/third_party/dep/fs/fs.go +++ /dev/null @@ -1,373 +0,0 @@ -/* -Copyright (c) for portions of fs.go are held by The Go Authors, 2016 and are provided under -the BSD license. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package fs - -import ( - "io" - "io/ioutil" - "os" - "path/filepath" - "runtime" - "syscall" - - "github.com/pkg/errors" -) - -// fs contains a copy of a few functions from dep tool code to avoid a dependency on golang/dep. -// This code is copied from https://github.com/golang/dep/blob/37d6c560cdf407be7b6cd035b23dba89df9275cf/internal/fs/fs.go -// No changes to the code were made other than removing some unused functions - -// RenameWithFallback attempts to rename a file or directory, but falls back to -// copying in the event of a cross-device link error. If the fallback copy -// succeeds, src is still removed, emulating normal rename behavior. -func RenameWithFallback(src, dst string) error { - _, err := os.Stat(src) - if err != nil { - return errors.Wrapf(err, "cannot stat %s", src) - } - - err = os.Rename(src, dst) - if err == nil { - return nil - } - - return renameFallback(err, src, dst) -} - -// renameByCopy attempts to rename a file or directory by copying it to the -// destination and then removing the src thus emulating the rename behavior. -func renameByCopy(src, dst string) error { - var cerr error - if dir, _ := IsDir(src); dir { - cerr = CopyDir(src, dst) - if cerr != nil { - cerr = errors.Wrap(cerr, "copying directory failed") - } - } else { - cerr = copyFile(src, dst) - if cerr != nil { - cerr = errors.Wrap(cerr, "copying file failed") - } - } - - if cerr != nil { - return errors.Wrapf(cerr, "rename fallback failed: cannot rename %s to %s", src, dst) - } - - return errors.Wrapf(os.RemoveAll(src), "cannot delete %s", src) -} - -var ( - errSrcNotDir = errors.New("source is not a directory") - errDstExist = errors.New("destination already exists") -) - -// CopyDir recursively copies a directory tree, attempting to preserve permissions. -// Source directory must exist, destination directory must *not* exist. -func CopyDir(src, dst string) error { - src = filepath.Clean(src) - dst = filepath.Clean(dst) - - // We use os.Lstat() here to ensure we don't fall in a loop where a symlink - // actually links to a one of its parent directories. - fi, err := os.Lstat(src) - if err != nil { - return err - } - if !fi.IsDir() { - return errSrcNotDir - } - - _, err = os.Stat(dst) - if err != nil && !os.IsNotExist(err) { - return err - } - if err == nil { - return errDstExist - } - - if err = os.MkdirAll(dst, fi.Mode()); err != nil { - return errors.Wrapf(err, "cannot mkdir %s", dst) - } - - entries, err := ioutil.ReadDir(src) - if err != nil { - return errors.Wrapf(err, "cannot read directory %s", dst) - } - - for _, entry := range entries { - srcPath := filepath.Join(src, entry.Name()) - dstPath := filepath.Join(dst, entry.Name()) - - if entry.IsDir() { - if err = CopyDir(srcPath, dstPath); err != nil { - return errors.Wrap(err, "copying directory failed") - } - } else { - // This will include symlinks, which is what we want when - // copying things. - if err = copyFile(srcPath, dstPath); err != nil { - return errors.Wrap(err, "copying file failed") - } - } - } - - return nil -} - -// copyFile copies the contents of the file named src to the file named -// by dst. The file will be created if it does not already exist. If the -// destination file exists, all its contents will be replaced by the contents -// of the source file. The file mode will be copied from the source. -func copyFile(src, dst string) (err error) { - if sym, err := IsSymlink(src); err != nil { - return errors.Wrap(err, "symlink check failed") - } else if sym { - if err := cloneSymlink(src, dst); err != nil { - if runtime.GOOS == "windows" { - // If cloning the symlink fails on Windows because the user - // does not have the required privileges, ignore the error and - // fall back to copying the file contents. - // - // ERROR_PRIVILEGE_NOT_HELD is 1314 (0x522): - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx - if lerr, ok := err.(*os.LinkError); ok && lerr.Err != syscall.Errno(1314) { - return err - } - } else { - return err - } - } else { - return nil - } - } - - in, err := os.Open(src) - if err != nil { - return - } - defer in.Close() - - out, err := os.Create(dst) - if err != nil { - return - } - - if _, err = io.Copy(out, in); err != nil { - out.Close() - return - } - - // Check for write errors on Close - if err = out.Close(); err != nil { - return - } - - si, err := os.Stat(src) - if err != nil { - return - } - - // Temporary fix for Go < 1.9 - // - // See: https://github.com/golang/dep/issues/774 - // and https://github.com/golang/go/issues/20829 - if runtime.GOOS == "windows" { - dst = fixLongPath(dst) - } - err = os.Chmod(dst, si.Mode()) - - return -} - -// cloneSymlink will create a new symlink that points to the resolved path of sl. -// If sl is a relative symlink, dst will also be a relative symlink. -func cloneSymlink(sl, dst string) error { - resolved, err := os.Readlink(sl) - if err != nil { - return err - } - - return os.Symlink(resolved, dst) -} - -// IsDir determines is the path given is a directory or not. -func IsDir(name string) (bool, error) { - fi, err := os.Stat(name) - if err != nil { - return false, err - } - if !fi.IsDir() { - return false, errors.Errorf("%q is not a directory", name) - } - return true, nil -} - -// IsSymlink determines if the given path is a symbolic link. -func IsSymlink(path string) (bool, error) { - l, err := os.Lstat(path) - if err != nil { - return false, err - } - - return l.Mode()&os.ModeSymlink == os.ModeSymlink, nil -} - -// fixLongPath returns the extended-length (\\?\-prefixed) form of -// path when needed, in order to avoid the default 260 character file -// path limit imposed by Windows. If path is not easily converted to -// the extended-length form (for example, if path is a relative path -// or contains .. elements), or is short enough, fixLongPath returns -// path unmodified. -// -// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath -func fixLongPath(path string) string { - // Do nothing (and don't allocate) if the path is "short". - // Empirically (at least on the Windows Server 2013 builder), - // the kernel is arbitrarily okay with < 248 bytes. That - // matches what the docs above say: - // "When using an API to create a directory, the specified - // path cannot be so long that you cannot append an 8.3 file - // name (that is, the directory name cannot exceed MAX_PATH - // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248. - // - // The MSDN docs appear to say that a normal path that is 248 bytes long - // will work; empirically the path must be less then 248 bytes long. - if len(path) < 248 { - // Don't fix. (This is how Go 1.7 and earlier worked, - // not automatically generating the \\?\ form) - return path - } - - // The extended form begins with \\?\, as in - // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt. - // The extended form disables evaluation of . and .. path - // elements and disables the interpretation of / as equivalent - // to \. The conversion here rewrites / to \ and elides - // . elements as well as trailing or duplicate separators. For - // simplicity it avoids the conversion entirely for relative - // paths or paths containing .. elements. For now, - // \\server\share paths are not converted to - // \\?\UNC\server\share paths because the rules for doing so - // are less well-specified. - if len(path) >= 2 && path[:2] == `\\` { - // Don't canonicalize UNC paths. - return path - } - if !isAbs(path) { - // Relative path - return path - } - - const prefix = `\\?` - - pathbuf := make([]byte, len(prefix)+len(path)+len(`\`)) - copy(pathbuf, prefix) - n := len(path) - r, w := 0, len(prefix) - for r < n { - switch { - case os.IsPathSeparator(path[r]): - // empty block - r++ - case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): - // /./ - r++ - case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): - // /../ is currently unhandled - return path - default: - pathbuf[w] = '\\' - w++ - for ; r < n && !os.IsPathSeparator(path[r]); r++ { - pathbuf[w] = path[r] - w++ - } - } - } - // A drive's root directory needs a trailing \ - if w == len(`\\?\c:`) { - pathbuf[w] = '\\' - w++ - } - return string(pathbuf[:w]) -} - -func isAbs(path string) (b bool) { - v := volumeName(path) - if v == "" { - return false - } - path = path[len(v):] - if path == "" { - return false - } - return os.IsPathSeparator(path[0]) -} - -func volumeName(path string) (v string) { - if len(path) < 2 { - return "" - } - // with drive letter - c := path[0] - if path[1] == ':' && - ('0' <= c && c <= '9' || 'a' <= c && c <= 'z' || - 'A' <= c && c <= 'Z') { - return path[:2] - } - // is it UNC - if l := len(path); l >= 5 && os.IsPathSeparator(path[0]) && os.IsPathSeparator(path[1]) && - !os.IsPathSeparator(path[2]) && path[2] != '.' { - // first, leading `\\` and next shouldn't be `\`. its server name. - for n := 3; n < l-1; n++ { - // second, next '\' shouldn't be repeated. - if os.IsPathSeparator(path[n]) { - n++ - // third, following something characters. its share name. - if !os.IsPathSeparator(path[n]) { - if path[n] == '.' { - break - } - for ; n < l; n++ { - if os.IsPathSeparator(path[n]) { - break - } - } - return path[:n] - } - break - } - } - } - return "" -} diff --git a/internal/third_party/dep/fs/fs_test.go b/internal/third_party/dep/fs/fs_test.go deleted file mode 100644 index a9678d8..0000000 --- a/internal/third_party/dep/fs/fs_test.go +++ /dev/null @@ -1,719 +0,0 @@ -/* -Copyright (c) for portions of fs_test.go are held by The Go Authors, 2016 and are provided under -the BSD license. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package fs - -import ( - "io/ioutil" - "os" - "os/exec" - "os/user" - "path/filepath" - "runtime" - "sync" - "testing" -) - -var ( - mu sync.Mutex -) - -func TestRenameWithFallback(t *testing.T) { - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - if err = RenameWithFallback(filepath.Join(dir, "does_not_exists"), filepath.Join(dir, "dst")); err == nil { - t.Fatal("expected an error for non existing file, but got nil") - } - - srcpath := filepath.Join(dir, "src") - - if srcf, err := os.Create(srcpath); err != nil { - t.Fatal(err) - } else { - srcf.Close() - } - - if err = RenameWithFallback(srcpath, filepath.Join(dir, "dst")); err != nil { - t.Fatal(err) - } - - srcpath = filepath.Join(dir, "a") - if err = os.MkdirAll(srcpath, 0777); err != nil { - t.Fatal(err) - } - - dstpath := filepath.Join(dir, "b") - if err = os.MkdirAll(dstpath, 0777); err != nil { - t.Fatal(err) - } - - if err = RenameWithFallback(srcpath, dstpath); err == nil { - t.Fatal("expected an error if dst is an existing directory, but got nil") - } -} - -func TestCopyDir(t *testing.T) { - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - srcdir := filepath.Join(dir, "src") - if err := os.MkdirAll(srcdir, 0755); err != nil { - t.Fatal(err) - } - - files := []struct { - path string - contents string - fi os.FileInfo - }{ - {path: "myfile", contents: "hello world"}, - {path: filepath.Join("subdir", "file"), contents: "subdir file"}, - } - - // Create structure indicated in 'files' - for i, file := range files { - fn := filepath.Join(srcdir, file.path) - dn := filepath.Dir(fn) - if err = os.MkdirAll(dn, 0755); err != nil { - t.Fatal(err) - } - - fh, err := os.Create(fn) - if err != nil { - t.Fatal(err) - } - - if _, err = fh.Write([]byte(file.contents)); err != nil { - t.Fatal(err) - } - fh.Close() - - files[i].fi, err = os.Stat(fn) - if err != nil { - t.Fatal(err) - } - } - - destdir := filepath.Join(dir, "dest") - if err := CopyDir(srcdir, destdir); err != nil { - t.Fatal(err) - } - - // Compare copy against structure indicated in 'files' - for _, file := range files { - fn := filepath.Join(srcdir, file.path) - dn := filepath.Dir(fn) - dirOK, err := IsDir(dn) - if err != nil { - t.Fatal(err) - } - if !dirOK { - t.Fatalf("expected %s to be a directory", dn) - } - - got, err := ioutil.ReadFile(fn) - if err != nil { - t.Fatal(err) - } - - if file.contents != string(got) { - t.Fatalf("expected: %s, got: %s", file.contents, string(got)) - } - - gotinfo, err := os.Stat(fn) - if err != nil { - t.Fatal(err) - } - - if file.fi.Mode() != gotinfo.Mode() { - t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", - file.path, file.fi.Mode(), fn, gotinfo.Mode()) - } - } -} - -func TestCopyDirFail_SrcInaccessible(t *testing.T) { - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in - // Microsoft Windows. Skipping this until a - // compatible implementation is provided. - t.Skip("skipping on windows") - } - - var currentUser, err = user.Current() - - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - var srcdir, dstdir string - - cleanup := setupInaccessibleDir(t, func(dir string) error { - srcdir = filepath.Join(dir, "src") - return os.MkdirAll(srcdir, 0755) - }) - defer cleanup() - - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - dstdir = filepath.Join(dir, "dst") - if err = CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } -} - -func TestCopyDirFail_DstInaccessible(t *testing.T) { - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in - // Microsoft Windows. Skipping this until a - // compatible implementation is provided. - t.Skip("skipping on windows") - } - - var currentUser, err = user.Current() - - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - var srcdir, dstdir string - - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - srcdir = filepath.Join(dir, "src") - if err = os.MkdirAll(srcdir, 0755); err != nil { - t.Fatal(err) - } - - cleanup := setupInaccessibleDir(t, func(dir string) error { - dstdir = filepath.Join(dir, "dst") - return nil - }) - defer cleanup() - - if err := CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } -} - -func TestCopyDirFail_SrcIsNotDir(t *testing.T) { - var srcdir, dstdir string - - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - srcdir = filepath.Join(dir, "src") - if _, err = os.Create(srcdir); err != nil { - t.Fatal(err) - } - - dstdir = filepath.Join(dir, "dst") - - if err = CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } - - if err != errSrcNotDir { - t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errSrcNotDir, srcdir, dstdir, err) - } - -} - -func TestCopyDirFail_DstExists(t *testing.T) { - var srcdir, dstdir string - - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - srcdir = filepath.Join(dir, "src") - if err = os.MkdirAll(srcdir, 0755); err != nil { - t.Fatal(err) - } - - dstdir = filepath.Join(dir, "dst") - if err = os.MkdirAll(dstdir, 0755); err != nil { - t.Fatal(err) - } - - if err = CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } - - if err != errDstExist { - t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errDstExist, srcdir, dstdir, err) - } -} - -func TestCopyDirFailOpen(t *testing.T) { - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in - // Microsoft Windows. os.Chmod(..., 0222) below is not - // enough for the file to be readonly, and os.Chmod(..., - // 0000) returns an invalid argument error. Skipping - // this until a compatible implementation is - // provided. - t.Skip("skipping on windows") - } - - var currentUser, err = user.Current() - - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - var srcdir, dstdir string - - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - srcdir = filepath.Join(dir, "src") - if err = os.MkdirAll(srcdir, 0755); err != nil { - t.Fatal(err) - } - - srcfn := filepath.Join(srcdir, "file") - srcf, err := os.Create(srcfn) - if err != nil { - t.Fatal(err) - } - srcf.Close() - - // setup source file so that it cannot be read - if err = os.Chmod(srcfn, 0222); err != nil { - t.Fatal(err) - } - - dstdir = filepath.Join(dir, "dst") - - if err = CopyDir(srcdir, dstdir); err == nil { - t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) - } -} - -func TestCopyFile(t *testing.T) { - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - srcf, err := os.Create(filepath.Join(dir, "srcfile")) - if err != nil { - t.Fatal(err) - } - - want := "hello world" - if _, err := srcf.Write([]byte(want)); err != nil { - t.Fatal(err) - } - srcf.Close() - - destf := filepath.Join(dir, "destf") - if err := copyFile(srcf.Name(), destf); err != nil { - t.Fatal(err) - } - - got, err := ioutil.ReadFile(destf) - if err != nil { - t.Fatal(err) - } - - if want != string(got) { - t.Fatalf("expected: %s, got: %s", want, string(got)) - } - - wantinfo, err := os.Stat(srcf.Name()) - if err != nil { - t.Fatal(err) - } - - gotinfo, err := os.Stat(destf) - if err != nil { - t.Fatal(err) - } - - if wantinfo.Mode() != gotinfo.Mode() { - t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", srcf.Name(), wantinfo.Mode(), destf, gotinfo.Mode()) - } -} - -func cleanUpDir(dir string) { - // NOTE(mattn): It seems that sometimes git.exe is not dead - // when cleanUpDir() is called. But we do not know any way to wait for it. - if runtime.GOOS == "windows" { - mu.Lock() - exec.Command(`taskkill`, `/F`, `/IM`, `git.exe`).Run() - mu.Unlock() - } - if dir != "" { - os.RemoveAll(dir) - } -} - -func TestCopyFileSymlink(t *testing.T) { - var tempdir, err = ioutil.TempDir("", "gotest") - - if err != nil { - t.Fatalf("failed to create directory: %s", err) - } - - defer cleanUpDir(tempdir) - - testcases := map[string]string{ - filepath.Join("./testdata/symlinks/file-symlink"): filepath.Join(tempdir, "dst-file"), - filepath.Join("./testdata/symlinks/windows-file-symlink"): filepath.Join(tempdir, "windows-dst-file"), - filepath.Join("./testdata/symlinks/invalid-symlink"): filepath.Join(tempdir, "invalid-symlink"), - } - - for symlink, dst := range testcases { - t.Run(symlink, func(t *testing.T) { - var err error - if err = copyFile(symlink, dst); err != nil { - t.Fatalf("failed to copy symlink: %s", err) - } - - var want, got string - - if runtime.GOOS == "windows" { - // Creating symlinks on Windows require an additional permission - // regular users aren't granted usually. So we copy the file - // content as a fall back instead of creating a real symlink. - srcb, err := ioutil.ReadFile(symlink) - if err != nil { - t.Fatalf("%+v", err) - } - dstb, err := ioutil.ReadFile(dst) - if err != nil { - t.Fatalf("%+v", err) - } - - want = string(srcb) - got = string(dstb) - } else { - want, err = os.Readlink(symlink) - if err != nil { - t.Fatalf("%+v", err) - } - - got, err = os.Readlink(dst) - if err != nil { - t.Fatalf("could not resolve symlink: %s", err) - } - } - - if want != got { - t.Fatalf("resolved path is incorrect. expected %s, got %s", want, got) - } - }) - } -} - -func TestCopyFileFail(t *testing.T) { - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in - // Microsoft Windows. Skipping this until a - // compatible implementation is provided. - t.Skip("skipping on windows") - } - - var currentUser, err = user.Current() - - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - srcf, err := os.Create(filepath.Join(dir, "srcfile")) - if err != nil { - t.Fatal(err) - } - srcf.Close() - - var dstdir string - - cleanup := setupInaccessibleDir(t, func(dir string) error { - dstdir = filepath.Join(dir, "dir") - return os.Mkdir(dstdir, 0777) - }) - defer cleanup() - - fn := filepath.Join(dstdir, "file") - if err := copyFile(srcf.Name(), fn); err == nil { - t.Fatalf("expected error for %s, got none", fn) - } -} - -// setupInaccessibleDir creates a temporary location with a single -// directory in it, in such a way that directory is not accessible -// after this function returns. -// -// op is called with the directory as argument, so that it can create -// files or other test artifacts. -// -// If setupInaccessibleDir fails in its preparation, or op fails, t.Fatal -// will be invoked. -// -// This function returns a cleanup function that removes all the temporary -// files this function creates. It is the caller's responsibility to call -// this function before the test is done running, whether there's an error or not. -func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() { - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - return nil // keep compiler happy - } - - subdir := filepath.Join(dir, "dir") - - cleanup := func() { - if err := os.Chmod(subdir, 0777); err != nil { - t.Error(err) - } - if err := os.RemoveAll(dir); err != nil { - t.Error(err) - } - } - - if err := os.Mkdir(subdir, 0777); err != nil { - cleanup() - t.Fatal(err) - return nil - } - - if err := op(subdir); err != nil { - cleanup() - t.Fatal(err) - return nil - } - - if err := os.Chmod(subdir, 0666); err != nil { - cleanup() - t.Fatal(err) - return nil - } - - return cleanup -} - -func TestIsDir(t *testing.T) { - - var currentUser, err = user.Current() - - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - wd, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - var dn string - - cleanup := setupInaccessibleDir(t, func(dir string) error { - dn = filepath.Join(dir, "dir") - return os.Mkdir(dn, 0777) - }) - defer cleanup() - - tests := map[string]struct { - exists bool - err bool - }{ - wd: {true, false}, - filepath.Join(wd, "testdata"): {true, false}, - filepath.Join(wd, "main.go"): {false, true}, - filepath.Join(wd, "this_file_does_not_exist.thing"): {false, true}, - dn: {false, true}, - } - - if runtime.GOOS == "windows" { - // This test doesn't work on Microsoft Windows because - // of the differences in how file permissions are - // implemented. For this to work, the directory where - // the directory exists should be inaccessible. - delete(tests, dn) - } - - for f, want := range tests { - got, err := IsDir(f) - if err != nil && !want.err { - t.Fatalf("expected no error, got %v", err) - } - - if got != want.exists { - t.Fatalf("expected %t for %s, got %t", want.exists, f, got) - } - } -} - -func TestIsSymlink(t *testing.T) { - - var currentUser, err = user.Current() - - if err != nil { - t.Fatalf("Failed to get name of current user: %s", err) - } - - if currentUser.Name == "root" { - // Skipping if root, because all files are accessible - t.Skip("Skipping for root user") - } - - dir, err := ioutil.TempDir("", "helm-tmp") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dir) - - dirPath := filepath.Join(dir, "directory") - if err = os.MkdirAll(dirPath, 0777); err != nil { - t.Fatal(err) - } - - filePath := filepath.Join(dir, "file") - f, err := os.Create(filePath) - if err != nil { - t.Fatal(err) - } - f.Close() - - dirSymlink := filepath.Join(dir, "dirSymlink") - fileSymlink := filepath.Join(dir, "fileSymlink") - - if err = os.Symlink(dirPath, dirSymlink); err != nil { - t.Fatal(err) - } - if err = os.Symlink(filePath, fileSymlink); err != nil { - t.Fatal(err) - } - - var ( - inaccessibleFile string - inaccessibleSymlink string - ) - - cleanup := setupInaccessibleDir(t, func(dir string) error { - inaccessibleFile = filepath.Join(dir, "file") - if fh, err := os.Create(inaccessibleFile); err != nil { - return err - } else if err = fh.Close(); err != nil { - return err - } - - inaccessibleSymlink = filepath.Join(dir, "symlink") - return os.Symlink(inaccessibleFile, inaccessibleSymlink) - }) - defer cleanup() - - tests := map[string]struct{ expected, err bool }{ - dirPath: {false, false}, - filePath: {false, false}, - dirSymlink: {true, false}, - fileSymlink: {true, false}, - inaccessibleFile: {false, true}, - inaccessibleSymlink: {false, true}, - } - - if runtime.GOOS == "windows" { - // XXX: setting permissions works differently in Windows. Skipping - // these cases until a compatible implementation is provided. - delete(tests, inaccessibleFile) - delete(tests, inaccessibleSymlink) - } - - for path, want := range tests { - got, err := IsSymlink(path) - if err != nil { - if !want.err { - t.Errorf("expected no error, got %v", err) - } - } - - if got != want.expected { - t.Errorf("expected %t for %s, got %t", want.expected, path, got) - } - } -} diff --git a/internal/third_party/dep/fs/rename.go b/internal/third_party/dep/fs/rename.go deleted file mode 100644 index 0bb6009..0000000 --- a/internal/third_party/dep/fs/rename.go +++ /dev/null @@ -1,58 +0,0 @@ -// +build !windows - -/* -Copyright (c) for portions of rename.go are held by The Go Authors, 2016 and are provided under -the BSD license. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package fs - -import ( - "os" - "syscall" - - "github.com/pkg/errors" -) - -// renameFallback attempts to determine the appropriate fallback to failed rename -// operation depending on the resulting error. -func renameFallback(err error, src, dst string) error { - // Rename may fail if src and dst are on different devices; fall back to - // copy if we detect that case. syscall.EXDEV is the common name for the - // cross device link error which has varying output text across different - // operating systems. - terr, ok := err.(*os.LinkError) - if !ok { - return err - } else if terr.Err != syscall.EXDEV { - return errors.Wrapf(terr, "link error: cannot rename %s to %s", src, dst) - } - - return renameByCopy(src, dst) -} diff --git a/internal/third_party/dep/fs/rename_windows.go b/internal/third_party/dep/fs/rename_windows.go deleted file mode 100644 index 14f017d..0000000 --- a/internal/third_party/dep/fs/rename_windows.go +++ /dev/null @@ -1,69 +0,0 @@ -// +build windows - -/* -Copyright (c) for portions of rename_windows.go are held by The Go Authors, 2016 and are provided under -the BSD license. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -package fs - -import ( - "os" - "syscall" - - "github.com/pkg/errors" -) - -// renameFallback attempts to determine the appropriate fallback to failed rename -// operation depending on the resulting error. -func renameFallback(err error, src, dst string) error { - // Rename may fail if src and dst are on different devices; fall back to - // copy if we detect that case. syscall.EXDEV is the common name for the - // cross device link error which has varying output text across different - // operating systems. - terr, ok := err.(*os.LinkError) - if !ok { - return err - } - - if terr.Err != syscall.EXDEV { - // In windows it can drop down to an operating system call that - // returns an operating system error with a different number and - // message. Checking for that as a fall back. - noerr, ok := terr.Err.(syscall.Errno) - - // 0x11 (ERROR_NOT_SAME_DEVICE) is the windows error. - // See https://msdn.microsoft.com/en-us/library/cc231199.aspx - if ok && noerr != 0x11 { - return errors.Wrapf(terr, "link error: cannot rename %s to %s", src, dst) - } - } - - return renameByCopy(src, dst) -} diff --git a/internal/third_party/dep/fs/testdata/symlinks/file-symlink b/internal/third_party/dep/fs/testdata/symlinks/file-symlink deleted file mode 120000 index 4c52274..0000000 --- a/internal/third_party/dep/fs/testdata/symlinks/file-symlink +++ /dev/null @@ -1 +0,0 @@ -../test.file \ No newline at end of file diff --git a/internal/third_party/dep/fs/testdata/symlinks/invalid-symlink b/internal/third_party/dep/fs/testdata/symlinks/invalid-symlink deleted file mode 120000 index 0edf4f3..0000000 --- a/internal/third_party/dep/fs/testdata/symlinks/invalid-symlink +++ /dev/null @@ -1 +0,0 @@ -/non/existing/file \ No newline at end of file diff --git a/internal/third_party/dep/fs/testdata/symlinks/windows-file-symlink b/internal/third_party/dep/fs/testdata/symlinks/windows-file-symlink deleted file mode 120000 index af1d6c8..0000000 --- a/internal/third_party/dep/fs/testdata/symlinks/windows-file-symlink +++ /dev/null @@ -1 +0,0 @@ -C:/Users/ibrahim/go/src/github.com/golang/dep/internal/fs/testdata/test.file \ No newline at end of file diff --git a/internal/third_party/dep/fs/testdata/test.file b/internal/third_party/dep/fs/testdata/test.file deleted file mode 100644 index e69de29..0000000 diff --git a/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go b/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go deleted file mode 100644 index da93a69..0000000 --- a/internal/third_party/k8s.io/kubernetes/deployment/util/deploymentutil.go +++ /dev/null @@ -1,177 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package util - -import ( - "sort" - - apps "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" - apiequality "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - intstrutil "k8s.io/apimachinery/pkg/util/intstr" - appsclient "k8s.io/client-go/kubernetes/typed/apps/v1" -) - -// deploymentutil contains a copy of a few functions from Kubernetes controller code to avoid a dependency on k8s.io/kubernetes. -// This code is copied from https://github.com/kubernetes/kubernetes/blob/e856613dd5bb00bcfaca6974431151b5c06cbed5/pkg/controller/deployment/util/deployment_util.go -// No changes to the code were made other than removing some unused functions - -// RsListFunc returns the ReplicaSet from the ReplicaSet namespace and the List metav1.ListOptions. -type RsListFunc func(string, metav1.ListOptions) ([]*apps.ReplicaSet, error) - -// ListReplicaSets returns a slice of RSes the given deployment targets. -// Note that this does NOT attempt to reconcile ControllerRef (adopt/orphan), -// because only the controller itself should do that. -// However, it does filter out anything whose ControllerRef doesn't match. -func ListReplicaSets(deployment *apps.Deployment, getRSList RsListFunc) ([]*apps.ReplicaSet, error) { - // TODO: Right now we list replica sets by their labels. We should list them by selector, i.e. the replica set's selector - // should be a superset of the deployment's selector, see https://github.com/kubernetes/kubernetes/issues/19830. - namespace := deployment.Namespace - selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector) - if err != nil { - return nil, err - } - options := metav1.ListOptions{LabelSelector: selector.String()} - all, err := getRSList(namespace, options) - if err != nil { - return nil, err - } - // Only include those whose ControllerRef matches the Deployment. - owned := make([]*apps.ReplicaSet, 0, len(all)) - for _, rs := range all { - if metav1.IsControlledBy(rs, deployment) { - owned = append(owned, rs) - } - } - return owned, nil -} - -// ReplicaSetsByCreationTimestamp sorts a list of ReplicaSet by creation timestamp, using their names as a tie breaker. -type ReplicaSetsByCreationTimestamp []*apps.ReplicaSet - -func (o ReplicaSetsByCreationTimestamp) Len() int { return len(o) } -func (o ReplicaSetsByCreationTimestamp) Swap(i, j int) { o[i], o[j] = o[j], o[i] } -func (o ReplicaSetsByCreationTimestamp) Less(i, j int) bool { - if o[i].CreationTimestamp.Equal(&o[j].CreationTimestamp) { - return o[i].Name < o[j].Name - } - return o[i].CreationTimestamp.Before(&o[j].CreationTimestamp) -} - -// FindNewReplicaSet returns the new RS this given deployment targets (the one with the same pod template). -func FindNewReplicaSet(deployment *apps.Deployment, rsList []*apps.ReplicaSet) *apps.ReplicaSet { - sort.Sort(ReplicaSetsByCreationTimestamp(rsList)) - for i := range rsList { - if EqualIgnoreHash(&rsList[i].Spec.Template, &deployment.Spec.Template) { - // In rare cases, such as after cluster upgrades, Deployment may end up with - // having more than one new ReplicaSets that have the same template as its template, - // see https://github.com/kubernetes/kubernetes/issues/40415 - // We deterministically choose the oldest new ReplicaSet. - return rsList[i] - } - } - // new ReplicaSet does not exist. - return nil -} - -// EqualIgnoreHash returns true if two given podTemplateSpec are equal, ignoring the diff in value of Labels[pod-template-hash] -// We ignore pod-template-hash because: -// 1. The hash result would be different upon podTemplateSpec API changes -// (e.g. the addition of a new field will cause the hash code to change) -// 2. The deployment template won't have hash labels -func EqualIgnoreHash(template1, template2 *v1.PodTemplateSpec) bool { - t1Copy := template1.DeepCopy() - t2Copy := template2.DeepCopy() - // Remove hash labels from template.Labels before comparing - delete(t1Copy.Labels, apps.DefaultDeploymentUniqueLabelKey) - delete(t2Copy.Labels, apps.DefaultDeploymentUniqueLabelKey) - return apiequality.Semantic.DeepEqual(t1Copy, t2Copy) -} - -// GetNewReplicaSet returns a replica set that matches the intent of the given deployment; get ReplicaSetList from client interface. -// Returns nil if the new replica set doesn't exist yet. -func GetNewReplicaSet(deployment *apps.Deployment, c appsclient.AppsV1Interface) (*apps.ReplicaSet, error) { - rsList, err := ListReplicaSets(deployment, RsListFromClient(c)) - if err != nil { - return nil, err - } - return FindNewReplicaSet(deployment, rsList), nil -} - -// RsListFromClient returns an rsListFunc that wraps the given client. -func RsListFromClient(c appsclient.AppsV1Interface) RsListFunc { - return func(namespace string, options metav1.ListOptions) ([]*apps.ReplicaSet, error) { - rsList, err := c.ReplicaSets(namespace).List(options) - if err != nil { - return nil, err - } - var ret []*apps.ReplicaSet - for i := range rsList.Items { - ret = append(ret, &rsList.Items[i]) - } - return ret, err - } -} - -// IsRollingUpdate returns true if the strategy type is a rolling update. -func IsRollingUpdate(deployment *apps.Deployment) bool { - return deployment.Spec.Strategy.Type == apps.RollingUpdateDeploymentStrategyType -} - -// MaxUnavailable returns the maximum unavailable pods a rolling deployment can take. -func MaxUnavailable(deployment apps.Deployment) int32 { - if !IsRollingUpdate(&deployment) || *(deployment.Spec.Replicas) == 0 { - return int32(0) - } - // Error caught by validation - _, maxUnavailable, _ := ResolveFenceposts(deployment.Spec.Strategy.RollingUpdate.MaxSurge, deployment.Spec.Strategy.RollingUpdate.MaxUnavailable, *(deployment.Spec.Replicas)) - if maxUnavailable > *deployment.Spec.Replicas { - return *deployment.Spec.Replicas - } - return maxUnavailable -} - -// ResolveFenceposts resolves both maxSurge and maxUnavailable. This needs to happen in one -// step. For example: -// -// 2 desired, max unavailable 1%, surge 0% - should scale old(-1), then new(+1), then old(-1), then new(+1) -// 1 desired, max unavailable 1%, surge 0% - should scale old(-1), then new(+1) -// 2 desired, max unavailable 25%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1) -// 1 desired, max unavailable 25%, surge 1% - should scale new(+1), then old(-1) -// 2 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1), then new(+1), then old(-1) -// 1 desired, max unavailable 0%, surge 1% - should scale new(+1), then old(-1) -func ResolveFenceposts(maxSurge, maxUnavailable *intstrutil.IntOrString, desired int32) (int32, int32, error) { - surge, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxSurge, intstrutil.FromInt(0)), int(desired), true) - if err != nil { - return 0, 0, err - } - unavailable, err := intstrutil.GetValueFromIntOrPercent(intstrutil.ValueOrDefault(maxUnavailable, intstrutil.FromInt(0)), int(desired), false) - if err != nil { - return 0, 0, err - } - - if surge == 0 && unavailable == 0 { - // Validation should never allow the user to explicitly use zero values for both maxSurge - // maxUnavailable. Due to rounding down maxUnavailable though, it may resolve to zero. - // If both fenceposts resolve to zero, then we should set maxUnavailable to 1 on the - // theory that surge might not work due to quota. - unavailable = 1 - } - - return int32(surge), int32(unavailable), nil -} diff --git a/internal/tlsutil/cfg.go b/internal/tlsutil/cfg.go deleted file mode 100644 index 8b9d432..0000000 --- a/internal/tlsutil/cfg.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package tlsutil - -import ( - "crypto/tls" - "crypto/x509" - "os" - - "github.com/pkg/errors" -) - -// Options represents configurable options used to create client and server TLS configurations. -type Options struct { - CaCertFile string - // If either the KeyFile or CertFile is empty, ClientConfig() will not load them. - KeyFile string - CertFile string - // Client-only options - InsecureSkipVerify bool -} - -// ClientConfig returns a TLS configuration for use by a Helm client. -func ClientConfig(opts Options) (cfg *tls.Config, err error) { - var cert *tls.Certificate - var pool *x509.CertPool - - if opts.CertFile != "" || opts.KeyFile != "" { - if cert, err = CertFromFilePair(opts.CertFile, opts.KeyFile); err != nil { - if os.IsNotExist(err) { - return nil, errors.Wrapf(err, "could not load x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile) - } - return nil, errors.Wrapf(err, "could not read x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile) - } - } - if !opts.InsecureSkipVerify && opts.CaCertFile != "" { - if pool, err = CertPoolFromFile(opts.CaCertFile); err != nil { - return nil, err - } - } - - cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool} - return cfg, nil -} diff --git a/internal/tlsutil/tls.go b/internal/tlsutil/tls.go deleted file mode 100644 index ed7795d..0000000 --- a/internal/tlsutil/tls.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package tlsutil - -import ( - "crypto/tls" - "crypto/x509" - "io/ioutil" - - "github.com/pkg/errors" -) - -// NewClientTLS returns tls.Config appropriate for client auth. -func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) { - config := tls.Config{} - - if certFile != "" && keyFile != "" { - cert, err := CertFromFilePair(certFile, keyFile) - if err != nil { - return nil, err - } - config.Certificates = []tls.Certificate{*cert} - } - - if caFile != "" { - cp, err := CertPoolFromFile(caFile) - if err != nil { - return nil, err - } - config.RootCAs = cp - } - - return &config, nil -} - -// CertPoolFromFile returns an x509.CertPool containing the certificates -// in the given PEM-encoded file. -// Returns an error if the file could not be read, a certificate could not -// be parsed, or if the file does not contain any certificates -func CertPoolFromFile(filename string) (*x509.CertPool, error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, errors.Errorf("can't read CA file: %v", filename) - } - cp := x509.NewCertPool() - if !cp.AppendCertsFromPEM(b) { - return nil, errors.Errorf("failed to append certificates from file: %s", filename) - } - return cp, nil -} - -// CertFromFilePair returns an tls.Certificate containing the -// certificates public/private key pair from a pair of given PEM-encoded files. -// Returns an error if the file could not be read, a certificate could not -// be parsed, or if the file does not contain any certificates -func CertFromFilePair(certFile, keyFile string) (*tls.Certificate, error) { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return nil, errors.Wrapf(err, "can't load key pair from cert %s and key %s", certFile, keyFile) - } - return &cert, err -} diff --git a/internal/tlsutil/tlsutil_test.go b/internal/tlsutil/tlsutil_test.go deleted file mode 100644 index 24551fd..0000000 --- a/internal/tlsutil/tlsutil_test.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package tlsutil - -import ( - "path/filepath" - "testing" -) - -const tlsTestDir = "../../testdata" - -const ( - testCaCertFile = "rootca.crt" - testCertFile = "crt.pem" - testKeyFile = "key.pem" -) - -func TestClientConfig(t *testing.T) { - opts := Options{ - CaCertFile: testfile(t, testCaCertFile), - CertFile: testfile(t, testCertFile), - KeyFile: testfile(t, testKeyFile), - InsecureSkipVerify: false, - } - - cfg, err := ClientConfig(opts) - if err != nil { - t.Fatalf("error building tls client config: %v", err) - } - - if got := len(cfg.Certificates); got != 1 { - t.Fatalf("expecting 1 client certificates, got %d", got) - } - if cfg.InsecureSkipVerify { - t.Fatalf("insecure skip verify mistmatch, expecting false") - } - if cfg.RootCAs == nil { - t.Fatalf("mismatch tls RootCAs, expecting non-nil") - } -} - -func testfile(t *testing.T, file string) (path string) { - var err error - if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil { - t.Fatalf("error getting absolute path to test file %q: %v", file, err) - } - return path -} - -func TestNewClientTLS(t *testing.T) { - certFile := testfile(t, testCertFile) - keyFile := testfile(t, testKeyFile) - caCertFile := testfile(t, testCaCertFile) - - cfg, err := NewClientTLS(certFile, keyFile, caCertFile) - if err != nil { - t.Error(err) - } - - if got := len(cfg.Certificates); got != 1 { - t.Fatalf("expecting 1 client certificates, got %d", got) - } - if cfg.InsecureSkipVerify { - t.Fatalf("insecure skip verify mistmatch, expecting false") - } - if cfg.RootCAs == nil { - t.Fatalf("mismatch tls RootCAs, expecting non-nil") - } - - cfg, err = NewClientTLS("", "", caCertFile) - if err != nil { - t.Error(err) - } - - if got := len(cfg.Certificates); got != 0 { - t.Fatalf("expecting 0 client certificates, got %d", got) - } - if cfg.InsecureSkipVerify { - t.Fatalf("insecure skip verify mistmatch, expecting false") - } - if cfg.RootCAs == nil { - t.Fatalf("mismatch tls RootCAs, expecting non-nil") - } - - cfg, err = NewClientTLS(certFile, keyFile, "") - if err != nil { - t.Error(err) - } - - if got := len(cfg.Certificates); got != 1 { - t.Fatalf("expecting 1 client certificates, got %d", got) - } - if cfg.InsecureSkipVerify { - t.Fatalf("insecure skip verify mistmatch, expecting false") - } - if cfg.RootCAs != nil { - t.Fatalf("mismatch tls RootCAs, expecting nil") - } -} diff --git a/internal/urlutil/urlutil.go b/internal/urlutil/urlutil.go deleted file mode 100644 index a8cf739..0000000 --- a/internal/urlutil/urlutil.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package urlutil - -import ( - "net/url" - "path" - "path/filepath" -) - -// URLJoin joins a base URL to one or more path components. -// -// It's like filepath.Join for URLs. If the baseURL is pathish, this will still -// perform a join. -// -// If the URL is unparsable, this returns an error. -func URLJoin(baseURL string, paths ...string) (string, error) { - u, err := url.Parse(baseURL) - if err != nil { - return "", err - } - // We want path instead of filepath because path always uses /. - all := []string{u.Path} - all = append(all, paths...) - u.Path = path.Join(all...) - return u.String(), nil -} - -// Equal normalizes two URLs and then compares for equality. -func Equal(a, b string) bool { - au, err := url.Parse(a) - if err != nil { - a = filepath.Clean(a) - b = filepath.Clean(b) - // If urls are paths, return true only if they are an exact match - return a == b - } - bu, err := url.Parse(b) - if err != nil { - return false - } - - for _, u := range []*url.URL{au, bu} { - if u.Path == "" { - u.Path = "/" - } - u.Path = filepath.Clean(u.Path) - } - return au.String() == bu.String() -} - -// ExtractHostname returns hostname from URL -func ExtractHostname(addr string) (string, error) { - u, err := url.Parse(addr) - if err != nil { - return "", err - } - return u.Hostname(), nil -} diff --git a/internal/urlutil/urlutil_test.go b/internal/urlutil/urlutil_test.go deleted file mode 100644 index 8e99c1b..0000000 --- a/internal/urlutil/urlutil_test.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package urlutil - -import "testing" - -func TestURLJoin(t *testing.T) { - tests := []struct { - name, url, expect string - paths []string - }{ - {name: "URL, one path", url: "http://example.com", paths: []string{"hello"}, expect: "http://example.com/hello"}, - {name: "Long URL, one path", url: "http://example.com/but/first", paths: []string{"slurm"}, expect: "http://example.com/but/first/slurm"}, - {name: "URL, two paths", url: "http://example.com", paths: []string{"hello", "world"}, expect: "http://example.com/hello/world"}, - {name: "URL, no paths", url: "http://example.com", paths: []string{}, expect: "http://example.com"}, - {name: "basepath, two paths", url: "../example.com", paths: []string{"hello", "world"}, expect: "../example.com/hello/world"}, - } - - for _, tt := range tests { - if got, err := URLJoin(tt.url, tt.paths...); err != nil { - t.Errorf("%s: error %q", tt.name, err) - } else if got != tt.expect { - t.Errorf("%s: expected %q, got %q", tt.name, tt.expect, got) - } - } -} - -func TestEqual(t *testing.T) { - for _, tt := range []struct { - a, b string - match bool - }{ - {"http://example.com", "http://example.com", true}, - {"http://example.com", "http://another.example.com", false}, - {"https://example.com", "https://example.com", true}, - {"http://example.com/", "http://example.com", true}, - {"https://example.com", "http://example.com", false}, - {"http://example.com/foo", "http://example.com/foo/", true}, - {"http://example.com/foo//", "http://example.com/foo/", true}, - {"http://example.com/./foo/", "http://example.com/foo/", true}, - {"http://example.com/bar/../foo/", "http://example.com/foo/", true}, - {"/foo", "/foo", true}, - {"/foo", "/foo/", true}, - {"/foo/.", "/foo/", true}, - } { - if tt.match != Equal(tt.a, tt.b) { - t.Errorf("Expected %q==%q to be %t", tt.a, tt.b, tt.match) - } - } -} - -func TestExtractHostname(t *testing.T) { - tests := map[string]string{ - "http://example.com": "example.com", - "https://example.com/foo": "example.com", - - "https://example.com:31337/not/with/a/bang/but/a/whimper": "example.com", - } - for start, expect := range tests { - if got, _ := ExtractHostname(start); got != expect { - t.Errorf("Got %q, expected %q", got, expect) - } - } -} diff --git a/internal/version/version.go b/internal/version/version.go deleted file mode 100644 index 22439d1..0000000 --- a/internal/version/version.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package version // import "helm.sh/helm/v3/internal/version" - -import ( - "flag" - "runtime" - "strings" -) - -var ( - // version is the current version of the Helm. - // Update this whenever making a new release. - // The version is of the format Major.Minor.Patch[-Prerelease][+BuildMetadata] - // - // Increment major number for new feature additions and behavioral changes. - // Increment minor number for bug fixes and performance enhancements. - // Increment patch number for critical fixes to existing releases. - version = "v3.0" - - // metadata is extra build time data - metadata = "" - // gitCommit is the git sha1 - gitCommit = "" - // gitTreeState is the state of the git tree - gitTreeState = "" -) - -// BuildInfo describes the compile time information. -type BuildInfo struct { - // Version is the current semver. - Version string `json:"version,omitempty"` - // GitCommit is the git sha1. - GitCommit string `json:"git_commit,omitempty"` - // GitTreeState is the state of the git tree. - GitTreeState string `json:"git_tree_state,omitempty"` - // GoVersion is the version of the Go compiler used. - GoVersion string `json:"go_version,omitempty"` -} - -// GetVersion returns the semver string of the version -func GetVersion() string { - if metadata == "" { - return version - } - return version + "+" + metadata -} - -// GetUserAgent returns a user agent for user with an HTTP client -func GetUserAgent() string { - return "Helm/" + strings.TrimPrefix(GetVersion(), "v") -} - -// Get returns build info -func Get() BuildInfo { - v := BuildInfo{ - Version: GetVersion(), - GitCommit: gitCommit, - GitTreeState: gitTreeState, - GoVersion: runtime.Version(), - } - - // HACK(bacongobbler): strip out GoVersion during a test run for consistent test output - if flag.Lookup("test.v") != nil { - v.GoVersion = "" - } - return v -} diff --git a/pkg/action/action.go b/pkg/action/action.go deleted file mode 100644 index 9405cc4..0000000 --- a/pkg/action/action.go +++ /dev/null @@ -1,256 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "path" - "regexp" - - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/client-go/discovery" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - - "helm.sh/helm/v3/internal/experimental/registry" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/kube" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/storage" - "helm.sh/helm/v3/pkg/storage/driver" - "helm.sh/helm/v3/pkg/time" -) - -// Timestamper is a function capable of producing a timestamp.Timestamper. -// -// By default, this is a time.Time function from the Helm time package. This can -// be overridden for testing though, so that timestamps are predictable. -var Timestamper = time.Now - -var ( - // errMissingChart indicates that a chart was not provided. - errMissingChart = errors.New("no chart provided") - // errMissingRelease indicates that a release (name) was not provided. - errMissingRelease = errors.New("no release provided") - // errInvalidRevision indicates that an invalid release revision number was provided. - errInvalidRevision = errors.New("invalid release revision") - // errInvalidName indicates that an invalid release name was provided - errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53") -) - -// ValidName is a regular expression for names. -// -// According to the Kubernetes help text, the regular expression it uses is: -// -// (([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])? -// -// We modified that. First, we added start and end delimiters. Second, we changed -// the final ? to + to require that the pattern match at least once. This modification -// prevents an empty string from matching. -var ValidName = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$") - -// Configuration injects the dependencies that all actions share. -type Configuration struct { - // RESTClientGetter is an interface that loads Kubernetes clients. - RESTClientGetter RESTClientGetter - - // Releases stores records of releases. - Releases *storage.Storage - - // KubeClient is a Kubernetes API client. - KubeClient kube.Interface - - // RegistryClient is a client for working with registries - RegistryClient *registry.Client - - // Capabilities describes the capabilities of the Kubernetes cluster. - Capabilities *chartutil.Capabilities - - Log func(string, ...interface{}) -} - -// RESTClientGetter gets the rest client -type RESTClientGetter interface { - ToRESTConfig() (*rest.Config, error) - ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) - ToRESTMapper() (meta.RESTMapper, error) -} - -// DebugLog sets the logger that writes debug strings -type DebugLog func(format string, v ...interface{}) - -// capabilities builds a Capabilities from discovery information. -func (c *Configuration) getCapabilities() (*chartutil.Capabilities, error) { - if c.Capabilities != nil { - return c.Capabilities, nil - } - dc, err := c.RESTClientGetter.ToDiscoveryClient() - if err != nil { - return nil, errors.Wrap(err, "could not get Kubernetes discovery client") - } - // force a discovery cache invalidation to always fetch the latest server version/capabilities. - dc.Invalidate() - kubeVersion, err := dc.ServerVersion() - if err != nil { - return nil, errors.Wrap(err, "could not get server version from Kubernetes") - } - // Issue #6361: - // Client-Go emits an error when an API service is registered but unimplemented. - // We trap that error here and print a warning. But since the discovery client continues - // building the API object, it is correctly populated with all valid APIs. - // See https://github.com/kubernetes/kubernetes/issues/72051#issuecomment-521157642 - apiVersions, err := GetVersionSet(dc) - if err != nil { - if discovery.IsGroupDiscoveryFailedError(err) { - c.Log("WARNING: The Kubernetes server has an orphaned API service. Server reports: %s", err) - c.Log("WARNING: To fix this, kubectl delete apiservice ") - } else { - return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes") - } - } - - c.Capabilities = &chartutil.Capabilities{ - APIVersions: apiVersions, - KubeVersion: chartutil.KubeVersion{ - Version: kubeVersion.GitVersion, - Major: kubeVersion.Major, - Minor: kubeVersion.Minor, - }, - } - return c.Capabilities, nil -} - -func (c *Configuration) KubernetesClientSet() (kubernetes.Interface, error) { - conf, err := c.RESTClientGetter.ToRESTConfig() - if err != nil { - return nil, errors.Wrap(err, "unable to generate config for kubernetes client") - } - - return kubernetes.NewForConfig(conf) -} - -// Now generates a timestamp -// -// If the configuration has a Timestamper on it, that will be used. -// Otherwise, this will use time.Now(). -func (c *Configuration) Now() time.Time { - return Timestamper() -} - -func (c *Configuration) releaseContent(name string, version int) (*release.Release, error) { - if err := validateReleaseName(name); err != nil { - return nil, errors.Errorf("releaseContent: Release name is invalid: %s", name) - } - - if version <= 0 { - return c.Releases.Last(name) - } - - return c.Releases.Get(name, version) -} - -// GetVersionSet retrieves a set of available k8s API versions -func GetVersionSet(client discovery.ServerResourcesInterface) (chartutil.VersionSet, error) { - groups, resources, err := client.ServerGroupsAndResources() - if err != nil && !discovery.IsGroupDiscoveryFailedError(err) { - return chartutil.DefaultVersionSet, errors.Wrap(err, "could not get apiVersions from Kubernetes") - } - - // FIXME: The Kubernetes test fixture for cli appears to always return nil - // for calls to Discovery().ServerGroupsAndResources(). So in this case, we - // return the default API list. This is also a safe value to return in any - // other odd-ball case. - if len(groups) == 0 && len(resources) == 0 { - return chartutil.DefaultVersionSet, nil - } - - versionMap := make(map[string]interface{}) - versions := []string{} - - // Extract the groups - for _, g := range groups { - for _, gv := range g.Versions { - versionMap[gv.GroupVersion] = struct{}{} - } - } - - // Extract the resources - var id string - var ok bool - for _, r := range resources { - for _, rl := range r.APIResources { - - // A Kind at a GroupVersion can show up more than once. We only want - // it displayed once in the final output. - id = path.Join(r.GroupVersion, rl.Kind) - if _, ok = versionMap[id]; !ok { - versionMap[id] = struct{}{} - } - } - } - - // Convert to a form that NewVersionSet can use - for k := range versionMap { - versions = append(versions, k) - } - - return chartutil.VersionSet(versions), nil -} - -// recordRelease with an update operation in case reuse has been set. -func (c *Configuration) recordRelease(r *release.Release) { - if err := c.Releases.Update(r); err != nil { - c.Log("warning: Failed to update release %s: %s", r.Name, err) - } -} - -// InitActionConfig initializes the action configuration -func (c *Configuration) Init(getter genericclioptions.RESTClientGetter, namespace string, helmDriver string, log DebugLog) error { - kc := kube.New(getter) - kc.Log = log - - clientset, err := kc.Factory.KubernetesClientSet() - if err != nil { - return err - } - - var store *storage.Storage - switch helmDriver { - case "secret", "secrets", "": - d := driver.NewSecrets(clientset.CoreV1().Secrets(namespace)) - d.Log = log - store = storage.Init(d) - case "configmap", "configmaps": - d := driver.NewConfigMaps(clientset.CoreV1().ConfigMaps(namespace)) - d.Log = log - store = storage.Init(d) - case "memory": - d := driver.NewMemory() - store = storage.Init(d) - default: - // Not sure what to do here. - panic("Unknown driver in HELM_DRIVER: " + helmDriver) - } - - c.RESTClientGetter = getter - c.KubeClient = kc - c.Releases = store - c.Log = log - - return nil -} diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go deleted file mode 100644 index a5baec9..0000000 --- a/pkg/action/action_test.go +++ /dev/null @@ -1,303 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package action - -import ( - "context" - "flag" - "io/ioutil" - "path/filepath" - "testing" - - dockerauth "github.com/deislabs/oras/pkg/auth/docker" - fakeclientset "k8s.io/client-go/kubernetes/fake" - - "helm.sh/helm/v3/internal/experimental/registry" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - kubefake "helm.sh/helm/v3/pkg/kube/fake" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/storage" - "helm.sh/helm/v3/pkg/storage/driver" - "helm.sh/helm/v3/pkg/time" -) - -var verbose = flag.Bool("test.log", false, "enable test logging") - -func actionConfigFixture(t *testing.T) *Configuration { - t.Helper() - - client, err := dockerauth.NewClient() - if err != nil { - t.Fatal(err) - } - - resolver, err := client.Resolver(context.Background()) - if err != nil { - t.Fatal(err) - } - - tdir, err := ioutil.TempDir("", "helm-action-test") - if err != nil { - t.Fatal(err) - } - - cache, err := registry.NewCache( - registry.CacheOptDebug(true), - registry.CacheOptRoot(filepath.Join(tdir, registry.CacheRootDir)), - ) - if err != nil { - t.Fatal(err) - } - - registryClient, err := registry.NewClient( - registry.ClientOptAuthorizer(®istry.Authorizer{ - Client: client, - }), - registry.ClientOptResolver(®istry.Resolver{ - Resolver: resolver, - }), - registry.ClientOptCache(cache), - ) - if err != nil { - t.Fatal(err) - } - - return &Configuration{ - Releases: storage.Init(driver.NewMemory()), - KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: ioutil.Discard}}, - Capabilities: chartutil.DefaultCapabilities, - RegistryClient: registryClient, - Log: func(format string, v ...interface{}) { - t.Helper() - if *verbose { - t.Logf(format, v...) - } - }, - } -} - -var manifestWithHook = `kind: ConfigMap -metadata: - name: test-cm - annotations: - "helm.sh/hook": post-install,pre-delete,post-upgrade -data: - name: value` - -var manifestWithTestHook = `kind: Pod - metadata: - name: finding-nemo, - annotations: - "helm.sh/hook": test - spec: - containers: - - name: nemo-test - image: fake-image - cmd: fake-command - ` - -var rbacManifests = `apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: schedule-agents -rules: -- apiGroups: [""] - resources: ["pods", "pods/exec", "pods/log"] - verbs: ["*"] - ---- - -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: schedule-agents - namespace: {{ default .Release.Namespace}} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: schedule-agents -subjects: -- kind: ServiceAccount - name: schedule-agents - namespace: {{ .Release.Namespace }} -` - -type chartOptions struct { - *chart.Chart -} - -type chartOption func(*chartOptions) - -func buildChart(opts ...chartOption) *chart.Chart { - c := &chartOptions{ - Chart: &chart.Chart{ - // TODO: This should be more complete. - Metadata: &chart.Metadata{ - APIVersion: "v1", - Name: "hello", - Version: "0.1.0", - }, - // This adds a basic template and hooks. - Templates: []*chart.File{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, - }, - } - - for _, opt := range opts { - opt(c) - } - - return c.Chart -} - -func withName(name string) chartOption { - return func(opts *chartOptions) { - opts.Metadata.Name = name - } -} - -func withSampleValues() chartOption { - values := map[string]interface{}{ - "someKey": "someValue", - "nestedKey": map[string]interface{}{ - "simpleKey": "simpleValue", - "anotherNestedKey": map[string]interface{}{ - "yetAnotherNestedKey": map[string]interface{}{ - "youReadyForAnotherNestedKey": "No", - }, - }, - }, - } - return func(opts *chartOptions) { - opts.Values = values - } -} - -func withValues(values map[string]interface{}) chartOption { - return func(opts *chartOptions) { - opts.Values = values - } -} - -func withNotes(notes string) chartOption { - return func(opts *chartOptions) { - opts.Templates = append(opts.Templates, &chart.File{ - Name: "templates/NOTES.txt", - Data: []byte(notes), - }) - } -} - -func withDependency(dependencyOpts ...chartOption) chartOption { - return func(opts *chartOptions) { - opts.AddDependency(buildChart(dependencyOpts...)) - } -} - -func withMetadataDependency(dependency chart.Dependency) chartOption { - return func(opts *chartOptions) { - opts.Metadata.Dependencies = append(opts.Metadata.Dependencies, &dependency) - } -} - -func withSampleTemplates() chartOption { - return func(opts *chartOptions) { - sampleTemplates := []*chart.File{ - // This adds basic templates and partials. - {Name: "templates/goodbye", Data: []byte("goodbye: world")}, - {Name: "templates/empty", Data: []byte("")}, - {Name: "templates/with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)}, - {Name: "templates/partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)}, - } - opts.Templates = append(opts.Templates, sampleTemplates...) - } -} - -func withMultipleManifestTemplate() chartOption { - return func(opts *chartOptions) { - sampleTemplates := []*chart.File{ - {Name: "templates/rbac", Data: []byte(rbacManifests)}, - } - opts.Templates = append(opts.Templates, sampleTemplates...) - } -} - -func withKube(version string) chartOption { - return func(opts *chartOptions) { - opts.Metadata.KubeVersion = version - } -} - -// releaseStub creates a release stub, complete with the chartStub as its chart. -func releaseStub() *release.Release { - return namedReleaseStub("angry-panda", release.StatusDeployed) -} - -func namedReleaseStub(name string, status release.Status) *release.Release { - now := time.Now() - return &release.Release{ - Name: name, - Info: &release.Info{ - FirstDeployed: now, - LastDeployed: now, - Status: status, - Description: "Named Release Stub", - }, - Chart: buildChart(withSampleTemplates()), - Config: map[string]interface{}{"name": "value"}, - Version: 1, - Hooks: []*release.Hook{ - { - Name: "test-cm", - Kind: "ConfigMap", - Path: "test-cm", - Manifest: manifestWithHook, - Events: []release.HookEvent{ - release.HookPostInstall, - release.HookPreDelete, - }, - }, - { - Name: "finding-nemo", - Kind: "Pod", - Path: "finding-nemo", - Manifest: manifestWithTestHook, - Events: []release.HookEvent{ - release.HookTest, - }, - }, - }, - } -} - -func TestGetVersionSet(t *testing.T) { - client := fakeclientset.NewSimpleClientset() - - vs, err := GetVersionSet(client.Discovery()) - if err != nil { - t.Error(err) - } - - if !vs.Has("v1") { - t.Errorf("Expected supported versions to at least include v1.") - } - if vs.Has("nosuchversion/v1") { - t.Error("Non-existent version is reported found.") - } -} diff --git a/pkg/action/chart_export.go b/pkg/action/chart_export.go deleted file mode 100644 index 75840d8..0000000 --- a/pkg/action/chart_export.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "fmt" - "io" - "path/filepath" - - "helm.sh/helm/v3/internal/experimental/registry" - "helm.sh/helm/v3/pkg/chartutil" -) - -// ChartExport performs a chart export operation. -type ChartExport struct { - cfg *Configuration - - Destination string -} - -// NewChartExport creates a new ChartExport object with the given configuration. -func NewChartExport(cfg *Configuration) *ChartExport { - return &ChartExport{ - cfg: cfg, - } -} - -// Run executes the chart export operation -func (a *ChartExport) Run(out io.Writer, ref string) error { - r, err := registry.ParseReference(ref) - if err != nil { - return err - } - - ch, err := a.cfg.RegistryClient.LoadChart(r) - if err != nil { - return err - } - - // Save the chart to local destination directory - err = chartutil.SaveDir(ch, a.Destination) - if err != nil { - return err - } - - d := filepath.Join(a.Destination, ch.Metadata.Name) - fmt.Fprintf(out, "Exported chart to %s/\n", d) - return nil -} diff --git a/pkg/action/chart_list.go b/pkg/action/chart_list.go deleted file mode 100644 index db764b3..0000000 --- a/pkg/action/chart_list.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io" -) - -// ChartList performs a chart list operation. -type ChartList struct { - cfg *Configuration -} - -// NewChartList creates a new ChartList object with the given configuration. -func NewChartList(cfg *Configuration) *ChartList { - return &ChartList{ - cfg: cfg, - } -} - -// Run executes the chart list operation -func (a *ChartList) Run(out io.Writer) error { - return a.cfg.RegistryClient.PrintChartTable() -} diff --git a/pkg/action/chart_pull.go b/pkg/action/chart_pull.go deleted file mode 100644 index 97abde7..0000000 --- a/pkg/action/chart_pull.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io" - - "helm.sh/helm/v3/internal/experimental/registry" -) - -// ChartPull performs a chart pull operation. -type ChartPull struct { - cfg *Configuration -} - -// NewChartPull creates a new ChartPull object with the given configuration. -func NewChartPull(cfg *Configuration) *ChartPull { - return &ChartPull{ - cfg: cfg, - } -} - -// Run executes the chart pull operation -func (a *ChartPull) Run(out io.Writer, ref string) error { - r, err := registry.ParseReference(ref) - if err != nil { - return err - } - return a.cfg.RegistryClient.PullChart(r) -} diff --git a/pkg/action/chart_push.go b/pkg/action/chart_push.go deleted file mode 100644 index 91ec49d..0000000 --- a/pkg/action/chart_push.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io" - - "helm.sh/helm/v3/internal/experimental/registry" -) - -// ChartPush performs a chart push operation. -type ChartPush struct { - cfg *Configuration -} - -// NewChartPush creates a new ChartPush object with the given configuration. -func NewChartPush(cfg *Configuration) *ChartPush { - return &ChartPush{ - cfg: cfg, - } -} - -// Run executes the chart push operation -func (a *ChartPush) Run(out io.Writer, ref string) error { - r, err := registry.ParseReference(ref) - if err != nil { - return err - } - return a.cfg.RegistryClient.PushChart(r) -} diff --git a/pkg/action/chart_remove.go b/pkg/action/chart_remove.go deleted file mode 100644 index 3c0fc2e..0000000 --- a/pkg/action/chart_remove.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io" - - "helm.sh/helm/v3/internal/experimental/registry" -) - -// ChartRemove performs a chart remove operation. -type ChartRemove struct { - cfg *Configuration -} - -// NewChartRemove creates a new ChartRemove object with the given configuration. -func NewChartRemove(cfg *Configuration) *ChartRemove { - return &ChartRemove{ - cfg: cfg, - } -} - -// Run executes the chart remove operation -func (a *ChartRemove) Run(out io.Writer, ref string) error { - r, err := registry.ParseReference(ref) - if err != nil { - return err - } - return a.cfg.RegistryClient.RemoveChart(r) -} diff --git a/pkg/action/chart_save.go b/pkg/action/chart_save.go deleted file mode 100644 index 14a2d7c..0000000 --- a/pkg/action/chart_save.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io" - - "helm.sh/helm/v3/internal/experimental/registry" - "helm.sh/helm/v3/pkg/chart" -) - -// ChartSave performs a chart save operation. -type ChartSave struct { - cfg *Configuration -} - -// NewChartSave creates a new ChartSave object with the given configuration. -func NewChartSave(cfg *Configuration) *ChartSave { - return &ChartSave{ - cfg: cfg, - } -} - -// Run executes the chart save operation -func (a *ChartSave) Run(out io.Writer, ch *chart.Chart, ref string) error { - r, err := registry.ParseReference(ref) - if err != nil { - return err - } - - // If no tag is present, use the chart version - if r.Tag == "" { - r.Tag = ch.Metadata.Version - } - - return a.cfg.RegistryClient.SaveChart(ch, r) -} diff --git a/pkg/action/chart_save_test.go b/pkg/action/chart_save_test.go deleted file mode 100644 index 4fd991a..0000000 --- a/pkg/action/chart_save_test.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io/ioutil" - "testing" - - "helm.sh/helm/v3/internal/experimental/registry" -) - -func chartSaveAction(t *testing.T) *ChartSave { - t.Helper() - config := actionConfigFixture(t) - action := NewChartSave(config) - return action -} - -func TestChartSave(t *testing.T) { - action := chartSaveAction(t) - - input := buildChart() - if err := action.Run(ioutil.Discard, input, "localhost:5000/test:0.2.0"); err != nil { - t.Error(err) - } - - ref, err := registry.ParseReference("localhost:5000/test:0.2.0") - if err != nil { - t.Fatal(err) - } - - if _, err := action.cfg.RegistryClient.LoadChart(ref); err != nil { - t.Error(err) - } - - // now let's check if `helm chart save` can use the chart version when the tag is not present - if err := action.Run(ioutil.Discard, input, "localhost:5000/test"); err != nil { - t.Error(err) - } - - ref, err = registry.ParseReference("localhost:5000/test") - if err != nil { - t.Fatal(err) - } - - // TODO: guess latest based on semver? - _, err = action.cfg.RegistryClient.LoadChart(ref) - if err == nil { - t.Error("Expected error parsing ref without tag") - } - - ref.Tag = "0.1.0" - if _, err := action.cfg.RegistryClient.LoadChart(ref); err != nil { - t.Error(err) - } -} diff --git a/pkg/action/dependency.go b/pkg/action/dependency.go deleted file mode 100644 index 5781cc9..0000000 --- a/pkg/action/dependency.go +++ /dev/null @@ -1,185 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "fmt" - "io" - "os" - "path/filepath" - - "github.com/Masterminds/semver/v3" - "github.com/gosuri/uitable" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" -) - -// Dependency is the action for building a given chart's dependency tree. -// -// It provides the implementation of 'helm dependency' and its respective subcommands. -type Dependency struct { - Verify bool - Keyring string - SkipRefresh bool -} - -// NewDependency creates a new Dependency object with the given configuration. -func NewDependency() *Dependency { - return &Dependency{} -} - -// List executes 'helm dependency list'. -func (d *Dependency) List(chartpath string, out io.Writer) error { - c, err := loader.Load(chartpath) - if err != nil { - return err - } - - if c.Metadata.Dependencies == nil { - fmt.Fprintf(out, "WARNING: no dependencies at %s\n", filepath.Join(chartpath, "charts")) - return nil - } - - d.printDependencies(chartpath, out, c.Metadata.Dependencies) - fmt.Fprintln(out) - d.printMissing(chartpath, out, c.Metadata.Dependencies) - return nil -} - -func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency) string { - filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*") - - switch archives, err := filepath.Glob(filepath.Join(chartpath, "charts", filename)); { - case err != nil: - return "bad pattern" - case len(archives) > 1: - return "too many matches" - case len(archives) == 1: - archive := archives[0] - if _, err := os.Stat(archive); err == nil { - c, err := loader.Load(archive) - if err != nil { - return "corrupt" - } - if c.Name() != dep.Name { - return "misnamed" - } - - if c.Metadata.Version != dep.Version { - constraint, err := semver.NewConstraint(dep.Version) - if err != nil { - return "invalid version" - } - - v, err := semver.NewVersion(c.Metadata.Version) - if err != nil { - return "invalid version" - } - - if constraint.Check(v) { - return "ok" - } - return "wrong version" - } - return "ok" - } - } - - folder := filepath.Join(chartpath, "charts", dep.Name) - if fi, err := os.Stat(folder); err != nil { - return "missing" - } else if !fi.IsDir() { - return "mispackaged" - } - - c, err := loader.Load(folder) - if err != nil { - return "corrupt" - } - - if c.Name() != dep.Name { - return "misnamed" - } - - if c.Metadata.Version != dep.Version { - constraint, err := semver.NewConstraint(dep.Version) - if err != nil { - return "invalid version" - } - - v, err := semver.NewVersion(c.Metadata.Version) - if err != nil { - return "invalid version" - } - - if constraint.Check(v) { - return "unpacked" - } - return "wrong version" - } - - return "unpacked" -} - -// printDependencies prints all of the dependencies in the yaml file. -func (d *Dependency) printDependencies(chartpath string, out io.Writer, reqs []*chart.Dependency) { - table := uitable.New() - table.MaxColWidth = 80 - table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS") - for _, row := range reqs { - table.AddRow(row.Name, row.Version, row.Repository, d.dependencyStatus(chartpath, row)) - } - fmt.Fprintln(out, table) -} - -// printMissing prints warnings about charts that are present on disk, but are -// not in Charts.yaml. -func (d *Dependency) printMissing(chartpath string, out io.Writer, reqs []*chart.Dependency) { - folder := filepath.Join(chartpath, "charts/*") - files, err := filepath.Glob(folder) - if err != nil { - fmt.Fprintln(out, err) - return - } - - for _, f := range files { - fi, err := os.Stat(f) - if err != nil { - fmt.Fprintf(out, "Warning: %s\n", err) - } - // Skip anything that is not a directory and not a tgz file. - if !fi.IsDir() && filepath.Ext(f) != ".tgz" { - continue - } - c, err := loader.Load(f) - if err != nil { - fmt.Fprintf(out, "WARNING: %q is not a chart.\n", f) - continue - } - found := false - for _, d := range reqs { - if d.Name == c.Name() { - found = true - break - } - } - if !found { - fmt.Fprintf(out, "WARNING: %q is not in Chart.yaml.\n", f) - } - } -} diff --git a/pkg/action/doc.go b/pkg/action/doc.go deleted file mode 100644 index 3c91bd6..0000000 --- a/pkg/action/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package action contains the logic for each action that Helm can perform. -// -// This is a library for calling top-level Helm actions like 'install', -// 'upgrade', or 'list'. Actions approximately match the command line -// invocations that the Helm client uses. -package action diff --git a/pkg/action/get.go b/pkg/action/get.go deleted file mode 100644 index c776eb6..0000000 --- a/pkg/action/get.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "helm.sh/helm/v3/pkg/release" -) - -// Get is the action for checking a given release's information. -// -// It provides the implementation of 'helm get' and its respective subcommands (except `helm get values`). -type Get struct { - cfg *Configuration - - Version int -} - -// NewGet creates a new Get object with the given configuration. -func NewGet(cfg *Configuration) *Get { - return &Get{ - cfg: cfg, - } -} - -// Run executes 'helm get' against the given release. -func (g *Get) Run(name string) (*release.Release, error) { - if err := g.cfg.KubeClient.IsReachable(); err != nil { - return nil, err - } - - return g.cfg.releaseContent(name, g.Version) -} diff --git a/pkg/action/get_values.go b/pkg/action/get_values.go deleted file mode 100644 index 9c32db2..0000000 --- a/pkg/action/get_values.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "helm.sh/helm/v3/pkg/chartutil" -) - -// GetValues is the action for checking a given release's values. -// -// It provides the implementation of 'helm get values'. -type GetValues struct { - cfg *Configuration - - Version int - AllValues bool -} - -// NewGetValues creates a new GetValues object with the given configuration. -func NewGetValues(cfg *Configuration) *GetValues { - return &GetValues{ - cfg: cfg, - } -} - -// Run executes 'helm get values' against the given release. -func (g *GetValues) Run(name string) (map[string]interface{}, error) { - if err := g.cfg.KubeClient.IsReachable(); err != nil { - return nil, err - } - - rel, err := g.cfg.releaseContent(name, g.Version) - if err != nil { - return nil, err - } - - // If the user wants all values, compute the values and return. - if g.AllValues { - cfg, err := chartutil.CoalesceValues(rel.Chart, rel.Config) - if err != nil { - return nil, err - } - return cfg, nil - } - return rel.Config, nil -} diff --git a/pkg/action/history.go b/pkg/action/history.go deleted file mode 100644 index a592745..0000000 --- a/pkg/action/history.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/release" -) - -// History is the action for checking the release's ledger. -// -// It provides the implementation of 'helm history'. -type History struct { - cfg *Configuration - - Max int - Version int -} - -// NewHistory creates a new History object with the given configuration. -func NewHistory(cfg *Configuration) *History { - return &History{ - cfg: cfg, - } -} - -// Run executes 'helm history' against the given release. -func (h *History) Run(name string) ([]*release.Release, error) { - if err := h.cfg.KubeClient.IsReachable(); err != nil { - return nil, err - } - - if err := validateReleaseName(name); err != nil { - return nil, errors.Errorf("release name is invalid: %s", name) - } - - h.cfg.Log("getting history for release %s", name) - return h.cfg.Releases.History(name) -} diff --git a/pkg/action/hooks.go b/pkg/action/hooks.go deleted file mode 100644 index a161f93..0000000 --- a/pkg/action/hooks.go +++ /dev/null @@ -1,150 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "bytes" - "sort" - "time" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/release" - helmtime "helm.sh/helm/v3/pkg/time" -) - -// execHook executes all of the hooks for the given hook event. -func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent, timeout time.Duration) error { - executingHooks := []*release.Hook{} - - for _, h := range rl.Hooks { - for _, e := range h.Events { - if e == hook { - executingHooks = append(executingHooks, h) - } - } - } - - sort.Sort(hookByWeight(executingHooks)) - - for _, h := range executingHooks { - // Set default delete policy to before-hook-creation - if h.DeletePolicies == nil || len(h.DeletePolicies) == 0 { - // TODO(jlegrone): Only apply before-hook-creation delete policy to run to completion - // resources. For all other resource types update in place if a - // resource with the same name already exists and is owned by the - // current release. - h.DeletePolicies = []release.HookDeletePolicy{release.HookBeforeHookCreation} - } - - if err := cfg.deleteHookByPolicy(h, release.HookBeforeHookCreation); err != nil { - return err - } - - resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest), true) - if err != nil { - return errors.Wrapf(err, "unable to build kubernetes object for %s hook %s", hook, h.Path) - } - - // Record the time at which the hook was applied to the cluster - h.LastRun = release.HookExecution{ - StartedAt: helmtime.Now(), - Phase: release.HookPhaseRunning, - } - cfg.recordRelease(rl) - - // As long as the implementation of WatchUntilReady does not panic, HookPhaseFailed or HookPhaseSucceeded - // should always be set by this function. If we fail to do that for any reason, then HookPhaseUnknown is - // the most appropriate value to surface. - h.LastRun.Phase = release.HookPhaseUnknown - - // Create hook resources - if _, err := cfg.KubeClient.Create(resources); err != nil { - h.LastRun.CompletedAt = helmtime.Now() - h.LastRun.Phase = release.HookPhaseFailed - return errors.Wrapf(err, "warning: Hook %s %s failed", hook, h.Path) - } - - // Watch hook resources until they have completed - err = cfg.KubeClient.WatchUntilReady(resources, timeout) - // Note the time of success/failure - h.LastRun.CompletedAt = helmtime.Now() - // Mark hook as succeeded or failed - if err != nil { - h.LastRun.Phase = release.HookPhaseFailed - // If a hook is failed, check the annotation of the hook to determine whether the hook should be deleted - // under failed condition. If so, then clear the corresponding resource object in the hook - if err := cfg.deleteHookByPolicy(h, release.HookFailed); err != nil { - return err - } - return err - } - h.LastRun.Phase = release.HookPhaseSucceeded - } - - // If all hooks are successful, check the annotation of each hook to determine whether the hook should be deleted - // under succeeded condition. If so, then clear the corresponding resource object in each hook - for _, h := range executingHooks { - if err := cfg.deleteHookByPolicy(h, release.HookSucceeded); err != nil { - return err - } - } - - return nil -} - -// hookByWeight is a sorter for hooks -type hookByWeight []*release.Hook - -func (x hookByWeight) Len() int { return len(x) } -func (x hookByWeight) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x hookByWeight) Less(i, j int) bool { - if x[i].Weight == x[j].Weight { - return x[i].Name < x[j].Name - } - return x[i].Weight < x[j].Weight -} - -// deleteHookByPolicy deletes a hook if the hook policy instructs it to -func (cfg *Configuration) deleteHookByPolicy(h *release.Hook, policy release.HookDeletePolicy) error { - // Never delete CustomResourceDefinitions; this could cause lots of - // cascading garbage collection. - if h.Kind == "CustomResourceDefinition" { - return nil - } - if hookHasDeletePolicy(h, policy) { - resources, err := cfg.KubeClient.Build(bytes.NewBufferString(h.Manifest), false) - if err != nil { - return errors.Wrapf(err, "unable to build kubernetes object for deleting hook %s", h.Path) - } - _, errs := cfg.KubeClient.Delete(resources) - if len(errs) > 0 { - return errors.New(joinErrors(errs)) - } - } - return nil -} - -// hookHasDeletePolicy determines whether the defined hook deletion policy matches the hook deletion polices -// supported by helm. If so, mark the hook as one should be deleted. -func hookHasDeletePolicy(h *release.Hook, policy release.HookDeletePolicy) bool { - for _, v := range h.DeletePolicies { - if policy == v { - return true - } - } - return false -} diff --git a/pkg/action/install.go b/pkg/action/install.go deleted file mode 100644 index 292a7ec..0000000 --- a/pkg/action/install.go +++ /dev/null @@ -1,709 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" - "strings" - "text/template" - "time" - - "github.com/Masterminds/sprig/v3" - "github.com/pkg/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/cli-runtime/pkg/resource" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/engine" - "helm.sh/helm/v3/pkg/getter" - kubefake "helm.sh/helm/v3/pkg/kube/fake" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/releaseutil" - "helm.sh/helm/v3/pkg/repo" - "helm.sh/helm/v3/pkg/storage" - "helm.sh/helm/v3/pkg/storage/driver" -) - -// releaseNameMaxLen is the maximum length of a release name. -// -// As of Kubernetes 1.4, the max limit on a name is 63 chars. We reserve 10 for -// charts to add data. Effectively, that gives us 53 chars. -// See https://github.com/helm/helm/issues/1528 -const releaseNameMaxLen = 53 - -// NOTESFILE_SUFFIX that we want to treat special. It goes through the templating engine -// but it's not a yaml file (resource) hence can't have hooks, etc. And the user actually -// wants to see this file after rendering in the status command. However, it must be a suffix -// since there can be filepath in front of it. -const notesFileSuffix = "NOTES.txt" - -const defaultDirectoryPermission = 0755 - -// Install performs an installation operation. -type Install struct { - cfg *Configuration - - ChartPathOptions - - ClientOnly bool - DryRun bool - DisableHooks bool - Replace bool - Wait bool - Devel bool - DependencyUpdate bool - Timeout time.Duration - Namespace string - ReleaseName string - GenerateName bool - NameTemplate string - Description string - OutputDir string - Atomic bool - SkipCRDs bool - SubNotes bool - // APIVersions allows a manual set of supported API Versions to be passed - // (for things like templating). These are ignored if ClientOnly is false - APIVersions chartutil.VersionSet - // Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false - IsUpgrade bool -} - -// ChartPathOptions captures common options used for controlling chart paths -type ChartPathOptions struct { - CaFile string // --ca-file - CertFile string // --cert-file - KeyFile string // --key-file - Keyring string // --keyring - Password string // --password - RepoURL string // --repo - Username string // --username - Verify bool // --verify - Version string // --version -} - -// NewInstall creates a new Install object with the given configuration. -func NewInstall(cfg *Configuration) *Install { - return &Install{ - cfg: cfg, - } -} - -func (i *Install) installCRDs(crds []*chart.File) error { - // We do these one file at a time in the order they were read. - totalItems := []*resource.Info{} - for _, obj := range crds { - // Read in the resources - res, err := i.cfg.KubeClient.Build(bytes.NewBuffer(obj.Data), false) - if err != nil { - return errors.Wrapf(err, "failed to install CRD %s", obj.Name) - } - - // Send them to Kube - if _, err := i.cfg.KubeClient.Create(res); err != nil { - // If the error is CRD already exists, continue. - if apierrors.IsAlreadyExists(err) { - crdName := res[0].Name - i.cfg.Log("CRD %s is already present. Skipping.", crdName) - continue - } - return errors.Wrapf(err, "failed to install CRD %s", obj.Name) - } - totalItems = append(totalItems, res...) - } - // Invalidate the local cache, since it will not have the new CRDs - // present. - discoveryClient, err := i.cfg.RESTClientGetter.ToDiscoveryClient() - if err != nil { - return err - } - i.cfg.Log("Clearing discovery cache") - discoveryClient.Invalidate() - // Give time for the CRD to be recognized. - if err := i.cfg.KubeClient.Wait(totalItems, 60*time.Second); err != nil { - return err - } - // Make sure to force a rebuild of the cache. - discoveryClient.ServerGroups() - return nil -} - -// Run executes the installation -// -// If DryRun is set to true, this will prepare the release, but not install it -func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.Release, error) { - // Check reachability of cluster unless in client-only mode (e.g. `helm template` without `--validate`) - if !i.ClientOnly { - if err := i.cfg.KubeClient.IsReachable(); err != nil { - return nil, err - } - } - - if err := i.availableName(); err != nil { - return nil, err - } - - // Pre-install anything in the crd/ directory. We do this before Helm - // contacts the upstream server and builds the capabilities object. - if crds := chrt.CRDs(); !i.ClientOnly && !i.SkipCRDs && len(crds) > 0 { - // On dry run, bail here - if i.DryRun { - i.cfg.Log("WARNING: This chart or one of its subcharts contains CRDs. Rendering may fail or contain inaccuracies.") - } else if err := i.installCRDs(crds); err != nil { - return nil, err - } - } - - if i.ClientOnly { - // Add mock objects in here so it doesn't use Kube API server - // NOTE(bacongobbler): used for `helm template` - i.cfg.Capabilities = chartutil.DefaultCapabilities - i.cfg.Capabilities.APIVersions = append(i.cfg.Capabilities.APIVersions, i.APIVersions...) - i.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard} - i.cfg.Releases = storage.Init(driver.NewMemory()) - } else if !i.ClientOnly && len(i.APIVersions) > 0 { - i.cfg.Log("API Version list given outside of client only mode, this list will be ignored") - } - - if err := chartutil.ProcessDependencies(chrt, vals); err != nil { - return nil, err - } - - // Make sure if Atomic is set, that wait is set as well. This makes it so - // the user doesn't have to specify both - i.Wait = i.Wait || i.Atomic - - caps, err := i.cfg.getCapabilities() - if err != nil { - return nil, err - } - - //special case for helm template --is-upgrade - isUpgrade := i.IsUpgrade && i.DryRun - options := chartutil.ReleaseOptions{ - Name: i.ReleaseName, - Namespace: i.Namespace, - Revision: 1, - IsInstall: !isUpgrade, - IsUpgrade: isUpgrade, - } - valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps) - if err != nil { - return nil, err - } - - rel := i.createRelease(chrt, vals) - - var manifestDoc *bytes.Buffer - rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.OutputDir, i.SubNotes) - // Even for errors, attach this if available - if manifestDoc != nil { - rel.Manifest = manifestDoc.String() - } - // Check error from render - if err != nil { - rel.SetStatus(release.StatusFailed, fmt.Sprintf("failed to render resource: %s", err.Error())) - // Return a release with partial data so that the client can show debugging information. - return rel, err - } - - // Mark this release as in-progress - rel.SetStatus(release.StatusPendingInstall, "Initial install underway") - - resources, err := i.cfg.KubeClient.Build(bytes.NewBufferString(rel.Manifest), true) - if err != nil { - return nil, errors.Wrap(err, "unable to build kubernetes objects from release manifest") - } - - // Install requires an extra validation step of checking that resources - // don't already exist before we actually create resources. If we continue - // forward and create the release object with resources that already exist, - // we'll end up in a state where we will delete those resources upon - // deleting the release because the manifest will be pointing at that - // resource - if !i.ClientOnly && !isUpgrade { - if err := existingResourceConflict(resources); err != nil { - return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install") - } - } - - // Bail out here if it is a dry run - if i.DryRun { - rel.Info.Description = "Dry run complete" - return rel, nil - } - - // If Replace is true, we need to supercede the last release. - if i.Replace { - if err := i.replaceRelease(rel); err != nil { - return nil, err - } - } - - // Store the release in history before continuing (new in Helm 3). We always know - // that this is a create operation. - if err := i.cfg.Releases.Create(rel); err != nil { - // We could try to recover gracefully here, but since nothing has been installed - // yet, this is probably safer than trying to continue when we know storage is - // not working. - return rel, err - } - - // pre-install hooks - if !i.DisableHooks { - if err := i.cfg.execHook(rel, release.HookPreInstall, i.Timeout); err != nil { - return i.failRelease(rel, fmt.Errorf("failed pre-install: %s", err)) - } - } - - // At this point, we can do the install. Note that before we were detecting whether to - // do an update, but it's not clear whether we WANT to do an update if the re-use is set - // to true, since that is basically an upgrade operation. - if _, err := i.cfg.KubeClient.Create(resources); err != nil { - return i.failRelease(rel, err) - } - - if i.Wait { - if err := i.cfg.KubeClient.Wait(resources, i.Timeout); err != nil { - return i.failRelease(rel, err) - } - - } - - if !i.DisableHooks { - if err := i.cfg.execHook(rel, release.HookPostInstall, i.Timeout); err != nil { - return i.failRelease(rel, fmt.Errorf("failed post-install: %s", err)) - } - } - - if len(i.Description) > 0 { - rel.SetStatus(release.StatusDeployed, i.Description) - } else { - rel.SetStatus(release.StatusDeployed, "Install complete") - } - - // This is a tricky case. The release has been created, but the result - // cannot be recorded. The truest thing to tell the user is that the - // release was created. However, the user will not be able to do anything - // further with this release. - // - // One possible strategy would be to do a timed retry to see if we can get - // this stored in the future. - if err := i.recordRelease(rel); err != nil { - i.cfg.Log("failed to record the release: %s", err) - } - - return rel, nil -} - -func (i *Install) failRelease(rel *release.Release, err error) (*release.Release, error) { - rel.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", i.ReleaseName, err.Error())) - if i.Atomic { - i.cfg.Log("Install failed and atomic is set, uninstalling release") - uninstall := NewUninstall(i.cfg) - uninstall.DisableHooks = i.DisableHooks - uninstall.KeepHistory = false - uninstall.Timeout = i.Timeout - if _, uninstallErr := uninstall.Run(i.ReleaseName); uninstallErr != nil { - return rel, errors.Wrapf(uninstallErr, "an error occurred while uninstalling the release. original install error: %s", err) - } - return rel, errors.Wrapf(err, "release %s failed, and has been uninstalled due to atomic being set", i.ReleaseName) - } - i.recordRelease(rel) // Ignore the error, since we have another error to deal with. - return rel, err -} - -// availableName tests whether a name is available -// -// Roughly, this will return an error if name is -// -// - empty -// - too long -// - already in use, and not deleted -// - used by a deleted release, and i.Replace is false -func (i *Install) availableName() error { - start := i.ReleaseName - if start == "" { - return errors.New("name is required") - } - - if len(start) > releaseNameMaxLen { - return errors.Errorf("release name %q exceeds max length of %d", start, releaseNameMaxLen) - } - - if i.DryRun { - return nil - } - - h, err := i.cfg.Releases.History(start) - if err != nil || len(h) < 1 { - return nil - } - releaseutil.Reverse(h, releaseutil.SortByRevision) - rel := h[0] - - if st := rel.Info.Status; i.Replace && (st == release.StatusUninstalled || st == release.StatusFailed) { - return nil - } - return errors.New("cannot re-use a name that is still in use") -} - -// createRelease creates a new release object -func (i *Install) createRelease(chrt *chart.Chart, rawVals map[string]interface{}) *release.Release { - ts := i.cfg.Now() - return &release.Release{ - Name: i.ReleaseName, - Namespace: i.Namespace, - Chart: chrt, - Config: rawVals, - Info: &release.Info{ - FirstDeployed: ts, - LastDeployed: ts, - Status: release.StatusUnknown, - }, - Version: 1, - } -} - -// recordRelease with an update operation in case reuse has been set. -func (i *Install) recordRelease(r *release.Release) error { - // This is a legacy function which has been reduced to a oneliner. Could probably - // refactor it out. - return i.cfg.Releases.Update(r) -} - -// replaceRelease replaces an older release with this one -// -// This allows us to re-use names by superseding an existing release with a new one -func (i *Install) replaceRelease(rel *release.Release) error { - hist, err := i.cfg.Releases.History(rel.Name) - if err != nil || len(hist) == 0 { - // No releases exist for this name, so we can return early - return nil - } - - releaseutil.Reverse(hist, releaseutil.SortByRevision) - last := hist[0] - - // Update version to the next available - rel.Version = last.Version + 1 - - // Do not change the status of a failed release. - if last.Info.Status == release.StatusFailed { - return nil - } - - // For any other status, mark it as superseded and store the old record - last.SetStatus(release.StatusSuperseded, "superseded by new release") - return i.recordRelease(last) -} - -// renderResources renders the templates in a chart -func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, outputDir string, subNotes bool) ([]*release.Hook, *bytes.Buffer, string, error) { - hs := []*release.Hook{} - b := bytes.NewBuffer(nil) - - caps, err := c.getCapabilities() - if err != nil { - return hs, b, "", err - } - - if ch.Metadata.KubeVersion != "" { - if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) { - return hs, b, "", errors.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String()) - } - } - - var files map[string]string - var err2 error - - if c.RESTClientGetter != nil { - rest, err := c.RESTClientGetter.ToRESTConfig() - if err != nil { - return hs, b, "", err - } - files, err2 = engine.RenderWithClient(ch, values, rest) - } else { - files, err2 = engine.Render(ch, values) - } - - if err2 != nil { - return hs, b, "", err - } - - // NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource, - // pull it out of here into a separate file so that we can actually use the output of the rendered - // text file. We have to spin through this map because the file contains path information, so we - // look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip - // it in the sortHooks. - var notesBuffer bytes.Buffer - for k, v := range files { - if strings.HasSuffix(k, notesFileSuffix) { - if subNotes || (k == path.Join(ch.Name(), "templates", notesFileSuffix)) { - // If buffer contains data, add newline before adding more - if notesBuffer.Len() > 0 { - notesBuffer.WriteString("\n") - } - notesBuffer.WriteString(v) - } - delete(files, k) - } - } - notes := notesBuffer.String() - - // Sort hooks, manifests, and partials. Only hooks and manifests are returned, - // as partials are not used after renderer.Render. Empty manifests are also - // removed here. - hs, manifests, err := releaseutil.SortManifests(files, caps.APIVersions, releaseutil.InstallOrder) - if err != nil { - // By catching parse errors here, we can prevent bogus releases from going - // to Kubernetes. - // - // We return the files as a big blob of data to help the user debug parser - // errors. - for name, content := range files { - if strings.TrimSpace(content) == "" { - continue - } - fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content) - } - return hs, b, "", err - } - - // Aggregate all valid manifests into one big doc. - fileWritten := make(map[string]bool) - for _, m := range manifests { - if outputDir == "" { - fmt.Fprintf(b, "---\n# Source: %s\n%s\n", m.Name, m.Content) - } else { - err = writeToFile(outputDir, m.Name, m.Content, fileWritten[m.Name]) - if err != nil { - return hs, b, "", err - } - fileWritten[m.Name] = true - } - } - - return hs, b, notes, nil -} - -// write the to /. controls if the file is created or content will be appended -func writeToFile(outputDir string, name string, data string, append bool) error { - outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator)) - - err := ensureDirectoryForFile(outfileName) - if err != nil { - return err - } - - f, err := createOrOpenFile(outfileName, append) - if err != nil { - return err - } - - defer f.Close() - - _, err = f.WriteString(fmt.Sprintf("---\n# Source: %s\n%s\n", name, data)) - - if err != nil { - return err - } - - fmt.Printf("wrote %s\n", outfileName) - return nil -} - -func createOrOpenFile(filename string, append bool) (*os.File, error) { - if append { - return os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0600) - } - return os.Create(filename) -} - -// check if the directory exists to create file. creates if don't exists -func ensureDirectoryForFile(file string) error { - baseDir := path.Dir(file) - _, err := os.Stat(baseDir) - if err != nil && !os.IsNotExist(err) { - return err - } - - return os.MkdirAll(baseDir, defaultDirectoryPermission) -} - -// NameAndChart returns the name and chart that should be used. -// -// This will read the flags and handle name generation if necessary. -func (i *Install) NameAndChart(args []string) (string, string, error) { - flagsNotSet := func() error { - if i.GenerateName { - return errors.New("cannot set --generate-name and also specify a name") - } - if i.NameTemplate != "" { - return errors.New("cannot set --name-template and also specify a name") - } - return nil - } - - if len(args) > 2 { - return args[0], args[1], errors.Errorf("expected at most two arguments, unexpected arguments: %v", strings.Join(args[2:], ", ")) - } - - if len(args) == 2 { - return args[0], args[1], flagsNotSet() - } - - if i.NameTemplate != "" { - name, err := TemplateName(i.NameTemplate) - return name, args[0], err - } - - if i.ReleaseName != "" { - return i.ReleaseName, args[0], nil - } - - if !i.GenerateName { - return "", args[0], errors.New("must either provide a name or specify --generate-name") - } - - base := filepath.Base(args[0]) - if base == "." || base == "" { - base = "chart" - } - // if present, strip out the file extension from the name - if idx := strings.Index(base, "."); idx != -1 { - base = base[0:idx] - } - - return fmt.Sprintf("%s-%d", base, time.Now().Unix()), args[0], nil -} - -// TemplateName renders a name template, returning the name or an error. -func TemplateName(nameTemplate string) (string, error) { - if nameTemplate == "" { - return "", nil - } - - t, err := template.New("name-template").Funcs(sprig.TxtFuncMap()).Parse(nameTemplate) - if err != nil { - return "", err - } - var b bytes.Buffer - if err := t.Execute(&b, nil); err != nil { - return "", err - } - - return b.String(), nil -} - -// CheckDependencies checks the dependencies for a chart. -func CheckDependencies(ch *chart.Chart, reqs []*chart.Dependency) error { - var missing []string - -OUTER: - for _, r := range reqs { - for _, d := range ch.Dependencies() { - if d.Name() == r.Name { - continue OUTER - } - } - missing = append(missing, r.Name) - } - - if len(missing) > 0 { - return errors.Errorf("found in Chart.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", ")) - } - return nil -} - -// LocateChart looks for a chart directory in known places, and returns either the full path or an error. -// -// This does not ensure that the chart is well-formed; only that the requested filename exists. -// -// Order of resolution: -// - relative to current working directory -// - if path is absolute or begins with '.', error out here -// - URL -// -// If 'verify' was set on ChartPathOptions, this will attempt to also verify the chart. -func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (string, error) { - name = strings.TrimSpace(name) - version := strings.TrimSpace(c.Version) - - if _, err := os.Stat(name); err == nil { - abs, err := filepath.Abs(name) - if err != nil { - return abs, err - } - if c.Verify { - if _, err := downloader.VerifyChart(abs, c.Keyring); err != nil { - return "", err - } - } - return abs, nil - } - if filepath.IsAbs(name) || strings.HasPrefix(name, ".") { - return name, errors.Errorf("path %q not found", name) - } - - dl := downloader.ChartDownloader{ - Out: os.Stdout, - Keyring: c.Keyring, - Getters: getter.All(settings), - Options: []getter.Option{ - getter.WithBasicAuth(c.Username, c.Password), - }, - RepositoryConfig: settings.RepositoryConfig, - RepositoryCache: settings.RepositoryCache, - } - if c.Verify { - dl.Verify = downloader.VerifyAlways - } - if c.RepoURL != "" { - chartURL, err := repo.FindChartInAuthRepoURL(c.RepoURL, c.Username, c.Password, name, version, - c.CertFile, c.KeyFile, c.CaFile, getter.All(settings)) - if err != nil { - return "", err - } - name = chartURL - } - - if err := os.MkdirAll(settings.RepositoryCache, 0755); err != nil { - return "", err - } - - filename, _, err := dl.DownloadTo(name, version, settings.RepositoryCache) - if err == nil { - lname, err := filepath.Abs(filename) - if err != nil { - return filename, err - } - return lname, nil - } else if settings.Debug { - return filename, err - } - - return filename, errors.Errorf("failed to download %q (hint: running `helm repo update` may help)", name) -} diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go deleted file mode 100644 index d6f1c88..0000000 --- a/pkg/action/install_test.go +++ /dev/null @@ -1,576 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "fmt" - "io/ioutil" - "log" - "os" - "path/filepath" - "regexp" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - - "helm.sh/helm/v3/internal/test" - "helm.sh/helm/v3/pkg/chartutil" - kubefake "helm.sh/helm/v3/pkg/kube/fake" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/storage/driver" - "helm.sh/helm/v3/pkg/time" -) - -type nameTemplateTestCase struct { - tpl string - expected string - expectedErrorStr string -} - -func installAction(t *testing.T) *Install { - config := actionConfigFixture(t) - instAction := NewInstall(config) - instAction.Namespace = "spaced" - instAction.ReleaseName = "test-install-release" - - return instAction -} - -func TestInstallRelease(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - vals := map[string]interface{}{} - res, err := instAction.Run(buildChart(), vals) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - is.Equal(res.Name, "test-install-release", "Expected release name.") - is.Equal(res.Namespace, "spaced") - - rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) - is.NoError(err) - - is.Len(rel.Hooks, 1) - is.Equal(rel.Hooks[0].Manifest, manifestWithHook) - is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall) - is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete") - - is.NotEqual(len(res.Manifest), 0) - is.NotEqual(len(rel.Manifest), 0) - is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") - is.Equal(rel.Info.Description, "Install complete") -} - -func TestInstallReleaseWithValues(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - userVals := map[string]interface{}{ - "nestedKey": map[string]interface{}{ - "simpleKey": "simpleValue", - }, - } - expectedUserValues := map[string]interface{}{ - "nestedKey": map[string]interface{}{ - "simpleKey": "simpleValue", - }, - } - res, err := instAction.Run(buildChart(withSampleValues()), userVals) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - is.Equal(res.Name, "test-install-release", "Expected release name.") - is.Equal(res.Namespace, "spaced") - - rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) - is.NoError(err) - - is.Len(rel.Hooks, 1) - is.Equal(rel.Hooks[0].Manifest, manifestWithHook) - is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall) - is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete") - - is.NotEqual(len(res.Manifest), 0) - is.NotEqual(len(rel.Manifest), 0) - is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") - is.Equal("Install complete", rel.Info.Description) - is.Equal(expectedUserValues, rel.Config) -} - -func TestInstallReleaseClientOnly(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - instAction.ClientOnly = true - instAction.Run(buildChart(), nil) // disregard output - - is.Equal(instAction.cfg.Capabilities, chartutil.DefaultCapabilities) - is.Equal(instAction.cfg.KubeClient, &kubefake.PrintingKubeClient{Out: ioutil.Discard}) -} - -func TestInstallRelease_NoName(t *testing.T) { - instAction := installAction(t) - instAction.ReleaseName = "" - vals := map[string]interface{}{} - _, err := instAction.Run(buildChart(), vals) - if err == nil { - t.Fatal("expected failure when no name is specified") - } - assert.Contains(t, err.Error(), "name is required") -} - -func TestInstallRelease_WithNotes(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - instAction.ReleaseName = "with-notes" - vals := map[string]interface{}{} - res, err := instAction.Run(buildChart(withNotes("note here")), vals) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - - is.Equal(res.Name, "with-notes") - is.Equal(res.Namespace, "spaced") - - rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) - is.NoError(err) - is.Len(rel.Hooks, 1) - is.Equal(rel.Hooks[0].Manifest, manifestWithHook) - is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall) - is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete") - is.NotEqual(len(res.Manifest), 0) - is.NotEqual(len(rel.Manifest), 0) - is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") - is.Equal(rel.Info.Description, "Install complete") - - is.Equal(rel.Info.Notes, "note here") -} - -func TestInstallRelease_WithNotesRendered(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - instAction.ReleaseName = "with-notes" - vals := map[string]interface{}{} - res, err := instAction.Run(buildChart(withNotes("got-{{.Release.Name}}")), vals) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - - rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) - is.NoError(err) - - expectedNotes := fmt.Sprintf("got-%s", res.Name) - is.Equal(expectedNotes, rel.Info.Notes) - is.Equal(rel.Info.Description, "Install complete") -} - -func TestInstallRelease_WithChartAndDependencyParentNotes(t *testing.T) { - // Regression: Make sure that the child's notes don't override the parent's - is := assert.New(t) - instAction := installAction(t) - instAction.ReleaseName = "with-notes" - vals := map[string]interface{}{} - res, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child"))), vals) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - - rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) - is.Equal("with-notes", rel.Name) - is.NoError(err) - is.Equal("parent", rel.Info.Notes) - is.Equal(rel.Info.Description, "Install complete") -} - -func TestInstallRelease_WithChartAndDependencyAllNotes(t *testing.T) { - // Regression: Make sure that the child's notes don't override the parent's - is := assert.New(t) - instAction := installAction(t) - instAction.ReleaseName = "with-notes" - instAction.SubNotes = true - vals := map[string]interface{}{} - res, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child"))), vals) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - - rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) - is.Equal("with-notes", rel.Name) - is.NoError(err) - // test run can return as either 'parent\nchild' or 'child\nparent' - if !strings.Contains(rel.Info.Notes, "parent") && !strings.Contains(rel.Info.Notes, "child") { - t.Fatalf("Expected 'parent\nchild' or 'child\nparent', got '%s'", rel.Info.Notes) - } - is.Equal(rel.Info.Description, "Install complete") -} - -func TestInstallRelease_DryRun(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - instAction.DryRun = true - vals := map[string]interface{}{} - res, err := instAction.Run(buildChart(withSampleTemplates()), vals) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - - is.Contains(res.Manifest, "---\n# Source: hello/templates/hello\nhello: world") - is.Contains(res.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world") - is.Contains(res.Manifest, "hello: Earth") - is.NotContains(res.Manifest, "hello: {{ template \"_planet\" . }}") - is.NotContains(res.Manifest, "empty") - - _, err = instAction.cfg.Releases.Get(res.Name, res.Version) - is.Error(err) - is.Len(res.Hooks, 1) - is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "expect hook to not be marked as run") - is.Equal(res.Info.Description, "Dry run complete") -} - -func TestInstallRelease_NoHooks(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - instAction.DisableHooks = true - instAction.ReleaseName = "no-hooks" - instAction.cfg.Releases.Create(releaseStub()) - - vals := map[string]interface{}{} - res, err := instAction.Run(buildChart(), vals) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - - is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "hooks should not run with no-hooks") -} - -func TestInstallRelease_FailedHooks(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - instAction.ReleaseName = "failed-hooks" - failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) - failer.WatchUntilReadyError = fmt.Errorf("Failed watch") - instAction.cfg.KubeClient = failer - - vals := map[string]interface{}{} - res, err := instAction.Run(buildChart(), vals) - is.Error(err) - is.Contains(res.Info.Description, "failed post-install") - is.Equal(release.StatusFailed, res.Info.Status) -} - -func TestInstallRelease_ReplaceRelease(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - instAction.Replace = true - - rel := releaseStub() - rel.Info.Status = release.StatusUninstalled - instAction.cfg.Releases.Create(rel) - instAction.ReleaseName = rel.Name - - vals := map[string]interface{}{} - res, err := instAction.Run(buildChart(), vals) - is.NoError(err) - - // This should have been auto-incremented - is.Equal(2, res.Version) - is.Equal(res.Name, rel.Name) - - getres, err := instAction.cfg.Releases.Get(rel.Name, res.Version) - is.NoError(err) - is.Equal(getres.Info.Status, release.StatusDeployed) -} - -func TestInstallRelease_KubeVersion(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - vals := map[string]interface{}{} - _, err := instAction.Run(buildChart(withKube(">=0.0.0")), vals) - is.NoError(err) - - // This should fail for a few hundred years - instAction.ReleaseName = "should-fail" - vals = map[string]interface{}{} - _, err = instAction.Run(buildChart(withKube(">=99.0.0")), vals) - is.Error(err) - is.Contains(err.Error(), "chart requires kubeVersion") -} - -func TestInstallRelease_Wait(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - instAction.ReleaseName = "come-fail-away" - failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) - failer.WaitError = fmt.Errorf("I timed out") - instAction.cfg.KubeClient = failer - instAction.Wait = true - vals := map[string]interface{}{} - - res, err := instAction.Run(buildChart(), vals) - is.Error(err) - is.Contains(res.Info.Description, "I timed out") - is.Equal(res.Info.Status, release.StatusFailed) -} - -func TestInstallRelease_Atomic(t *testing.T) { - is := assert.New(t) - - t.Run("atomic uninstall succeeds", func(t *testing.T) { - instAction := installAction(t) - instAction.ReleaseName = "come-fail-away" - failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) - failer.WaitError = fmt.Errorf("I timed out") - instAction.cfg.KubeClient = failer - instAction.Atomic = true - vals := map[string]interface{}{} - - res, err := instAction.Run(buildChart(), vals) - is.Error(err) - is.Contains(err.Error(), "I timed out") - is.Contains(err.Error(), "atomic") - - // Now make sure it isn't in storage any more - _, err = instAction.cfg.Releases.Get(res.Name, res.Version) - is.Error(err) - is.Equal(err, driver.ErrReleaseNotFound) - }) - - t.Run("atomic uninstall fails", func(t *testing.T) { - instAction := installAction(t) - instAction.ReleaseName = "come-fail-away-with-me" - failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) - failer.WaitError = fmt.Errorf("I timed out") - failer.DeleteError = fmt.Errorf("uninstall fail") - instAction.cfg.KubeClient = failer - instAction.Atomic = true - vals := map[string]interface{}{} - - _, err := instAction.Run(buildChart(), vals) - is.Error(err) - is.Contains(err.Error(), "I timed out") - is.Contains(err.Error(), "uninstall fail") - is.Contains(err.Error(), "an error occurred while uninstalling the release") - }) -} - -func TestNameTemplate(t *testing.T) { - testCases := []nameTemplateTestCase{ - // Just a straight up nop please - { - tpl: "foobar", - expected: "foobar", - expectedErrorStr: "", - }, - // Random numbers at the end for fun & profit - { - tpl: "foobar-{{randNumeric 6}}", - expected: "foobar-[0-9]{6}$", - expectedErrorStr: "", - }, - // Random numbers in the middle for fun & profit - { - tpl: "foobar-{{randNumeric 4}}-baz", - expected: "foobar-[0-9]{4}-baz$", - expectedErrorStr: "", - }, - // No such function - { - tpl: "foobar-{{randInt}}", - expected: "", - expectedErrorStr: "function \"randInt\" not defined", - }, - // Invalid template - { - tpl: "foobar-{{", - expected: "", - expectedErrorStr: "unexpected unclosed action", - }, - } - - for _, tc := range testCases { - - n, err := TemplateName(tc.tpl) - if err != nil { - if tc.expectedErrorStr == "" { - t.Errorf("Was not expecting error, but got: %v", err) - continue - } - re, compErr := regexp.Compile(tc.expectedErrorStr) - if compErr != nil { - t.Errorf("Expected error string failed to compile: %v", compErr) - continue - } - if !re.MatchString(err.Error()) { - t.Errorf("Error didn't match for %s expected %s but got %v", tc.tpl, tc.expectedErrorStr, err) - continue - } - } - if err == nil && tc.expectedErrorStr != "" { - t.Errorf("Was expecting error %s but didn't get an error back", tc.expectedErrorStr) - } - - if tc.expected != "" { - re, err := regexp.Compile(tc.expected) - if err != nil { - t.Errorf("Expected string failed to compile: %v", err) - continue - } - if !re.MatchString(n) { - t.Errorf("Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n) - } - } - } -} - -func TestInstallReleaseOutputDir(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - vals := map[string]interface{}{} - - dir, err := ioutil.TempDir("", "output-dir") - if err != nil { - log.Fatal(err) - } - defer os.RemoveAll(dir) - - instAction.OutputDir = dir - - _, err = instAction.Run(buildChart(withSampleTemplates(), withMultipleManifestTemplate()), vals) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - - _, err = os.Stat(filepath.Join(dir, "hello/templates/goodbye")) - is.NoError(err) - - _, err = os.Stat(filepath.Join(dir, "hello/templates/hello")) - is.NoError(err) - - _, err = os.Stat(filepath.Join(dir, "hello/templates/with-partials")) - is.NoError(err) - - _, err = os.Stat(filepath.Join(dir, "hello/templates/rbac")) - is.NoError(err) - - test.AssertGoldenFile(t, filepath.Join(dir, "hello/templates/rbac"), "rbac.txt") - - _, err = os.Stat(filepath.Join(dir, "hello/templates/empty")) - is.True(os.IsNotExist(err)) -} - -func TestNameAndChart(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - chartName := "./foo" - - name, chrt, err := instAction.NameAndChart([]string{chartName}) - if err != nil { - t.Fatal(err) - } - is.Equal(instAction.ReleaseName, name) - is.Equal(chartName, chrt) - - instAction.GenerateName = true - _, _, err = instAction.NameAndChart([]string{"foo", chartName}) - if err == nil { - t.Fatal("expected an error") - } - is.Equal("cannot set --generate-name and also specify a name", err.Error()) - - instAction.GenerateName = false - instAction.NameTemplate = "{{ . }}" - _, _, err = instAction.NameAndChart([]string{"foo", chartName}) - if err == nil { - t.Fatal("expected an error") - } - is.Equal("cannot set --name-template and also specify a name", err.Error()) - - instAction.NameTemplate = "" - instAction.ReleaseName = "" - _, _, err = instAction.NameAndChart([]string{chartName}) - if err == nil { - t.Fatal("expected an error") - } - is.Equal("must either provide a name or specify --generate-name", err.Error()) - - instAction.NameTemplate = "" - instAction.ReleaseName = "" - _, _, err = instAction.NameAndChart([]string{"foo", chartName, "bar"}) - if err == nil { - t.Fatal("expected an error") - } - is.Equal("expected at most two arguments, unexpected arguments: bar", err.Error()) -} - -func TestNameAndChartGenerateName(t *testing.T) { - is := assert.New(t) - instAction := installAction(t) - - instAction.ReleaseName = "" - instAction.GenerateName = true - - tests := []struct { - Name string - Chart string - ExpectedName string - }{ - { - "local filepath", - "./chart", - fmt.Sprintf("chart-%d", time.Now().Unix()), - }, - { - "dot filepath", - ".", - fmt.Sprintf("chart-%d", time.Now().Unix()), - }, - { - "empty filepath", - "", - fmt.Sprintf("chart-%d", time.Now().Unix()), - }, - { - "packaged chart", - "chart.tgz", - fmt.Sprintf("chart-%d", time.Now().Unix()), - }, - { - "packaged chart with .tar.gz extension", - "chart.tar.gz", - fmt.Sprintf("chart-%d", time.Now().Unix()), - }, - { - "packaged chart with local extension", - "./chart.tgz", - fmt.Sprintf("chart-%d", time.Now().Unix()), - }, - } - - for _, tc := range tests { - tc := tc - t.Run(tc.Name, func(t *testing.T) { - t.Parallel() - - name, chrt, err := instAction.NameAndChart([]string{tc.Chart}) - if err != nil { - t.Fatal(err) - } - - is.Equal(tc.ExpectedName, name) - is.Equal(tc.Chart, chrt) - }) - } -} diff --git a/pkg/action/lint.go b/pkg/action/lint.go deleted file mode 100644 index c216c58..0000000 --- a/pkg/action/lint.go +++ /dev/null @@ -1,116 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/lint" - "helm.sh/helm/v3/pkg/lint/support" -) - -// Lint is the action for checking that the semantics of a chart are well-formed. -// -// It provides the implementation of 'helm lint'. -type Lint struct { - Strict bool - Namespace string -} - -type LintResult struct { - TotalChartsLinted int - Messages []support.Message - Errors []error -} - -// NewLint creates a new Lint object with the given configuration. -func NewLint() *Lint { - return &Lint{} -} - -// Run executes 'helm Lint' against the given chart. -func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult { - lowestTolerance := support.ErrorSev - if l.Strict { - lowestTolerance = support.WarningSev - } - result := &LintResult{} - for _, path := range paths { - linter, err := lintChart(path, vals, l.Namespace, l.Strict) - if err != nil { - result.Errors = append(result.Errors, err) - continue - } - - result.Messages = append(result.Messages, linter.Messages...) - result.TotalChartsLinted++ - for _, msg := range linter.Messages { - if msg.Severity >= lowestTolerance { - result.Errors = append(result.Errors, msg.Err) - } - } - } - return result -} - -func lintChart(path string, vals map[string]interface{}, namespace string, strict bool) (support.Linter, error) { - var chartPath string - linter := support.Linter{} - - if strings.HasSuffix(path, ".tgz") || strings.HasSuffix(path, ".tar.gz") { - tempDir, err := ioutil.TempDir("", "helm-lint") - if err != nil { - return linter, errors.Wrap(err, "unable to create temp dir to extract tarball") - } - defer os.RemoveAll(tempDir) - - file, err := os.Open(path) - if err != nil { - return linter, errors.Wrap(err, "unable to open tarball") - } - defer file.Close() - - if err = chartutil.Expand(tempDir, file); err != nil { - return linter, errors.Wrap(err, "unable to extract tarball") - } - - files, err := ioutil.ReadDir(tempDir) - if err != nil { - return linter, errors.Wrapf(err, "unable to read temporary output directory %s", tempDir) - } - if !files[0].IsDir() { - return linter, errors.Errorf("unexpected file %s in temporary output directory %s", files[0].Name(), tempDir) - } - - chartPath = filepath.Join(tempDir, files[0].Name()) - } else { - chartPath = path - } - - // Guard: Error out if this is not a chart. - if _, err := os.Stat(filepath.Join(chartPath, "Chart.yaml")); err != nil { - return linter, errors.Wrap(err, "unable to check Chart.yaml file in chart") - } - - return lint.All(chartPath, vals, namespace, strict), nil -} diff --git a/pkg/action/lint_test.go b/pkg/action/lint_test.go deleted file mode 100644 index 68d3c4b..0000000 --- a/pkg/action/lint_test.go +++ /dev/null @@ -1,160 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "testing" -) - -var ( - values = make(map[string]interface{}) - namespace = "testNamespace" - strict = false - chart1MultipleChartLint = "../../cmd/helm/testdata/testcharts/multiplecharts-lint-chart-1" - chart2MultipleChartLint = "../../cmd/helm/testdata/testcharts/multiplecharts-lint-chart-2" - corruptedTgzChart = "../../cmd/helm/testdata/testcharts/corrupted-compressed-chart.tgz" - chartWithNoTemplatesDir = "../../cmd/helm/testdata/testcharts/chart-with-no-templates-dir" -) - -func TestLintChart(t *testing.T) { - tests := []struct { - name string - chartPath string - err bool - }{ - { - name: "decompressed-chart", - chartPath: "../../cmd/helm/testdata/testcharts/decompressedchart/", - }, - { - name: "archived-chart-path", - chartPath: "../../cmd/helm/testdata/testcharts/compressedchart-0.1.0.tgz", - }, - { - name: "archived-chart-path-with-hyphens", - chartPath: "../../cmd/helm/testdata/testcharts/compressedchart-with-hyphens-0.1.0.tgz", - }, - { - name: "archived-tar-gz-chart-path", - chartPath: "../../cmd/helm/testdata/testcharts/compressedchart-0.1.0.tar.gz", - }, - { - name: "invalid-archived-chart-path", - chartPath: "../../cmd/helm/testdata/testcharts/invalidcompressedchart0.1.0.tgz", - err: true, - }, - { - name: "chart-missing-manifest", - chartPath: "../../cmd/helm/testdata/testcharts/chart-missing-manifest", - err: true, - }, - { - name: "chart-with-schema", - chartPath: "../../cmd/helm/testdata/testcharts/chart-with-schema", - }, - { - name: "chart-with-schema-negative", - chartPath: "../../cmd/helm/testdata/testcharts/chart-with-schema-negative", - }, - { - name: "pre-release-chart", - chartPath: "../../cmd/helm/testdata/testcharts/pre-release-chart-0.1.0-alpha.tgz", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, err := lintChart(tt.chartPath, map[string]interface{}{}, namespace, strict) - switch { - case err != nil && !tt.err: - t.Errorf("%s", err) - case err == nil && tt.err: - t.Errorf("Expected a chart parsing error") - } - }) - } -} - -func TestNonExistentChart(t *testing.T) { - t.Run("should error out for non existent tgz chart", func(t *testing.T) { - testCharts := []string{"non-existent-chart.tgz"} - expectedError := "unable to open tarball: open non-existent-chart.tgz: no such file or directory" - testLint := NewLint() - - result := testLint.Run(testCharts, values) - if len(result.Errors) != 1 { - t.Error("expected one error, but got", len(result.Errors)) - } - - actual := result.Errors[0].Error() - if actual != expectedError { - t.Errorf("expected '%s', but got '%s'", expectedError, actual) - } - }) - - t.Run("should error out for corrupted tgz chart", func(t *testing.T) { - testCharts := []string{corruptedTgzChart} - expectedEOFError := "unable to extract tarball: EOF" - testLint := NewLint() - - result := testLint.Run(testCharts, values) - if len(result.Errors) != 1 { - t.Error("expected one error, but got", len(result.Errors)) - } - - actual := result.Errors[0].Error() - if actual != expectedEOFError { - t.Errorf("expected '%s', but got '%s'", expectedEOFError, actual) - } - }) -} - -func TestLint_MultipleCharts(t *testing.T) { - testCharts := []string{chart2MultipleChartLint, chart1MultipleChartLint} - testLint := NewLint() - if result := testLint.Run(testCharts, values); len(result.Errors) > 0 { - t.Error(result.Errors) - } -} - -func TestLint_EmptyResultErrors(t *testing.T) { - testCharts := []string{chart2MultipleChartLint} - testLint := NewLint() - if result := testLint.Run(testCharts, values); len(result.Errors) > 0 { - t.Error("Expected no error, got more") - } -} - -func TestLint_ChartWithWarnings(t *testing.T) { - t.Run("should pass when not strict", func(t *testing.T) { - testCharts := []string{chartWithNoTemplatesDir} - testLint := NewLint() - testLint.Strict = false - if result := testLint.Run(testCharts, values); len(result.Errors) > 0 { - t.Error("Expected no error, got more") - } - }) - - t.Run("should fail with errors when strict", func(t *testing.T) { - testCharts := []string{chartWithNoTemplatesDir} - testLint := NewLint() - testLint.Strict = true - if result := testLint.Run(testCharts, values); len(result.Errors) != 1 { - t.Error("expected one error, but got", len(result.Errors)) - } - }) -} diff --git a/pkg/action/list.go b/pkg/action/list.go deleted file mode 100644 index 5be60ac..0000000 --- a/pkg/action/list.go +++ /dev/null @@ -1,274 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "path" - "regexp" - - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/releaseutil" -) - -// ListStates represents zero or more status codes that a list item may have set -// -// Because this is used as a bitmask filter, more than one bit can be flipped -// in the ListStates. -type ListStates uint - -const ( - // ListDeployed filters on status "deployed" - ListDeployed ListStates = 1 << iota - // ListUninstalled filters on status "uninstalled" - ListUninstalled - // ListUninstalling filters on status "uninstalling" (uninstall in progress) - ListUninstalling - // ListPendingInstall filters on status "pending" (deployment in progress) - ListPendingInstall - // ListPendingUpgrade filters on status "pending_upgrade" (upgrade in progress) - ListPendingUpgrade - // ListPendingRollback filters on status "pending_rollback" (rollback in progress) - ListPendingRollback - // ListSuperseded filters on status "superseded" (historical release version that is no longer deployed) - ListSuperseded - // ListFailed filters on status "failed" (release version not deployed because of error) - ListFailed - // ListUnknown filters on an unknown status - ListUnknown -) - -// FromName takes a state name and returns a ListStates representation. -// -// Currently, there are only names for individual flipped bits, so the returned -// ListStates will only match one of the constants. However, it is possible that -// this behavior could change in the future. -func (s ListStates) FromName(str string) ListStates { - switch str { - case "deployed": - return ListDeployed - case "uninstalled": - return ListUninstalled - case "superseded": - return ListSuperseded - case "failed": - return ListFailed - case "uninstalling": - return ListUninstalling - case "pending-install": - return ListPendingInstall - case "pending-upgrade": - return ListPendingUpgrade - case "pending-rollback": - return ListPendingRollback - } - return ListUnknown -} - -// ListAll is a convenience for enabling all list filters -const ListAll = ListDeployed | ListUninstalled | ListUninstalling | ListPendingInstall | ListPendingRollback | ListPendingUpgrade | ListSuperseded | ListFailed - -// Sorter is a top-level sort -type Sorter uint - -const ( - // ByNameDesc sorts by descending lexicographic order - ByNameDesc Sorter = iota + 1 - // ByDateAsc sorts by ascending dates (oldest updated release first) - ByDateAsc - // ByDateDesc sorts by descending dates (latest updated release first) - ByDateDesc -) - -// List is the action for listing releases. -// -// It provides, for example, the implementation of 'helm list'. -type List struct { - cfg *Configuration - - // All ignores the limit/offset - All bool - // AllNamespaces searches across namespaces - AllNamespaces bool - // Sort indicates the sort to use - // - // see pkg/releaseutil for several useful sorters - Sort Sorter - // Overrides the default lexicographic sorting - ByDate bool - SortReverse bool - // StateMask accepts a bitmask of states for items to show. - // The default is ListDeployed - StateMask ListStates - // Limit is the number of items to return per Run() - Limit int - // Offset is the starting index for the Run() call - Offset int - // Filter is a filter that is applied to the results - Filter string - Short bool - Uninstalled bool - Superseded bool - Uninstalling bool - Deployed bool - Failed bool - Pending bool -} - -// NewList constructs a new *List -func NewList(cfg *Configuration) *List { - return &List{ - StateMask: ListDeployed | ListFailed, - cfg: cfg, - } -} - -// Run executes the list command, returning a set of matches. -func (l *List) Run() ([]*release.Release, error) { - if err := l.cfg.KubeClient.IsReachable(); err != nil { - return nil, err - } - - var filter *regexp.Regexp - if l.Filter != "" { - var err error - filter, err = regexp.Compile(l.Filter) - if err != nil { - return nil, err - } - } - - results, err := l.cfg.Releases.List(func(rel *release.Release) bool { - // Skip anything that the mask doesn't cover - currentStatus := l.StateMask.FromName(rel.Info.Status.String()) - if l.StateMask¤tStatus == 0 { - return false - } - - // Skip anything that doesn't match the filter. - if filter != nil && !filter.MatchString(rel.Name) { - return false - } - return true - }) - - if results == nil { - return results, nil - } - - results = filterList(results) - - // Unfortunately, we have to sort before truncating, which can incur substantial overhead - l.sort(results) - - // Guard on offset - if l.Offset >= len(results) { - return []*release.Release{}, nil - } - - // Calculate the limit and offset, and then truncate results if necessary. - limit := len(results) - if l.Limit > 0 && l.Limit < limit { - limit = l.Limit - } - last := l.Offset + limit - if l := len(results); l < last { - last = l - } - results = results[l.Offset:last] - - return results, err -} - -// sort is an in-place sort where order is based on the value of a.Sort -func (l *List) sort(rels []*release.Release) { - if l.SortReverse { - l.Sort = ByNameDesc - } - - if l.ByDate { - l.Sort = ByDateDesc - if l.SortReverse { - l.Sort = ByDateAsc - } - } - - switch l.Sort { - case ByDateDesc: - releaseutil.SortByDate(rels) - case ByDateAsc: - releaseutil.Reverse(rels, releaseutil.SortByDate) - case ByNameDesc: - releaseutil.Reverse(rels, releaseutil.SortByName) - default: - releaseutil.SortByName(rels) - } -} - -// filterList returns a list scrubbed of old releases. -func filterList(releases []*release.Release) []*release.Release { - latestReleases := make(map[string]*release.Release) - - for _, rls := range releases { - name, namespace := rls.Name, rls.Namespace - key := path.Join(namespace, name) - if latestRelease, exists := latestReleases[key]; exists && latestRelease.Version > rls.Version { - continue - } - latestReleases[key] = rls - } - - var list = make([]*release.Release, 0, len(latestReleases)) - for _, rls := range latestReleases { - list = append(list, rls) - } - return list -} - -// setStateMask calculates the state mask based on parameters. -func (l *List) SetStateMask() { - if l.All { - l.StateMask = ListAll - return - } - - state := ListStates(0) - if l.Deployed { - state |= ListDeployed - } - if l.Uninstalled { - state |= ListUninstalled - } - if l.Uninstalling { - state |= ListUninstalling - } - if l.Pending { - state |= ListPendingInstall | ListPendingRollback | ListPendingUpgrade - } - if l.Failed { - state |= ListFailed - } - if l.Superseded { - state |= ListSuperseded - } - - // Apply a default - if state == 0 { - state = ListDeployed | ListFailed - } - - l.StateMask = state -} diff --git a/pkg/action/list_test.go b/pkg/action/list_test.go deleted file mode 100644 index 378a747..0000000 --- a/pkg/action/list_test.go +++ /dev/null @@ -1,272 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/storage" -) - -func TestListStates(t *testing.T) { - for input, expect := range map[string]ListStates{ - "deployed": ListDeployed, - "uninstalled": ListUninstalled, - "uninstalling": ListUninstalling, - "superseded": ListSuperseded, - "failed": ListFailed, - "pending-install": ListPendingInstall, - "pending-rollback": ListPendingRollback, - "pending-upgrade": ListPendingUpgrade, - "unknown": ListUnknown, - "totally made up key": ListUnknown, - } { - if expect != expect.FromName(input) { - t.Errorf("Expected %d for %s", expect, input) - } - // This is a cheap way to verify that ListAll actually allows everything but Unknown - if got := expect.FromName(input); got != ListUnknown && got&ListAll == 0 { - t.Errorf("Expected %s to match the ListAll filter", input) - } - } - - filter := ListDeployed | ListPendingRollback - if status := filter.FromName("deployed"); filter&status == 0 { - t.Errorf("Expected %d to match mask %d", status, filter) - } - if status := filter.FromName("failed"); filter&status != 0 { - t.Errorf("Expected %d to fail to match mask %d", status, filter) - } -} - -func TestList_Empty(t *testing.T) { - lister := NewList(actionConfigFixture(t)) - list, err := lister.Run() - assert.NoError(t, err) - assert.Len(t, list, 0) -} - -func newListFixture(t *testing.T) *List { - return NewList(actionConfigFixture(t)) -} - -func TestList_OneNamespace(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - makeMeSomeReleases(lister.cfg.Releases, t) - list, err := lister.Run() - is.NoError(err) - is.Len(list, 3) -} - -func TestList_AllNamespaces(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - makeMeSomeReleases(lister.cfg.Releases, t) - lister.AllNamespaces = true - lister.SetStateMask() - list, err := lister.Run() - is.NoError(err) - is.Len(list, 3) -} - -func TestList_Sort(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - lister.Sort = ByNameDesc // Other sorts are tested elsewhere - makeMeSomeReleases(lister.cfg.Releases, t) - list, err := lister.Run() - is.NoError(err) - is.Len(list, 3) - is.Equal("two", list[0].Name) - is.Equal("three", list[1].Name) - is.Equal("one", list[2].Name) -} - -func TestList_Limit(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - lister.Limit = 2 - makeMeSomeReleases(lister.cfg.Releases, t) - list, err := lister.Run() - is.NoError(err) - is.Len(list, 2) - // Lex order means one, three, two - is.Equal("one", list[0].Name) - is.Equal("three", list[1].Name) -} - -func TestList_BigLimit(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - lister.Limit = 20 - makeMeSomeReleases(lister.cfg.Releases, t) - list, err := lister.Run() - is.NoError(err) - is.Len(list, 3) - - // Lex order means one, three, two - is.Equal("one", list[0].Name) - is.Equal("three", list[1].Name) - is.Equal("two", list[2].Name) -} - -func TestList_LimitOffset(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - lister.Limit = 2 - lister.Offset = 1 - makeMeSomeReleases(lister.cfg.Releases, t) - list, err := lister.Run() - is.NoError(err) - is.Len(list, 2) - - // Lex order means one, three, two - is.Equal("three", list[0].Name) - is.Equal("two", list[1].Name) -} - -func TestList_LimitOffsetOutOfBounds(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - lister.Limit = 2 - lister.Offset = 3 // Last item is index 2 - makeMeSomeReleases(lister.cfg.Releases, t) - list, err := lister.Run() - is.NoError(err) - is.Len(list, 0) - - lister.Limit = 10 - lister.Offset = 1 - list, err = lister.Run() - is.NoError(err) - is.Len(list, 2) -} - -func TestList_StateMask(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - makeMeSomeReleases(lister.cfg.Releases, t) - one, err := lister.cfg.Releases.Get("one", 1) - is.NoError(err) - one.SetStatus(release.StatusUninstalled, "uninstalled") - err = lister.cfg.Releases.Update(one) - is.NoError(err) - - res, err := lister.Run() - is.NoError(err) - is.Len(res, 2) - is.Equal("three", res[0].Name) - is.Equal("two", res[1].Name) - - lister.StateMask = ListUninstalled - res, err = lister.Run() - is.NoError(err) - is.Len(res, 1) - is.Equal("one", res[0].Name) - - lister.StateMask |= ListDeployed - res, err = lister.Run() - is.NoError(err) - is.Len(res, 3) -} - -func TestList_Filter(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - lister.Filter = "th." - makeMeSomeReleases(lister.cfg.Releases, t) - - res, err := lister.Run() - is.NoError(err) - is.Len(res, 1) - is.Equal("three", res[0].Name) -} - -func TestList_FilterFailsCompile(t *testing.T) { - is := assert.New(t) - lister := newListFixture(t) - lister.Filter = "t[h.{{{" - makeMeSomeReleases(lister.cfg.Releases, t) - - _, err := lister.Run() - is.Error(err) -} - -func makeMeSomeReleases(store *storage.Storage, t *testing.T) { - t.Helper() - one := releaseStub() - one.Name = "one" - one.Namespace = "default" - one.Version = 1 - two := releaseStub() - two.Name = "two" - two.Namespace = "default" - two.Version = 2 - three := releaseStub() - three.Name = "three" - three.Namespace = "default" - three.Version = 3 - - for _, rel := range []*release.Release{one, two, three} { - if err := store.Create(rel); err != nil { - t.Fatal(err) - } - } - - all, err := store.ListReleases() - assert.NoError(t, err) - assert.Len(t, all, 3, "sanity test: three items added") -} - -func TestFilterList(t *testing.T) { - t.Run("should filter old versions of the same release", func(t *testing.T) { - r1 := releaseStub() - r1.Name = "r" - r1.Version = 1 - r2 := releaseStub() - r2.Name = "r" - r2.Version = 2 - another := releaseStub() - another.Name = "another" - another.Version = 1 - - filteredList := filterList([]*release.Release{r1, r2, another}) - expectedFilteredList := []*release.Release{r2, another} - - assert.ElementsMatch(t, expectedFilteredList, filteredList) - }) - - t.Run("should not filter out any version across namespaces", func(t *testing.T) { - r1 := releaseStub() - r1.Name = "r" - r1.Namespace = "default" - r1.Version = 1 - r2 := releaseStub() - r2.Name = "r" - r2.Namespace = "testing" - r2.Version = 2 - - filteredList := filterList([]*release.Release{r1, r2}) - expectedFilteredList := []*release.Release{r1, r2} - - assert.ElementsMatch(t, expectedFilteredList, filteredList) - }) -} diff --git a/pkg/action/package.go b/pkg/action/package.go deleted file mode 100644 index b48fc65..0000000 --- a/pkg/action/package.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "fmt" - "io/ioutil" - "os" - "syscall" - - "github.com/Masterminds/semver/v3" - "github.com/pkg/errors" - "golang.org/x/crypto/ssh/terminal" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/provenance" -) - -// Package is the action for packaging a chart. -// -// It provides the implementation of 'helm package'. -type Package struct { - Sign bool - Key string - Keyring string - Version string - AppVersion string - Destination string - DependencyUpdate bool - - RepositoryConfig string - RepositoryCache string -} - -// NewPackage creates a new Package object with the given configuration. -func NewPackage() *Package { - return &Package{} -} - -// Run executes 'helm package' against the given chart and returns the path to the packaged chart. -func (p *Package) Run(path string, vals map[string]interface{}) (string, error) { - ch, err := loader.LoadDir(path) - if err != nil { - return "", err - } - - // If version is set, modify the version. - if p.Version != "" { - if err := setVersion(ch, p.Version); err != nil { - return "", err - } - } - - if p.AppVersion != "" { - ch.Metadata.AppVersion = p.AppVersion - } - - if reqs := ch.Metadata.Dependencies; reqs != nil { - if err := CheckDependencies(ch, reqs); err != nil { - return "", err - } - } - - var dest string - if p.Destination == "." { - // Save to the current working directory. - dest, err = os.Getwd() - if err != nil { - return "", err - } - } else { - // Otherwise save to set destination - dest = p.Destination - } - - name, err := chartutil.Save(ch, dest) - if err != nil { - return "", errors.Wrap(err, "failed to save") - } - - if p.Sign { - err = p.Clearsign(name) - } - - return name, err -} - -func setVersion(ch *chart.Chart, ver string) error { - // Verify that version is a Version, and error out if it is not. - if _, err := semver.NewVersion(ver); err != nil { - return err - } - - // Set the version field on the chart. - ch.Metadata.Version = ver - return nil -} - -func (p *Package) Clearsign(filename string) error { - // Load keyring - signer, err := provenance.NewFromKeyring(p.Keyring, p.Key) - if err != nil { - return err - } - - if err := signer.DecryptKey(promptUser); err != nil { - return err - } - - sig, err := signer.ClearSign(filename) - if err != nil { - return err - } - - return ioutil.WriteFile(filename+".prov", []byte(sig), 0644) -} - -// promptUser implements provenance.PassphraseFetcher -func promptUser(name string) ([]byte, error) { - fmt.Printf("Password for key %q > ", name) - pw, err := terminal.ReadPassword(int(syscall.Stdin)) - fmt.Println() - return pw, err -} diff --git a/pkg/action/package_test.go b/pkg/action/package_test.go deleted file mode 100644 index 0f71611..0000000 --- a/pkg/action/package_test.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "testing" - - "helm.sh/helm/v3/pkg/chart" -) - -func TestSetVersion(t *testing.T) { - c := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "prow", - Version: "0.0.1", - }, - } - expect := "1.2.3-beta.5" - if err := setVersion(c, expect); err != nil { - t.Fatal(err) - } - - if c.Metadata.Version != expect { - t.Errorf("Expected %q, got %q", expect, c.Metadata.Version) - } - - if err := setVersion(c, "monkeyface"); err == nil { - t.Error("Expected bogus version to return an error.") - } -} diff --git a/pkg/action/pull.go b/pkg/action/pull.go deleted file mode 100644 index 4ff5f5c..0000000 --- a/pkg/action/pull.go +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/downloader" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/repo" -) - -// Pull is the action for checking a given release's information. -// -// It provides the implementation of 'helm pull'. -type Pull struct { - ChartPathOptions - - Settings *cli.EnvSettings // TODO: refactor this out of pkg/action - - Devel bool - Untar bool - VerifyLater bool - UntarDir string - DestDir string -} - -// NewPull creates a new Pull object with the given configuration. -func NewPull() *Pull { - return &Pull{} -} - -// Run executes 'helm pull' against the given release. -func (p *Pull) Run(chartRef string) (string, error) { - var out strings.Builder - - c := downloader.ChartDownloader{ - Out: &out, - Keyring: p.Keyring, - Verify: downloader.VerifyNever, - Getters: getter.All(p.Settings), - Options: []getter.Option{ - getter.WithBasicAuth(p.Username, p.Password), - getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile), - }, - RepositoryConfig: p.Settings.RepositoryConfig, - RepositoryCache: p.Settings.RepositoryCache, - } - - if p.Verify { - c.Verify = downloader.VerifyAlways - } else if p.VerifyLater { - c.Verify = downloader.VerifyLater - } - - // If untar is set, we fetch to a tempdir, then untar and copy after - // verification. - dest := p.DestDir - if p.Untar { - var err error - dest, err = ioutil.TempDir("", "helm-") - if err != nil { - return out.String(), errors.Wrap(err, "failed to untar") - } - defer os.RemoveAll(dest) - } - - if p.RepoURL != "" { - chartURL, err := repo.FindChartInAuthRepoURL(p.RepoURL, p.Username, p.Password, chartRef, p.Version, p.CertFile, p.KeyFile, p.CaFile, getter.All(p.Settings)) - if err != nil { - return out.String(), err - } - chartRef = chartURL - } - - saved, v, err := c.DownloadTo(chartRef, p.Version, dest) - if err != nil { - return out.String(), err - } - - if p.Verify { - fmt.Fprintf(&out, "Verification: %v\n", v) - } - - // After verification, untar the chart into the requested directory. - if p.Untar { - ud := p.UntarDir - if !filepath.IsAbs(ud) { - ud = filepath.Join(p.DestDir, ud) - } - // Let udCheck to check conflict file/dir without replacing ud when untarDir is the current directory(.). - udCheck := ud - if udCheck == "." { - _, udCheck = filepath.Split(chartRef) - } else { - _, chartName := filepath.Split(chartRef) - udCheck = filepath.Join(udCheck, chartName) - } - if _, err := os.Stat(udCheck); err != nil { - if err := os.MkdirAll(udCheck, 0755); err != nil { - return out.String(), errors.Wrap(err, "failed to untar (mkdir)") - } - - } else { - return out.String(), errors.Errorf("failed to untar: a file or directory with the name %s already exists", udCheck) - } - - return out.String(), chartutil.ExpandFile(ud, saved) - } - return out.String(), nil -} diff --git a/pkg/action/registry_login.go b/pkg/action/registry_login.go deleted file mode 100644 index 00f6e26..0000000 --- a/pkg/action/registry_login.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io" -) - -// RegistryLogin performs a registry login operation. -type RegistryLogin struct { - cfg *Configuration -} - -// NewRegistryLogin creates a new RegistryLogin object with the given configuration. -func NewRegistryLogin(cfg *Configuration) *RegistryLogin { - return &RegistryLogin{ - cfg: cfg, - } -} - -// Run executes the registry login operation -func (a *RegistryLogin) Run(out io.Writer, hostname string, username string, password string, insecure bool) error { - return a.cfg.RegistryClient.Login(hostname, username, password, insecure) -} diff --git a/pkg/action/registry_logout.go b/pkg/action/registry_logout.go deleted file mode 100644 index 69add41..0000000 --- a/pkg/action/registry_logout.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io" -) - -// RegistryLogout performs a registry login operation. -type RegistryLogout struct { - cfg *Configuration -} - -// NewRegistryLogout creates a new RegistryLogout object with the given configuration. -func NewRegistryLogout(cfg *Configuration) *RegistryLogout { - return &RegistryLogout{ - cfg: cfg, - } -} - -// Run executes the registry logout operation -func (a *RegistryLogout) Run(out io.Writer, hostname string) error { - return a.cfg.RegistryClient.Logout(hostname) -} diff --git a/pkg/action/release_testing.go b/pkg/action/release_testing.go deleted file mode 100644 index b7a1da7..0000000 --- a/pkg/action/release_testing.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "fmt" - "io" - "time" - - "github.com/pkg/errors" - v1 "k8s.io/api/core/v1" - - "helm.sh/helm/v3/pkg/release" -) - -// ReleaseTesting is the action for testing a release. -// -// It provides the implementation of 'helm test'. -type ReleaseTesting struct { - cfg *Configuration - Timeout time.Duration - // Used for fetching logs from test pods - Namespace string -} - -// NewReleaseTesting creates a new ReleaseTesting object with the given configuration. -func NewReleaseTesting(cfg *Configuration) *ReleaseTesting { - return &ReleaseTesting{ - cfg: cfg, - } -} - -// Run executes 'helm test' against the given release. -func (r *ReleaseTesting) Run(name string) (*release.Release, error) { - if err := r.cfg.KubeClient.IsReachable(); err != nil { - return nil, err - } - - if err := validateReleaseName(name); err != nil { - return nil, errors.Errorf("releaseTest: Release name is invalid: %s", name) - } - - // finds the non-deleted release with the given name - rel, err := r.cfg.Releases.Last(name) - if err != nil { - return rel, err - } - - if err := r.cfg.execHook(rel, release.HookTest, r.Timeout); err != nil { - r.cfg.Releases.Update(rel) - return rel, err - } - - return rel, r.cfg.Releases.Update(rel) -} - -// GetPodLogs will write the logs for all test pods in the given release into -// the given writer. These can be immediately output to the user or captured for -// other uses -func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error { - client, err := r.cfg.KubernetesClientSet() - if err != nil { - return errors.Wrap(err, "unable to get kubernetes client to fetch pod logs") - } - - for _, h := range rel.Hooks { - for _, e := range h.Events { - if e == release.HookTest { - req := client.CoreV1().Pods(r.Namespace).GetLogs(h.Name, &v1.PodLogOptions{}) - logReader, err := req.Stream() - if err != nil { - return errors.Wrapf(err, "unable to get pod logs for %s", h.Name) - } - - fmt.Fprintf(out, "POD LOGS: %s\n", h.Name) - _, err = io.Copy(out, logReader) - fmt.Fprintln(out) - if err != nil { - return errors.Wrapf(err, "unable to write pod logs for %s", h.Name) - } - } - } - } - return nil -} diff --git a/pkg/action/resource_policy.go b/pkg/action/resource_policy.go deleted file mode 100644 index cfabdf7..0000000 --- a/pkg/action/resource_policy.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "strings" - - "helm.sh/helm/v3/pkg/releaseutil" -) - -// resourcePolicyAnno is the annotation name for a resource policy -const resourcePolicyAnno = "helm.sh/resource-policy" - -// keepPolicy is the resource policy type for keep -// -// This resource policy type allows resources to skip being deleted -// during an uninstallRelease action. -const keepPolicy = "keep" - -func filterManifestsToKeep(manifests []releaseutil.Manifest) (keep, remaining []releaseutil.Manifest) { - for _, m := range manifests { - if m.Head.Metadata == nil || m.Head.Metadata.Annotations == nil || len(m.Head.Metadata.Annotations) == 0 { - remaining = append(remaining, m) - continue - } - - resourcePolicyType, ok := m.Head.Metadata.Annotations[resourcePolicyAnno] - if !ok { - remaining = append(remaining, m) - continue - } - - resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType)) - if resourcePolicyType == keepPolicy { - keep = append(keep, m) - } - - } - return keep, remaining -} diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go deleted file mode 100644 index 942c9d8..0000000 --- a/pkg/action/rollback.go +++ /dev/null @@ -1,227 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "bytes" - "fmt" - "strings" - "time" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/release" - helmtime "helm.sh/helm/v3/pkg/time" -) - -// Rollback is the action for rolling back to a given release. -// -// It provides the implementation of 'helm rollback'. -type Rollback struct { - cfg *Configuration - - Version int - Timeout time.Duration - Wait bool - DisableHooks bool - DryRun bool - Recreate bool // will (if true) recreate pods after a rollback. - Force bool // will (if true) force resource upgrade through uninstall/recreate if needed - CleanupOnFail bool -} - -// NewRollback creates a new Rollback object with the given configuration. -func NewRollback(cfg *Configuration) *Rollback { - return &Rollback{ - cfg: cfg, - } -} - -// Run executes 'helm rollback' against the given release. -func (r *Rollback) Run(name string) error { - if err := r.cfg.KubeClient.IsReachable(); err != nil { - return err - } - - r.cfg.Log("preparing rollback of %s", name) - currentRelease, targetRelease, err := r.prepareRollback(name) - if err != nil { - return err - } - - if !r.DryRun { - r.cfg.Log("creating rolled back release for %s", name) - if err := r.cfg.Releases.Create(targetRelease); err != nil { - return err - } - } - - r.cfg.Log("performing rollback of %s", name) - if _, err := r.performRollback(currentRelease, targetRelease); err != nil { - return err - } - - if !r.DryRun { - r.cfg.Log("updating status for rolled back release for %s", name) - if err := r.cfg.Releases.Update(targetRelease); err != nil { - return err - } - } - return nil -} - -// prepareRollback finds the previous release and prepares a new release object with -// the previous release's configuration -func (r *Rollback) prepareRollback(name string) (*release.Release, *release.Release, error) { - if err := validateReleaseName(name); err != nil { - return nil, nil, errors.Errorf("prepareRollback: Release name is invalid: %s", name) - } - - if r.Version < 0 { - return nil, nil, errInvalidRevision - } - - currentRelease, err := r.cfg.Releases.Last(name) - if err != nil { - return nil, nil, err - } - - previousVersion := r.Version - if r.Version == 0 { - previousVersion = currentRelease.Version - 1 - } - - r.cfg.Log("rolling back %s (current: v%d, target: v%d)", name, currentRelease.Version, previousVersion) - - previousRelease, err := r.cfg.Releases.Get(name, previousVersion) - if err != nil { - return nil, nil, err - } - - // Store a new release object with previous release's configuration - targetRelease := &release.Release{ - Name: name, - Namespace: currentRelease.Namespace, - Chart: previousRelease.Chart, - Config: previousRelease.Config, - Info: &release.Info{ - FirstDeployed: currentRelease.Info.FirstDeployed, - LastDeployed: helmtime.Now(), - Status: release.StatusPendingRollback, - Notes: previousRelease.Info.Notes, - // Because we lose the reference to previous version elsewhere, we set the - // message here, and only override it later if we experience failure. - Description: fmt.Sprintf("Rollback to %d", previousVersion), - }, - Version: currentRelease.Version + 1, - Manifest: previousRelease.Manifest, - Hooks: previousRelease.Hooks, - } - - return currentRelease, targetRelease, nil -} - -func (r *Rollback) performRollback(currentRelease, targetRelease *release.Release) (*release.Release, error) { - if r.DryRun { - r.cfg.Log("dry run for %s", targetRelease.Name) - return targetRelease, nil - } - - current, err := r.cfg.KubeClient.Build(bytes.NewBufferString(currentRelease.Manifest), false) - if err != nil { - return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest") - } - target, err := r.cfg.KubeClient.Build(bytes.NewBufferString(targetRelease.Manifest), false) - if err != nil { - return targetRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest") - } - - // pre-rollback hooks - if !r.DisableHooks { - if err := r.cfg.execHook(targetRelease, release.HookPreRollback, r.Timeout); err != nil { - return targetRelease, err - } - } else { - r.cfg.Log("rollback hooks disabled for %s", targetRelease.Name) - } - - results, err := r.cfg.KubeClient.Update(current, target, r.Force) - - if err != nil { - msg := fmt.Sprintf("Rollback %q failed: %s", targetRelease.Name, err) - r.cfg.Log("warning: %s", msg) - currentRelease.Info.Status = release.StatusSuperseded - targetRelease.Info.Status = release.StatusFailed - targetRelease.Info.Description = msg - r.cfg.recordRelease(currentRelease) - r.cfg.recordRelease(targetRelease) - if r.CleanupOnFail { - r.cfg.Log("Cleanup on fail set, cleaning up %d resources", len(results.Created)) - _, errs := r.cfg.KubeClient.Delete(results.Created) - if errs != nil { - var errorList []string - for _, e := range errs { - errorList = append(errorList, e.Error()) - } - return targetRelease, errors.Wrapf(fmt.Errorf("unable to cleanup resources: %s", strings.Join(errorList, ", ")), "an error occurred while cleaning up resources. original rollback error: %s", err) - } - r.cfg.Log("Resource cleanup complete") - } - return targetRelease, err - } - - if r.Recreate { - // NOTE: Because this is not critical for a release to succeed, we just - // log if an error occurs and continue onward. If we ever introduce log - // levels, we should make these error level logs so users are notified - // that they'll need to go do the cleanup on their own - if err := recreate(r.cfg, results.Updated); err != nil { - r.cfg.Log(err.Error()) - } - } - - if r.Wait { - if err := r.cfg.KubeClient.Wait(target, r.Timeout); err != nil { - targetRelease.SetStatus(release.StatusFailed, fmt.Sprintf("Release %q failed: %s", targetRelease.Name, err.Error())) - r.cfg.recordRelease(currentRelease) - r.cfg.recordRelease(targetRelease) - return targetRelease, errors.Wrapf(err, "release %s failed", targetRelease.Name) - } - } - - // post-rollback hooks - if !r.DisableHooks { - if err := r.cfg.execHook(targetRelease, release.HookPostRollback, r.Timeout); err != nil { - return targetRelease, err - } - } - - deployed, err := r.cfg.Releases.DeployedAll(currentRelease.Name) - if err != nil { - return nil, err - } - // Supersede all previous deployments, see issue #2941. - for _, rel := range deployed { - r.cfg.Log("superseding previous deployment %d", rel.Version) - rel.Info.Status = release.StatusSuperseded - r.cfg.recordRelease(rel) - } - - targetRelease.Info.Status = release.StatusDeployed - - return targetRelease, nil -} diff --git a/pkg/action/show.go b/pkg/action/show.go deleted file mode 100644 index b29107d..0000000 --- a/pkg/action/show.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "fmt" - "strings" - - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" -) - -type ShowOutputFormat string - -const ( - ShowAll ShowOutputFormat = "all" - ShowChart ShowOutputFormat = "chart" - ShowValues ShowOutputFormat = "values" - ShowReadme ShowOutputFormat = "readme" -) - -var readmeFileNames = []string{"readme.md", "readme.txt", "readme"} - -func (o ShowOutputFormat) String() string { - return string(o) -} - -// Show is the action for checking a given release's information. -// -// It provides the implementation of 'helm show' and its respective subcommands. -type Show struct { - OutputFormat ShowOutputFormat - ChartPathOptions -} - -// NewShow creates a new Show object with the given configuration. -func NewShow(output ShowOutputFormat) *Show { - return &Show{ - OutputFormat: output, - } -} - -// Run executes 'helm show' against the given release. -func (s *Show) Run(chartpath string) (string, error) { - var out strings.Builder - chrt, err := loader.Load(chartpath) - if err != nil { - return "", err - } - cf, err := yaml.Marshal(chrt.Metadata) - if err != nil { - return "", err - } - - if s.OutputFormat == ShowChart || s.OutputFormat == ShowAll { - fmt.Fprintf(&out, "%s\n", cf) - } - - if (s.OutputFormat == ShowValues || s.OutputFormat == ShowAll) && chrt.Values != nil { - if s.OutputFormat == ShowAll { - fmt.Fprintln(&out, "---") - } - for _, f := range chrt.Raw { - if f.Name == chartutil.ValuesfileName { - fmt.Fprintln(&out, string(f.Data)) - } - } - } - - if s.OutputFormat == ShowReadme || s.OutputFormat == ShowAll { - if s.OutputFormat == ShowAll { - fmt.Fprintln(&out, "---") - } - readme := findReadme(chrt.Files) - if readme == nil { - return out.String(), nil - } - fmt.Fprintf(&out, "%s\n", readme.Data) - } - return out.String(), nil -} - -func findReadme(files []*chart.File) (file *chart.File) { - for _, file := range files { - for _, n := range readmeFileNames { - if strings.EqualFold(file.Name, n) { - return file - } - } - } - return nil -} diff --git a/pkg/action/show_test.go b/pkg/action/show_test.go deleted file mode 100644 index 0a53274..0000000 --- a/pkg/action/show_test.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "io/ioutil" - "strings" - "testing" -) - -func TestShow(t *testing.T) { - client := NewShow(ShowAll) - - output, err := client.Run("../../cmd/helm/testdata/testcharts/alpine") - if err != nil { - t.Fatal(err) - } - - // Load the data from the textfixture directly. - cdata, err := ioutil.ReadFile("../../cmd/helm/testdata/testcharts/alpine/Chart.yaml") - if err != nil { - t.Fatal(err) - } - data, err := ioutil.ReadFile("../../cmd/helm/testdata/testcharts/alpine/values.yaml") - if err != nil { - t.Fatal(err) - } - readmeData, err := ioutil.ReadFile("../../cmd/helm/testdata/testcharts/alpine/README.md") - if err != nil { - t.Fatal(err) - } - parts := strings.SplitN(output, "---", 3) - if len(parts) != 3 { - t.Fatalf("Expected 2 parts, got %d", len(parts)) - } - - expect := []string{ - strings.ReplaceAll(strings.TrimSpace(string(cdata)), "\r", ""), - strings.ReplaceAll(strings.TrimSpace(string(data)), "\r", ""), - strings.ReplaceAll(strings.TrimSpace(string(readmeData)), "\r", ""), - } - - // Problem: ghodss/yaml doesn't marshal into struct order. To solve, we - // have to carefully craft the Chart.yaml to match. - for i, got := range parts { - got = strings.ReplaceAll(strings.TrimSpace(got), "\r", "") - if got != expect[i] { - t.Errorf("Expected\n%q\nGot\n%q\n", expect[i], got) - } - } - - // Regression tests for missing values. See issue #1024. - client.OutputFormat = ShowValues - output, err = client.Run("../../cmd/helm/testdata/testcharts/novals") - if err != nil { - t.Fatal(err) - } - - if len(output) != 0 { - t.Errorf("expected empty values buffer, got %s", output) - } -} diff --git a/pkg/action/status.go b/pkg/action/status.go deleted file mode 100644 index a0c7f6e..0000000 --- a/pkg/action/status.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "helm.sh/helm/v3/pkg/release" -) - -// Status is the action for checking the deployment status of releases. -// -// It provides the implementation of 'helm status'. -type Status struct { - cfg *Configuration - - Version int -} - -// NewStatus creates a new Status object with the given configuration. -func NewStatus(cfg *Configuration) *Status { - return &Status{ - cfg: cfg, - } -} - -// Run executes 'helm status' against the given release. -func (s *Status) Run(name string) (*release.Release, error) { - if err := s.cfg.KubeClient.IsReachable(); err != nil { - return nil, err - } - - return s.cfg.releaseContent(name, s.Version) -} diff --git a/pkg/action/testdata/rbac.txt b/pkg/action/testdata/rbac.txt deleted file mode 100644 index 0cb15b8..0000000 --- a/pkg/action/testdata/rbac.txt +++ /dev/null @@ -1,25 +0,0 @@ ---- -# Source: hello/templates/rbac -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: schedule-agents -rules: -- apiGroups: [""] - resources: ["pods", "pods/exec", "pods/log"] - verbs: ["*"] ---- -# Source: hello/templates/rbac -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: schedule-agents - namespace: spaced -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: schedule-agents -subjects: -- kind: ServiceAccount - name: schedule-agents - namespace: spaced diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go deleted file mode 100644 index dfaa984..0000000 --- a/pkg/action/uninstall.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "strings" - "time" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/releaseutil" - helmtime "helm.sh/helm/v3/pkg/time" -) - -// Uninstall is the action for uninstalling releases. -// -// It provides the implementation of 'helm uninstall'. -type Uninstall struct { - cfg *Configuration - - DisableHooks bool - DryRun bool - KeepHistory bool - Timeout time.Duration - Description string -} - -// NewUninstall creates a new Uninstall object with the given configuration. -func NewUninstall(cfg *Configuration) *Uninstall { - return &Uninstall{ - cfg: cfg, - } -} - -// Run uninstalls the given release. -func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error) { - if err := u.cfg.KubeClient.IsReachable(); err != nil { - return nil, err - } - - if u.DryRun { - // In the dry run case, just see if the release exists - r, err := u.cfg.releaseContent(name, 0) - if err != nil { - return &release.UninstallReleaseResponse{}, err - } - return &release.UninstallReleaseResponse{Release: r}, nil - } - - if err := validateReleaseName(name); err != nil { - return nil, errors.Errorf("uninstall: Release name is invalid: %s", name) - } - - rels, err := u.cfg.Releases.History(name) - if err != nil { - return nil, errors.Wrapf(err, "uninstall: Release not loaded: %s", name) - } - if len(rels) < 1 { - return nil, errMissingRelease - } - - releaseutil.SortByRevision(rels) - rel := rels[len(rels)-1] - - // TODO: Are there any cases where we want to force a delete even if it's - // already marked deleted? - if rel.Info.Status == release.StatusUninstalled { - if !u.KeepHistory { - if err := u.purgeReleases(rels...); err != nil { - return nil, errors.Wrap(err, "uninstall: Failed to purge the release") - } - return &release.UninstallReleaseResponse{Release: rel}, nil - } - return nil, errors.Errorf("the release named %q is already deleted", name) - } - - u.cfg.Log("uninstall: Deleting %s", name) - rel.Info.Status = release.StatusUninstalling - rel.Info.Deleted = helmtime.Now() - rel.Info.Description = "Deletion in progress (or silently failed)" - res := &release.UninstallReleaseResponse{Release: rel} - - if !u.DisableHooks { - if err := u.cfg.execHook(rel, release.HookPreDelete, u.Timeout); err != nil { - return res, err - } - } else { - u.cfg.Log("delete hooks disabled for %s", name) - } - - // From here on out, the release is currently considered to be in StatusUninstalling - // state. - if err := u.cfg.Releases.Update(rel); err != nil { - u.cfg.Log("uninstall: Failed to store updated release: %s", err) - } - - kept, errs := u.deleteRelease(rel) - res.Info = kept - - if !u.DisableHooks { - if err := u.cfg.execHook(rel, release.HookPostDelete, u.Timeout); err != nil { - errs = append(errs, err) - } - } - - rel.Info.Status = release.StatusUninstalled - if len(u.Description) > 0 { - rel.Info.Description = u.Description - } else { - rel.Info.Description = "Uninstallation complete" - } - - if !u.KeepHistory { - u.cfg.Log("purge requested for %s", name) - err := u.purgeReleases(rels...) - if err != nil { - errs = append(errs, errors.Wrap(err, "uninstall: Failed to purge the release")) - } - - // Return the errors that occurred while deleting the release, if any - if len(errs) > 0 { - return res, errors.Errorf("uninstallation completed with %d error(s): %s", len(errs), joinErrors(errs)) - } - - return res, nil - } - - if err := u.cfg.Releases.Update(rel); err != nil { - u.cfg.Log("uninstall: Failed to store updated release: %s", err) - } - - if len(errs) > 0 { - return res, errors.Errorf("uninstallation completed with %d error(s): %s", len(errs), joinErrors(errs)) - } - return res, nil -} - -func (u *Uninstall) purgeReleases(rels ...*release.Release) error { - for _, rel := range rels { - if _, err := u.cfg.Releases.Delete(rel.Name, rel.Version); err != nil { - return err - } - } - return nil -} - -func joinErrors(errs []error) string { - es := make([]string, 0, len(errs)) - for _, e := range errs { - es = append(es, e.Error()) - } - return strings.Join(es, "; ") -} - -// deleteRelease deletes the release and returns manifests that were kept in the deletion process -func (u *Uninstall) deleteRelease(rel *release.Release) (string, []error) { - caps, err := u.cfg.getCapabilities() - if err != nil { - return rel.Manifest, []error{errors.Wrap(err, "could not get apiVersions from Kubernetes")} - } - - manifests := releaseutil.SplitManifests(rel.Manifest) - _, files, err := releaseutil.SortManifests(manifests, caps.APIVersions, releaseutil.UninstallOrder) - if err != nil { - // We could instead just delete everything in no particular order. - // FIXME: One way to delete at this point would be to try a label-based - // deletion. The problem with this is that we could get a false positive - // and delete something that was not legitimately part of this release. - return rel.Manifest, []error{errors.Wrap(err, "corrupted release record. You must manually delete the resources")} - } - - filesToKeep, filesToDelete := filterManifestsToKeep(files) - var kept string - for _, f := range filesToKeep { - kept += f.Name + "\n" - } - - var builder strings.Builder - for _, file := range filesToDelete { - builder.WriteString("\n---\n" + file.Content) - } - resources, err := u.cfg.KubeClient.Build(strings.NewReader(builder.String()), false) - if err != nil { - return "", []error{errors.Wrap(err, "unable to build kubernetes objects for delete")} - } - - _, errs := u.cfg.KubeClient.Delete(resources) - return kept, errs -} diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go deleted file mode 100644 index 1db4184..0000000 --- a/pkg/action/upgrade.go +++ /dev/null @@ -1,428 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "bytes" - "fmt" - "strings" - "time" - - "github.com/pkg/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/cli-runtime/pkg/resource" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/kube" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/releaseutil" -) - -// Upgrade is the action for upgrading releases. -// -// It provides the implementation of 'helm upgrade'. -type Upgrade struct { - cfg *Configuration - - ChartPathOptions - - Install bool - Devel bool - Namespace string - Timeout time.Duration - Wait bool - DisableHooks bool - DryRun bool - Force bool - ResetValues bool - ReuseValues bool - // Recreate will (if true) recreate pods after a rollback. - Recreate bool - // MaxHistory limits the maximum number of revisions saved per release - MaxHistory int - Atomic bool - CleanupOnFail bool - SubNotes bool - Description string -} - -// NewUpgrade creates a new Upgrade object with the given configuration. -func NewUpgrade(cfg *Configuration) *Upgrade { - return &Upgrade{ - cfg: cfg, - } -} - -// Run executes the upgrade on the given release. -func (u *Upgrade) Run(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, error) { - // Make sure if Atomic is set, that wait is set as well. This makes it so - // the user doesn't have to specify both - u.Wait = u.Wait || u.Atomic - - if err := validateReleaseName(name); err != nil { - return nil, errors.Errorf("release name is invalid: %s", name) - } - u.cfg.Log("preparing upgrade for %s", name) - currentRelease, upgradedRelease, err := u.prepareUpgrade(name, chart, vals) - if err != nil { - return nil, err - } - - u.cfg.Releases.MaxHistory = u.MaxHistory - - u.cfg.Log("performing update for %s", name) - res, err := u.performUpgrade(currentRelease, upgradedRelease) - if err != nil { - return res, err - } - - if !u.DryRun { - u.cfg.Log("updating status for upgraded release for %s", name) - if err := u.cfg.Releases.Update(upgradedRelease); err != nil { - return res, err - } - } - - return res, nil -} - -func validateReleaseName(releaseName string) error { - if releaseName == "" { - return errMissingRelease - } - - if !ValidName.MatchString(releaseName) || (len(releaseName) > releaseNameMaxLen) { - return errInvalidName - } - - return nil -} - -// prepareUpgrade builds an upgraded release for an upgrade operation. -func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[string]interface{}) (*release.Release, *release.Release, error) { - if chart == nil { - return nil, nil, errMissingChart - } - - // finds the deployed release with the given name - currentRelease, err := u.cfg.Releases.Deployed(name) - if err != nil { - return nil, nil, err - } - - // determine if values will be reused - vals, err = u.reuseValues(chart, currentRelease, vals) - if err != nil { - return nil, nil, err - } - - if err := chartutil.ProcessDependencies(chart, vals); err != nil { - return nil, nil, err - } - - // finds the non-deleted release with the given name - lastRelease, err := u.cfg.Releases.Last(name) - if err != nil { - return nil, nil, err - } - - // Increment revision count. This is passed to templates, and also stored on - // the release object. - revision := lastRelease.Version + 1 - - options := chartutil.ReleaseOptions{ - Name: name, - Namespace: currentRelease.Namespace, - Revision: revision, - IsUpgrade: true, - } - - caps, err := u.cfg.getCapabilities() - if err != nil { - return nil, nil, err - } - valuesToRender, err := chartutil.ToRenderValues(chart, vals, options, caps) - if err != nil { - return nil, nil, err - } - - hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", u.SubNotes) - if err != nil { - return nil, nil, err - } - - // Store an upgraded release. - upgradedRelease := &release.Release{ - Name: name, - Namespace: currentRelease.Namespace, - Chart: chart, - Config: vals, - Info: &release.Info{ - FirstDeployed: currentRelease.Info.FirstDeployed, - LastDeployed: Timestamper(), - Status: release.StatusPendingUpgrade, - Description: "Preparing upgrade", // This should be overwritten later. - }, - Version: revision, - Manifest: manifestDoc.String(), - Hooks: hooks, - } - - if len(notesTxt) > 0 { - upgradedRelease.Info.Notes = notesTxt - } - err = validateManifest(u.cfg.KubeClient, manifestDoc.Bytes()) - return currentRelease, upgradedRelease, err -} - -func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Release) (*release.Release, error) { - current, err := u.cfg.KubeClient.Build(bytes.NewBufferString(originalRelease.Manifest), false) - if err != nil { - return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from current release manifest") - } - target, err := u.cfg.KubeClient.Build(bytes.NewBufferString(upgradedRelease.Manifest), true) - if err != nil { - return upgradedRelease, errors.Wrap(err, "unable to build kubernetes objects from new release manifest") - } - - // Do a basic diff using gvk + name to figure out what new resources are being created so we can validate they don't already exist - existingResources := make(map[string]bool) - for _, r := range current { - existingResources[objectKey(r)] = true - } - - var toBeCreated kube.ResourceList - for _, r := range target { - if !existingResources[objectKey(r)] { - toBeCreated = append(toBeCreated, r) - } - } - - if err := existingResourceConflict(toBeCreated); err != nil { - return nil, errors.Wrap(err, "rendered manifests contain a new resource that already exists. Unable to continue with update") - } - - if u.DryRun { - u.cfg.Log("dry run for %s", upgradedRelease.Name) - if len(u.Description) > 0 { - upgradedRelease.Info.Description = u.Description - } else { - upgradedRelease.Info.Description = "Dry run complete" - } - return upgradedRelease, nil - } - - u.cfg.Log("creating upgraded release for %s", upgradedRelease.Name) - if err := u.cfg.Releases.Create(upgradedRelease); err != nil { - return nil, err - } - - // pre-upgrade hooks - if !u.DisableHooks { - if err := u.cfg.execHook(upgradedRelease, release.HookPreUpgrade, u.Timeout); err != nil { - return u.failRelease(upgradedRelease, kube.ResourceList{}, fmt.Errorf("pre-upgrade hooks failed: %s", err)) - } - } else { - u.cfg.Log("upgrade hooks disabled for %s", upgradedRelease.Name) - } - - results, err := u.cfg.KubeClient.Update(current, target, u.Force) - if err != nil { - u.cfg.recordRelease(originalRelease) - return u.failRelease(upgradedRelease, results.Created, err) - } - - if u.Recreate { - // NOTE: Because this is not critical for a release to succeed, we just - // log if an error occurs and continue onward. If we ever introduce log - // levels, we should make these error level logs so users are notified - // that they'll need to go do the cleanup on their own - if err := recreate(u.cfg, results.Updated); err != nil { - u.cfg.Log(err.Error()) - } - } - - if u.Wait { - if err := u.cfg.KubeClient.Wait(target, u.Timeout); err != nil { - u.cfg.recordRelease(originalRelease) - return u.failRelease(upgradedRelease, results.Created, err) - } - } - - // post-upgrade hooks - if !u.DisableHooks { - if err := u.cfg.execHook(upgradedRelease, release.HookPostUpgrade, u.Timeout); err != nil { - return u.failRelease(upgradedRelease, results.Created, fmt.Errorf("post-upgrade hooks failed: %s", err)) - } - } - - originalRelease.Info.Status = release.StatusSuperseded - u.cfg.recordRelease(originalRelease) - - upgradedRelease.Info.Status = release.StatusDeployed - if len(u.Description) > 0 { - upgradedRelease.Info.Description = u.Description - } else { - upgradedRelease.Info.Description = "Upgrade complete" - } - - return upgradedRelease, nil -} - -func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, err error) (*release.Release, error) { - msg := fmt.Sprintf("Upgrade %q failed: %s", rel.Name, err) - u.cfg.Log("warning: %s", msg) - - rel.Info.Status = release.StatusFailed - rel.Info.Description = msg - u.cfg.recordRelease(rel) - if u.CleanupOnFail && len(created) > 0 { - u.cfg.Log("Cleanup on fail set, cleaning up %d resources", len(created)) - _, errs := u.cfg.KubeClient.Delete(created) - if errs != nil { - var errorList []string - for _, e := range errs { - errorList = append(errorList, e.Error()) - } - return rel, errors.Wrapf(fmt.Errorf("unable to cleanup resources: %s", strings.Join(errorList, ", ")), "an error occurred while cleaning up resources. original upgrade error: %s", err) - } - u.cfg.Log("Resource cleanup complete") - } - if u.Atomic { - u.cfg.Log("Upgrade failed and atomic is set, rolling back to last successful release") - - // As a protection, get the last successful release before rollback. - // If there are no successful releases, bail out - hist := NewHistory(u.cfg) - fullHistory, herr := hist.Run(rel.Name) - if herr != nil { - return rel, errors.Wrapf(herr, "an error occurred while finding last successful release. original upgrade error: %s", err) - } - - // There isn't a way to tell if a previous release was successful, but - // generally failed releases do not get superseded unless the next - // release is successful, so this should be relatively safe - filteredHistory := releaseutil.FilterFunc(func(r *release.Release) bool { - return r.Info.Status == release.StatusSuperseded || r.Info.Status == release.StatusDeployed - }).Filter(fullHistory) - if len(filteredHistory) == 0 { - return rel, errors.Wrap(err, "unable to find a previously successful release when attempting to rollback. original upgrade error") - } - - releaseutil.Reverse(filteredHistory, releaseutil.SortByRevision) - - rollin := NewRollback(u.cfg) - rollin.Version = filteredHistory[0].Version - rollin.Wait = true - rollin.DisableHooks = u.DisableHooks - rollin.Recreate = u.Recreate - rollin.Force = u.Force - rollin.Timeout = u.Timeout - if rollErr := rollin.Run(rel.Name); rollErr != nil { - return rel, errors.Wrapf(rollErr, "an error occurred while rolling back the release. original upgrade error: %s", err) - } - return rel, errors.Wrapf(err, "release %s failed, and has been rolled back due to atomic being set", rel.Name) - } - - return rel, err -} - -// reuseValues copies values from the current release to a new release if the -// new release does not have any values. -// -// If the request already has values, or if there are no values in the current -// release, this does nothing. -// -// This is skipped if the u.ResetValues flag is set, in which case the -// request values are not altered. -func (u *Upgrade) reuseValues(chart *chart.Chart, current *release.Release, newVals map[string]interface{}) (map[string]interface{}, error) { - if u.ResetValues { - // If ResetValues is set, we completely ignore current.Config. - u.cfg.Log("resetting values to the chart's original version") - return newVals, nil - } - - // If the ReuseValues flag is set, we always copy the old values over the new config's values. - if u.ReuseValues { - u.cfg.Log("reusing the old release's values") - - // We have to regenerate the old coalesced values: - oldVals, err := chartutil.CoalesceValues(current.Chart, current.Config) - if err != nil { - return nil, errors.Wrap(err, "failed to rebuild old values") - } - - newVals = chartutil.CoalesceTables(newVals, current.Config) - - chart.Values = oldVals - - return newVals, nil - } - - if len(newVals) == 0 && len(current.Config) > 0 { - u.cfg.Log("copying values from %s (v%d) to new release.", current.Name, current.Version) - newVals = current.Config - } - return newVals, nil -} - -func validateManifest(c kube.Interface, manifest []byte) error { - _, err := c.Build(bytes.NewReader(manifest), true) - return err -} - -// recreate captures all the logic for recreating pods for both upgrade and -// rollback. If we end up refactoring rollback to use upgrade, this can just be -// made an unexported method on the upgrade action. -func recreate(cfg *Configuration, resources kube.ResourceList) error { - for _, res := range resources { - versioned := kube.AsVersioned(res) - selector, err := kube.SelectorsForObject(versioned) - if err != nil { - // If no selector is returned, it means this object is - // definitely not a pod, so continue onward - continue - } - - client, err := cfg.KubernetesClientSet() - if err != nil { - return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name) - } - - pods, err := client.CoreV1().Pods(res.Namespace).List(metav1.ListOptions{ - LabelSelector: selector.String(), - }) - if err != nil { - return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name) - } - - // Restart pods - for _, pod := range pods.Items { - // Delete each pod for get them restarted with changed spec. - if err := client.CoreV1().Pods(pod.Namespace).Delete(pod.Name, metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil { - return errors.Wrapf(err, "unable to recreate pods for object %s/%s because an error occurred", res.Namespace, res.Name) - } - } - } - return nil -} - -func objectKey(r *resource.Info) string { - gvk := r.Object.GetObjectKind().GroupVersionKind() - return fmt.Sprintf("%s/%s/%s/%s", gvk.GroupVersion().String(), gvk.Kind, r.Namespace, r.Name) -} diff --git a/pkg/action/upgrade_test.go b/pkg/action/upgrade_test.go deleted file mode 100644 index f25d115..0000000 --- a/pkg/action/upgrade_test.go +++ /dev/null @@ -1,255 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "fmt" - "testing" - - "helm.sh/helm/v3/pkg/chart" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - kubefake "helm.sh/helm/v3/pkg/kube/fake" - "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/time" -) - -func upgradeAction(t *testing.T) *Upgrade { - config := actionConfigFixture(t) - upAction := NewUpgrade(config) - upAction.Namespace = "spaced" - - return upAction -} - -func TestUpgradeRelease_Wait(t *testing.T) { - is := assert.New(t) - req := require.New(t) - - upAction := upgradeAction(t) - rel := releaseStub() - rel.Name = "come-fail-away" - rel.Info.Status = release.StatusDeployed - upAction.cfg.Releases.Create(rel) - - failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient) - failer.WaitError = fmt.Errorf("I timed out") - upAction.cfg.KubeClient = failer - upAction.Wait = true - vals := map[string]interface{}{} - - res, err := upAction.Run(rel.Name, buildChart(), vals) - req.Error(err) - is.Contains(res.Info.Description, "I timed out") - is.Equal(res.Info.Status, release.StatusFailed) -} - -func TestUpgradeRelease_CleanupOnFail(t *testing.T) { - is := assert.New(t) - req := require.New(t) - - upAction := upgradeAction(t) - rel := releaseStub() - rel.Name = "come-fail-away" - rel.Info.Status = release.StatusDeployed - upAction.cfg.Releases.Create(rel) - - failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient) - failer.WaitError = fmt.Errorf("I timed out") - failer.DeleteError = fmt.Errorf("I tried to delete nil") - upAction.cfg.KubeClient = failer - upAction.Wait = true - upAction.CleanupOnFail = true - vals := map[string]interface{}{} - - res, err := upAction.Run(rel.Name, buildChart(), vals) - req.Error(err) - is.NotContains(err.Error(), "unable to cleanup resources") - is.Contains(res.Info.Description, "I timed out") - is.Equal(res.Info.Status, release.StatusFailed) -} - -func TestUpgradeRelease_Atomic(t *testing.T) { - is := assert.New(t) - req := require.New(t) - - t.Run("atomic rollback succeeds", func(t *testing.T) { - upAction := upgradeAction(t) - - rel := releaseStub() - rel.Name = "nuketown" - rel.Info.Status = release.StatusDeployed - upAction.cfg.Releases.Create(rel) - - failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient) - // We can't make Update error because then the rollback won't work - failer.WatchUntilReadyError = fmt.Errorf("arming key removed") - upAction.cfg.KubeClient = failer - upAction.Atomic = true - vals := map[string]interface{}{} - - res, err := upAction.Run(rel.Name, buildChart(), vals) - req.Error(err) - is.Contains(err.Error(), "arming key removed") - is.Contains(err.Error(), "atomic") - - // Now make sure it is actually upgraded - updatedRes, err := upAction.cfg.Releases.Get(res.Name, 3) - is.NoError(err) - // Should have rolled back to the previous - is.Equal(updatedRes.Info.Status, release.StatusDeployed) - }) - - t.Run("atomic uninstall fails", func(t *testing.T) { - upAction := upgradeAction(t) - rel := releaseStub() - rel.Name = "fallout" - rel.Info.Status = release.StatusDeployed - upAction.cfg.Releases.Create(rel) - - failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient) - failer.UpdateError = fmt.Errorf("update fail") - upAction.cfg.KubeClient = failer - upAction.Atomic = true - vals := map[string]interface{}{} - - _, err := upAction.Run(rel.Name, buildChart(), vals) - req.Error(err) - is.Contains(err.Error(), "update fail") - is.Contains(err.Error(), "an error occurred while rolling back the release") - }) -} - -func TestUpgradeRelease_ReuseValues(t *testing.T) { - is := assert.New(t) - - t.Run("reuse values should work with values", func(t *testing.T) { - upAction := upgradeAction(t) - - existingValues := map[string]interface{}{ - "name": "value", - "maxHeapSize": "128m", - "replicas": 2, - } - newValues := map[string]interface{}{ - "name": "newValue", - "maxHeapSize": "512m", - "cpu": "12m", - } - expectedValues := map[string]interface{}{ - "name": "newValue", - "maxHeapSize": "512m", - "cpu": "12m", - "replicas": 2, - } - - rel := releaseStub() - rel.Name = "nuketown" - rel.Info.Status = release.StatusDeployed - rel.Config = existingValues - - err := upAction.cfg.Releases.Create(rel) - is.NoError(err) - - upAction.ReuseValues = true - // setting newValues and upgrading - res, err := upAction.Run(rel.Name, buildChart(), newValues) - is.NoError(err) - - // Now make sure it is actually upgraded - updatedRes, err := upAction.cfg.Releases.Get(res.Name, 2) - is.NoError(err) - - if updatedRes == nil { - is.Fail("Updated Release is nil") - return - } - is.Equal(release.StatusDeployed, updatedRes.Info.Status) - is.Equal(expectedValues, updatedRes.Config) - }) - - t.Run("reuse values should not install disabled charts", func(t *testing.T) { - upAction := upgradeAction(t) - chartDefaultValues := map[string]interface{}{ - "subchart": map[string]interface{}{ - "enabled": true, - }, - } - dependency := chart.Dependency{ - Name: "subchart", - Version: "0.1.0", - Repository: "http://some-repo.com", - Condition: "subchart.enabled", - } - sampleChart := buildChart( - withName("sample"), - withValues(chartDefaultValues), - withMetadataDependency(dependency), - ) - now := time.Now() - existingValues := map[string]interface{}{ - "subchart": map[string]interface{}{ - "enabled": false, - }, - } - rel := &release.Release{ - Name: "nuketown", - Info: &release.Info{ - FirstDeployed: now, - LastDeployed: now, - Status: release.StatusDeployed, - Description: "Named Release Stub", - }, - Chart: sampleChart, - Config: existingValues, - Version: 1, - } - err := upAction.cfg.Releases.Create(rel) - is.NoError(err) - - upAction.ReuseValues = true - sampleChartWithSubChart := buildChart( - withName(sampleChart.Name()), - withValues(sampleChart.Values), - withDependency(withName("subchart")), - withMetadataDependency(dependency), - ) - // reusing values and upgrading - res, err := upAction.Run(rel.Name, sampleChartWithSubChart, map[string]interface{}{}) - is.NoError(err) - - // Now get the upgraded release - updatedRes, err := upAction.cfg.Releases.Get(res.Name, 2) - is.NoError(err) - - if updatedRes == nil { - is.Fail("Updated Release is nil") - return - } - is.Equal(release.StatusDeployed, updatedRes.Info.Status) - is.Equal(0, len(updatedRes.Chart.Dependencies()), "expected 0 dependencies") - - expectedValues := map[string]interface{}{ - "subchart": map[string]interface{}{ - "enabled": false, - }, - } - is.Equal(expectedValues, updatedRes.Config) - }) -} diff --git a/pkg/action/validate.go b/pkg/action/validate.go deleted file mode 100644 index 6bbfc5e..0000000 --- a/pkg/action/validate.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "fmt" - - "github.com/pkg/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/cli-runtime/pkg/resource" - - "helm.sh/helm/v3/pkg/kube" -) - -func existingResourceConflict(resources kube.ResourceList) error { - err := resources.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - - helper := resource.NewHelper(info.Client, info.Mapping) - existing, err := helper.Get(info.Namespace, info.Name, info.Export) - if err != nil { - if apierrors.IsNotFound(err) { - return nil - } - - return errors.Wrap(err, "could not get information about the resource") - } - - return fmt.Errorf("existing resource conflict: namespace: %s, name: %s, existing_kind: %s, new_kind: %s", info.Namespace, info.Name, existing.GetObjectKind().GroupVersionKind(), info.Mapping.GroupVersionKind) - }) - return err -} diff --git a/pkg/action/verify.go b/pkg/action/verify.go deleted file mode 100644 index c66b14b..0000000 --- a/pkg/action/verify.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "helm.sh/helm/v3/pkg/downloader" -) - -// Verify is the action for building a given chart's Verify tree. -// -// It provides the implementation of 'helm verify'. -type Verify struct { - Keyring string -} - -// NewVerify creates a new Verify object with the given configuration. -func NewVerify() *Verify { - return &Verify{} -} - -// Run executes 'helm verify'. -func (v *Verify) Run(chartfile string) error { - _, err := downloader.VerifyChart(chartfile, v.Keyring) - return err -} diff --git a/pkg/api/install.go b/pkg/api/install.go index a5e82bb..9165922 100644 --- a/pkg/api/install.go +++ b/pkg/api/install.go @@ -4,7 +4,7 @@ import ( "encoding/json" "net/http" - "helm.sh/helm/v3/pkg/api/logger" + "albatross/pkg/api/logger" ) type InstallRequest struct { diff --git a/pkg/api/install_api_test.go b/pkg/api/install_api_test.go index 0ed15cf..fbddb24 100644 --- a/pkg/api/install_api_test.go +++ b/pkg/api/install_api_test.go @@ -14,8 +14,10 @@ import ( "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "helm.sh/helm/v3/pkg/api" - "helm.sh/helm/v3/pkg/api/logger" + "albatross/pkg/api/logger" + + "albatross/pkg/api" + "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/release" ) diff --git a/pkg/api/list.go b/pkg/api/list.go index 6af3cd7..8123c75 100644 --- a/pkg/api/list.go +++ b/pkg/api/list.go @@ -5,7 +5,8 @@ import ( "io" "net/http" - "helm.sh/helm/v3/pkg/api/logger" + "albatross/pkg/api/logger" + "helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/time" ) diff --git a/pkg/api/list_api_test.go b/pkg/api/list_api_test.go index e447bfa..acc5dfa 100644 --- a/pkg/api/list_api_test.go +++ b/pkg/api/list_api_test.go @@ -16,9 +16,11 @@ import ( "github.com/stretchr/testify/suite" "gotest.tools/assert" + "albatross/pkg/api/logger" + + "albatross/pkg/api" + "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/api" - "helm.sh/helm/v3/pkg/api/logger" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/release" ) diff --git a/pkg/api/ping.go b/pkg/api/ping.go index 532acf1..9833c57 100644 --- a/pkg/api/ping.go +++ b/pkg/api/ping.go @@ -4,7 +4,7 @@ import ( "encoding/json" "net/http" - "helm.sh/helm/v3/pkg/api/logger" + "albatross/pkg/api/logger" ) type PingResponse struct { diff --git a/pkg/api/ping_api_test.go b/pkg/api/ping_api_test.go index a310aa1..3314d3d 100644 --- a/pkg/api/ping_api_test.go +++ b/pkg/api/ping_api_test.go @@ -8,10 +8,11 @@ import ( "strings" "testing" + "albatross/pkg/api" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "helm.sh/helm/v3/pkg/api" ) type PingTestSuite struct { diff --git a/pkg/api/service.go b/pkg/api/service.go index 1a15d46..c2e585a 100644 --- a/pkg/api/service.go +++ b/pkg/api/service.go @@ -8,7 +8,8 @@ import ( "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/api/logger" + "albatross/pkg/api/logger" + "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/cli" diff --git a/pkg/api/service_test.go b/pkg/api/service_test.go index 2f01631..16928db 100644 --- a/pkg/api/service_test.go +++ b/pkg/api/service_test.go @@ -7,15 +7,17 @@ import ( "helm.sh/helm/v3/pkg/time" + "albatross/pkg/api" + "helm.sh/helm/v3/pkg/action" - "helm.sh/helm/v3/pkg/api" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "helm.sh/helm/v3/pkg/api/logger" + "albatross/pkg/api/logger" + "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/release" diff --git a/pkg/api/upgrade.go b/pkg/api/upgrade.go index f13c45f..5859423 100644 --- a/pkg/api/upgrade.go +++ b/pkg/api/upgrade.go @@ -5,7 +5,7 @@ import ( "io" "net/http" - "helm.sh/helm/v3/pkg/api/logger" + "albatross/pkg/api/logger" ) type UpgradeRequest struct { diff --git a/pkg/api/upgrade_api_test.go b/pkg/api/upgrade_api_test.go index d6ed66f..d4ca814 100644 --- a/pkg/api/upgrade_api_test.go +++ b/pkg/api/upgrade_api_test.go @@ -14,8 +14,9 @@ import ( "github.com/stretchr/testify/suite" "gotest.tools/assert" - "helm.sh/helm/v3/pkg/api" - "helm.sh/helm/v3/pkg/api/logger" + "albatross/pkg/api" + "albatross/pkg/api/logger" + "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/release" ) diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go deleted file mode 100644 index 5eb4d4d..0000000 --- a/pkg/chart/chart.go +++ /dev/null @@ -1,134 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import "strings" - -// APIVersionV1 is the API version number for version 1. -const APIVersionV1 = "v1" - -// APIVersionV2 is the API version number for version 2. -const APIVersionV2 = "v2" - -// Chart is a helm package that contains metadata, a default config, zero or more -// optionally parameterizable templates, and zero or more charts (dependencies). -type Chart struct { - // Raw contains the raw contents of the files originally contained in the chart archive. - // - // This should not be used except in special cases like `helm show values`, - // where we want to display the raw values, comments and all. - Raw []*File `json:"-"` - // Metadata is the contents of the Chartfile. - Metadata *Metadata `json:"metadata"` - // Lock is the contents of Chart.lock. - Lock *Lock `json:"lock"` - // Templates for this chart. - Templates []*File `json:"templates"` - // Values are default config for this chart. - Values map[string]interface{} `json:"values"` - // Schema is an optional JSON schema for imposing structure on Values - Schema []byte `json:"schema"` - // Files are miscellaneous files in a chart archive, - // e.g. README, LICENSE, etc. - Files []*File `json:"files"` - - parent *Chart - dependencies []*Chart -} - -// SetDependencies replaces the chart dependencies. -func (ch *Chart) SetDependencies(charts ...*Chart) { - ch.dependencies = nil - ch.AddDependency(charts...) -} - -// Name returns the name of the chart. -func (ch *Chart) Name() string { - if ch.Metadata == nil { - return "" - } - return ch.Metadata.Name -} - -// AddDependency determines if the chart is a subchart. -func (ch *Chart) AddDependency(charts ...*Chart) { - for i, x := range charts { - charts[i].parent = ch - ch.dependencies = append(ch.dependencies, x) - } -} - -// Root finds the root chart. -func (ch *Chart) Root() *Chart { - if ch.IsRoot() { - return ch - } - return ch.Parent().Root() -} - -// Dependencies are the charts that this chart depends on. -func (ch *Chart) Dependencies() []*Chart { return ch.dependencies } - -// IsRoot determines if the chart is the root chart. -func (ch *Chart) IsRoot() bool { return ch.parent == nil } - -// Parent returns a subchart's parent chart. -func (ch *Chart) Parent() *Chart { return ch.parent } - -// ChartPath returns the full path to this chart in dot notation. -func (ch *Chart) ChartPath() string { - if !ch.IsRoot() { - return ch.Parent().ChartPath() + "." + ch.Name() - } - return ch.Name() -} - -// ChartFullPath returns the full path to this chart. -func (ch *Chart) ChartFullPath() string { - if !ch.IsRoot() { - return ch.Parent().ChartFullPath() + "/charts/" + ch.Name() - } - return ch.Name() -} - -// Validate validates the metadata. -func (ch *Chart) Validate() error { - return ch.Metadata.Validate() -} - -// AppVersion returns the appversion of the chart. -func (ch *Chart) AppVersion() string { - if ch.Metadata == nil { - return "" - } - return ch.Metadata.AppVersion -} - -// CRDs returns a list of File objects in the 'crds/' directory of a Helm chart. -func (ch *Chart) CRDs() []*File { - files := []*File{} - // Find all resources in the crds/ directory - for _, f := range ch.Files { - if strings.HasPrefix(f.Name, "crds/") { - files = append(files, f) - } - } - // Get CRDs from dependencies, too. - for _, dep := range ch.Dependencies() { - files = append(files, dep.CRDs()...) - } - return files -} diff --git a/pkg/chart/chart_test.go b/pkg/chart/chart_test.go deleted file mode 100644 index 724e529..0000000 --- a/pkg/chart/chart_test.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package chart - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestCRDs(t *testing.T) { - chrt := Chart{ - Files: []*File{ - { - Name: "crds/foo.yaml", - Data: []byte("hello"), - }, - { - Name: "bar.yaml", - Data: []byte("hello"), - }, - { - Name: "crds/foo/bar/baz.yaml", - Data: []byte("hello"), - }, - { - Name: "crdsfoo/bar/baz.yaml", - Data: []byte("hello"), - }, - }, - } - - is := assert.New(t) - crds := chrt.CRDs() - is.Equal(2, len(crds)) - is.Equal("crds/foo.yaml", crds[0].Name) - is.Equal("crds/foo/bar/baz.yaml", crds[1].Name) -} - -func TestSaveChartNoRawData(t *testing.T) { - chrt := Chart{ - Raw: []*File{ - { - Name: "fhqwhgads.yaml", - Data: []byte("Everybody to the Limit"), - }, - }, - } - - is := assert.New(t) - data, err := json.Marshal(chrt) - if err != nil { - t.Fatal(err) - } - - res := &Chart{} - if err := json.Unmarshal(data, res); err != nil { - t.Fatal(err) - } - - is.Equal([]*File(nil), res.Raw) -} diff --git a/pkg/chart/dependency.go b/pkg/chart/dependency.go deleted file mode 100644 index 9ec4544..0000000 --- a/pkg/chart/dependency.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import "time" - -// Dependency describes a chart upon which another chart depends. -// -// Dependencies can be used to express developer intent, or to capture the state -// of a chart. -type Dependency struct { - // Name is the name of the dependency. - // - // This must mach the name in the dependency's Chart.yaml. - Name string `json:"name"` - // Version is the version (range) of this chart. - // - // A lock file will always produce a single version, while a dependency - // may contain a semantic version range. - Version string `json:"version,omitempty"` - // The URL to the repository. - // - // Appending `index.yaml` to this string should result in a URL that can be - // used to fetch the repository index. - Repository string `json:"repository"` - // A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled ) - Condition string `json:"condition,omitempty"` - // Tags can be used to group charts for enabling/disabling together - Tags []string `json:"tags,omitempty"` - // Enabled bool determines if chart should be loaded - Enabled bool `json:"enabled,omitempty"` - // ImportValues holds the mapping of source values to parent key to be imported. Each item can be a - // string or pair of child/parent sublist items. - ImportValues []interface{} `json:"import-values,omitempty"` - // Alias usable alias to be used for the chart - Alias string `json:"alias,omitempty"` -} - -// Lock is a lock file for dependencies. -// -// It represents the state that the dependencies should be in. -type Lock struct { - // Generated is the date the lock file was last generated. - Generated time.Time `json:"generated"` - // Digest is a hash of the dependencies in Chart.yaml. - Digest string `json:"digest"` - // Dependencies is the list of dependencies that this lock file has locked. - Dependencies []*Dependency `json:"dependencies"` -} diff --git a/pkg/chart/errors.go b/pkg/chart/errors.go deleted file mode 100644 index 4cb4189..0000000 --- a/pkg/chart/errors.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -// ValidationError represents a data validation error. -type ValidationError string - -func (v ValidationError) Error() string { - return "validation: " + string(v) -} diff --git a/pkg/chart/file.go b/pkg/chart/file.go deleted file mode 100644 index 9dd7c08..0000000 --- a/pkg/chart/file.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -// File represents a file as a name/value pair. -// -// By convention, name is a relative path within the scope of the chart's -// base directory. -type File struct { - // Name is the path-like name of the template. - Name string `json:"name"` - // Data is the template as byte data. - Data []byte `json:"data"` -} diff --git a/pkg/chart/loader/archive.go b/pkg/chart/loader/archive.go deleted file mode 100644 index 7e187a1..0000000 --- a/pkg/chart/loader/archive.go +++ /dev/null @@ -1,194 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "fmt" - "io" - "net/http" - "os" - "path" - "regexp" - "strings" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chart" -) - -var drivePathPattern = regexp.MustCompile(`^[a-zA-Z]:/`) - -// FileLoader loads a chart from a file -type FileLoader string - -// Load loads a chart -func (l FileLoader) Load() (*chart.Chart, error) { - return LoadFile(string(l)) -} - -// LoadFile loads from an archive file. -func LoadFile(name string) (*chart.Chart, error) { - if fi, err := os.Stat(name); err != nil { - return nil, err - } else if fi.IsDir() { - return nil, errors.New("cannot load a directory") - } - - raw, err := os.Open(name) - if err != nil { - return nil, err - } - defer raw.Close() - - err = ensureArchive(name, raw) - if err != nil { - return nil, err - } - - c, err := LoadArchive(raw) - if err != nil { - if err == gzip.ErrHeader { - return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err) - } - } - return c, err -} - -// ensureArchive's job is to return an informative error if the file does not appear to be a gzipped archive. -// -// Sometimes users will provide a values.yaml for an argument where a chart is expected. One common occurrence -// of this is invoking `helm template values.yaml mychart` which would otherwise produce a confusing error -// if we didn't check for this. -func ensureArchive(name string, raw *os.File) error { - defer raw.Seek(0, 0) // reset read offset to allow archive loading to proceed. - - // Check the file format to give us a chance to provide the user with more actionable feedback. - buffer := make([]byte, 512) - _, err := raw.Read(buffer) - if err != nil && err != io.EOF { - return fmt.Errorf("file '%s' cannot be read: %s", name, err) - } - if contentType := http.DetectContentType(buffer); contentType != "application/x-gzip" { - // TODO: Is there a way to reliably test if a file content is YAML? ghodss/yaml accepts a wide - // variety of content (Makefile, .zshrc) as valid YAML without errors. - - // Wrong content type. Let's check if it's yaml and give an extra hint? - if strings.HasSuffix(name, ".yml") || strings.HasSuffix(name, ".yaml") { - return fmt.Errorf("file '%s' seems to be a YAML file, but expected a gzipped archive", name) - } - return fmt.Errorf("file '%s' does not appear to be a gzipped archive; got '%s'", name, contentType) - } - return nil -} - -// LoadArchiveFiles reads in files out of an archive into memory. This function -// performs important path security checks and should always be used before -// expanding a tarball -func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) { - unzipped, err := gzip.NewReader(in) - if err != nil { - return nil, err - } - defer unzipped.Close() - - files := []*BufferedFile{} - tr := tar.NewReader(unzipped) - for { - b := bytes.NewBuffer(nil) - hd, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return nil, err - } - - if hd.FileInfo().IsDir() { - // Use this instead of hd.Typeflag because we don't have to do any - // inference chasing. - continue - } - - switch hd.Typeflag { - // We don't want to process these extension header files. - case tar.TypeXGlobalHeader, tar.TypeXHeader: - continue - } - - // Archive could contain \ if generated on Windows - delimiter := "/" - if strings.ContainsRune(hd.Name, '\\') { - delimiter = "\\" - } - - parts := strings.Split(hd.Name, delimiter) - n := strings.Join(parts[1:], delimiter) - - // Normalize the path to the / delimiter - n = strings.ReplaceAll(n, delimiter, "/") - - if path.IsAbs(n) { - return nil, errors.New("chart illegally contains absolute paths") - } - - n = path.Clean(n) - if n == "." { - // In this case, the original path was relative when it should have been absolute. - return nil, errors.Errorf("chart illegally contains content outside the base directory: %q", hd.Name) - } - if strings.HasPrefix(n, "..") { - return nil, errors.New("chart illegally references parent directory") - } - - // In some particularly arcane acts of path creativity, it is possible to intermix - // UNIX and Windows style paths in such a way that you produce a result of the form - // c:/foo even after all the built-in absolute path checks. So we explicitly check - // for this condition. - if drivePathPattern.MatchString(n) { - return nil, errors.New("chart contains illegally named files") - } - - if parts[0] == "Chart.yaml" { - return nil, errors.New("chart yaml not in base directory") - } - - if _, err := io.Copy(b, tr); err != nil { - return nil, err - } - - files = append(files, &BufferedFile{Name: n, Data: b.Bytes()}) - b.Reset() - } - - if len(files) == 0 { - return nil, errors.New("no files in chart archive") - } - return files, nil -} - -// LoadArchive loads from a reader containing a compressed tar archive. -func LoadArchive(in io.Reader) (*chart.Chart, error) { - files, err := LoadArchiveFiles(in) - if err != nil { - return nil, err - } - - return LoadFiles(files) -} diff --git a/pkg/chart/loader/archive_test.go b/pkg/chart/loader/archive_test.go deleted file mode 100644 index 7d8c8b5..0000000 --- a/pkg/chart/loader/archive_test.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "testing" -) - -func TestLoadArchiveFiles(t *testing.T) { - tcs := []struct { - name string - generate func(w *tar.Writer) - check func(t *testing.T, files []*BufferedFile, err error) - }{ - { - name: "empty input should return no files", - generate: func(w *tar.Writer) {}, - check: func(t *testing.T, files []*BufferedFile, err error) { - if err.Error() != "no files in chart archive" { - t.Fatalf(`expected "no files in chart archive", got [%#v]`, err) - } - }, - }, - { - name: "should ignore files with XGlobalHeader type", - generate: func(w *tar.Writer) { - // simulate the presence of a `pax_global_header` file like you would get when - // processing a GitHub release archive. - _ = w.WriteHeader(&tar.Header{ - Typeflag: tar.TypeXGlobalHeader, - Name: "pax_global_header", - }) - - // we need to have at least one file, otherwise we'll get the "no files in chart archive" error - _ = w.WriteHeader(&tar.Header{ - Typeflag: tar.TypeReg, - Name: "dir/empty", - }) - }, - check: func(t *testing.T, files []*BufferedFile, err error) { - if err != nil { - t.Fatalf(`got unwanted error [%#v] for tar file with pax_global_header content`, err) - } - - if len(files) != 1 { - t.Fatalf(`expected to get one file but got [%v]`, files) - } - }, - }, - { - name: "should ignore files with TypeXHeader type", - generate: func(w *tar.Writer) { - // simulate the presence of a `pax_header` file like you might get when - // processing a GitHub release archive. - _ = w.WriteHeader(&tar.Header{ - Typeflag: tar.TypeXHeader, - Name: "pax_header", - }) - - // we need to have at least one file, otherwise we'll get the "no files in chart archive" error - _ = w.WriteHeader(&tar.Header{ - Typeflag: tar.TypeReg, - Name: "dir/empty", - }) - }, - check: func(t *testing.T, files []*BufferedFile, err error) { - if err != nil { - t.Fatalf(`got unwanted error [%#v] for tar file with pax_header content`, err) - } - - if len(files) != 1 { - t.Fatalf(`expected to get one file but got [%v]`, files) - } - }, - }, - } - - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - buf := &bytes.Buffer{} - gzw := gzip.NewWriter(buf) - tw := tar.NewWriter(gzw) - - tc.generate(tw) - - _ = tw.Close() - _ = gzw.Close() - - files, err := LoadArchiveFiles(buf) - tc.check(t, files, err) - }) - } -} diff --git a/pkg/chart/loader/directory.go b/pkg/chart/loader/directory.go deleted file mode 100644 index a12c515..0000000 --- a/pkg/chart/loader/directory.go +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/internal/ignore" - "helm.sh/helm/v3/internal/sympath" - "helm.sh/helm/v3/pkg/chart" -) - -// DirLoader loads a chart from a directory -type DirLoader string - -// Load loads the chart -func (l DirLoader) Load() (*chart.Chart, error) { - return LoadDir(string(l)) -} - -// LoadDir loads from a directory. -// -// This loads charts only from directories. -func LoadDir(dir string) (*chart.Chart, error) { - topdir, err := filepath.Abs(dir) - if err != nil { - return nil, err - } - - // Just used for errors. - c := &chart.Chart{} - - rules := ignore.Empty() - ifile := filepath.Join(topdir, ignore.HelmIgnore) - if _, err := os.Stat(ifile); err == nil { - r, err := ignore.ParseFile(ifile) - if err != nil { - return c, err - } - rules = r - } - rules.AddDefaults() - - files := []*BufferedFile{} - topdir += string(filepath.Separator) - - walk := func(name string, fi os.FileInfo, err error) error { - n := strings.TrimPrefix(name, topdir) - if n == "" { - // No need to process top level. Avoid bug with helmignore .* matching - // empty names. See issue 1779. - return nil - } - - // Normalize to / since it will also work on Windows - n = filepath.ToSlash(n) - - if err != nil { - return err - } - if fi.IsDir() { - // Directory-based ignore rules should involve skipping the entire - // contents of that directory. - if rules.Ignore(n, fi) { - return filepath.SkipDir - } - return nil - } - - // If a .helmignore file matches, skip this file. - if rules.Ignore(n, fi) { - return nil - } - - // Irregular files include devices, sockets, and other uses of files that - // are not regular files. In Go they have a file mode type bit set. - // See https://golang.org/pkg/os/#FileMode for examples. - if !fi.Mode().IsRegular() { - return fmt.Errorf("cannot load irregular file %s as it has file mode type bits set", name) - } - - data, err := ioutil.ReadFile(name) - if err != nil { - return errors.Wrapf(err, "error reading %s", n) - } - - files = append(files, &BufferedFile{Name: n, Data: data}) - return nil - } - if err = sympath.Walk(topdir, walk); err != nil { - return c, err - } - - return LoadFiles(files) -} diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go deleted file mode 100644 index dd4fd2d..0000000 --- a/pkg/chart/loader/load.go +++ /dev/null @@ -1,185 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "bytes" - "log" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart" -) - -// ChartLoader loads a chart. -type ChartLoader interface { - Load() (*chart.Chart, error) -} - -// Loader returns a new ChartLoader appropriate for the given chart name -func Loader(name string) (ChartLoader, error) { - fi, err := os.Stat(name) - if err != nil { - return nil, err - } - if fi.IsDir() { - return DirLoader(name), nil - } - return FileLoader(name), nil - -} - -// Load takes a string name, tries to resolve it to a file or directory, and then loads it. -// -// This is the preferred way to load a chart. It will discover the chart encoding -// and hand off to the appropriate chart reader. -// -// If a .helmignore file is present, the directory loader will skip loading any files -// matching it. But .helmignore is not evaluated when reading out of an archive. -func Load(name string) (*chart.Chart, error) { - l, err := Loader(name) - if err != nil { - return nil, err - } - return l.Load() -} - -// BufferedFile represents an archive file buffered for later processing. -type BufferedFile struct { - Name string - Data []byte -} - -// LoadFiles loads from in-memory files. -func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { - c := new(chart.Chart) - subcharts := make(map[string][]*BufferedFile) - - for _, f := range files { - c.Raw = append(c.Raw, &chart.File{Name: f.Name, Data: f.Data}) - switch { - case f.Name == "Chart.yaml": - if c.Metadata == nil { - c.Metadata = new(chart.Metadata) - } - if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { - return c, errors.Wrap(err, "cannot load Chart.yaml") - } - // NOTE(bacongobbler): while the chart specification says that APIVersion must be set, - // Helm 2 accepted charts that did not provide an APIVersion in their chart metadata. - // Because of that, if APIVersion is unset, we should assume we're loading a v1 chart. - if c.Metadata.APIVersion == "" { - c.Metadata.APIVersion = chart.APIVersionV1 - } - case f.Name == "Chart.lock": - c.Lock = new(chart.Lock) - if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { - return c, errors.Wrap(err, "cannot load Chart.lock") - } - case f.Name == "values.yaml": - c.Values = make(map[string]interface{}) - if err := yaml.Unmarshal(f.Data, &c.Values); err != nil { - return c, errors.Wrap(err, "cannot load values.yaml") - } - case f.Name == "values.schema.json": - c.Schema = f.Data - - // Deprecated: requirements.yaml is deprecated use Chart.yaml. - // We will handle it for you because we are nice people - case f.Name == "requirements.yaml": - if c.Metadata == nil { - c.Metadata = new(chart.Metadata) - } - if c.Metadata.APIVersion != chart.APIVersionV1 { - log.Printf("Warning: Dependencies are handled in Chart.yaml since apiVersion \"v2\". We recommend migrating dependencies to Chart.yaml.") - } - if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { - return c, errors.Wrap(err, "cannot load requirements.yaml") - } - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - // Deprecated: requirements.lock is deprecated use Chart.lock. - case f.Name == "requirements.lock": - c.Lock = new(chart.Lock) - if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { - return c, errors.Wrap(err, "cannot load requirements.lock") - } - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - - case strings.HasPrefix(f.Name, "templates/"): - c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data}) - case strings.HasPrefix(f.Name, "charts/"): - if filepath.Ext(f.Name) == ".prov" { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - continue - } - - fname := strings.TrimPrefix(f.Name, "charts/") - cname := strings.SplitN(fname, "/", 2)[0] - subcharts[cname] = append(subcharts[cname], &BufferedFile{Name: fname, Data: f.Data}) - default: - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - } - - if err := c.Validate(); err != nil { - return c, err - } - - for n, files := range subcharts { - var sc *chart.Chart - var err error - switch { - case strings.IndexAny(n, "_.") == 0: - continue - case filepath.Ext(n) == ".tgz": - file := files[0] - if file.Name != n { - return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Name(), n, file.Name) - } - // Untar the chart and add to c.Dependencies - sc, err = LoadArchive(bytes.NewBuffer(file.Data)) - default: - // We have to trim the prefix off of every file, and ignore any file - // that is in charts/, but isn't actually a chart. - buff := make([]*BufferedFile, 0, len(files)) - for _, f := range files { - parts := strings.SplitN(f.Name, "/", 2) - if len(parts) < 2 { - continue - } - f.Name = parts[1] - buff = append(buff, f) - } - sc, err = LoadFiles(buff) - } - - if err != nil { - return c, errors.Wrapf(err, "error unpacking %s in %s", n, c.Name()) - } - c.AddDependency(sc) - } - - return c, nil -} diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go deleted file mode 100644 index 26513d3..0000000 --- a/pkg/chart/loader/load_test.go +++ /dev/null @@ -1,467 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "io/ioutil" - "os" - "path/filepath" - "runtime" - "strings" - "testing" - "time" - - "helm.sh/helm/v3/pkg/chart" -) - -func TestLoadDir(t *testing.T) { - l, err := Loader("testdata/frobnitz") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - c, err := l.Load() - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - verifyFrobnitz(t, c) - verifyChart(t, c) - verifyDependencies(t, c) - verifyDependenciesLock(t, c) -} - -func TestLoadDirWithDevNull(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("test only works on unix systems with /dev/null present") - } - - l, err := Loader("testdata/frobnitz_with_dev_null") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - if _, err := l.Load(); err == nil { - t.Errorf("packages with an irregular file (/dev/null) should not load") - } -} - -func TestLoadDirWithSymlink(t *testing.T) { - sym := filepath.Join("..", "LICENSE") - link := filepath.Join("testdata", "frobnitz_with_symlink", "LICENSE") - - if err := os.Symlink(sym, link); err != nil { - t.Fatal(err) - } - - defer os.Remove(link) - - l, err := Loader("testdata/frobnitz_with_symlink") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - - c, err := l.Load() - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - verifyFrobnitz(t, c) - verifyChart(t, c) - verifyDependencies(t, c) - verifyDependenciesLock(t, c) -} - -func TestLoadV1(t *testing.T) { - l, err := Loader("testdata/frobnitz.v1") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - c, err := l.Load() - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - verifyDependencies(t, c) - verifyDependenciesLock(t, c) -} - -func TestLoadFileV1(t *testing.T) { - l, err := Loader("testdata/frobnitz.v1.tgz") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - c, err := l.Load() - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - verifyDependencies(t, c) - verifyDependenciesLock(t, c) -} - -func TestLoadFile(t *testing.T) { - l, err := Loader("testdata/frobnitz-1.2.3.tgz") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - c, err := l.Load() - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - verifyFrobnitz(t, c) - verifyChart(t, c) - verifyDependencies(t, c) -} - -func TestLoadFiles(t *testing.T) { - goodFiles := []*BufferedFile{ - { - Name: "Chart.yaml", - Data: []byte(`apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -`), - }, - { - Name: "values.yaml", - Data: []byte("var: some values"), - }, - { - Name: "values.schema.json", - Data: []byte("type: Values"), - }, - { - Name: "templates/deployment.yaml", - Data: []byte("some deployment"), - }, - { - Name: "templates/service.yaml", - Data: []byte("some service"), - }, - } - - c, err := LoadFiles(goodFiles) - if err != nil { - t.Errorf("Expected good files to be loaded, got %v", err) - } - - if c.Name() != "frobnitz" { - t.Errorf("Expected chart name to be 'frobnitz', got %s", c.Name()) - } - - if c.Values["var"] != "some values" { - t.Error("Expected chart values to be populated with default values") - } - - if len(c.Raw) != 5 { - t.Errorf("Expected %d files, got %d", 5, len(c.Raw)) - } - - if !bytes.Equal(c.Schema, []byte("type: Values")) { - t.Error("Expected chart schema to be populated with default values") - } - - if len(c.Templates) != 2 { - t.Errorf("Expected number of templates == 2, got %d", len(c.Templates)) - } - - if _, err = LoadFiles([]*BufferedFile{}); err == nil { - t.Fatal("Expected err to be non-nil") - } - if err.Error() != "validation: chart.metadata is required" { - t.Errorf("Expected chart metadata missing error, got '%s'", err.Error()) - } -} - -// Packaging the chart on a Windows machine will produce an -// archive that has \\ as delimiters. Test that we support these archives -func TestLoadFileBackslash(t *testing.T) { - c, err := Load("testdata/frobnitz_backslash-1.2.3.tgz") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - verifyChartFileAndTemplate(t, c, "frobnitz_backslash") - verifyChart(t, c) - verifyDependencies(t, c) -} - -func TestLoadV2WithReqs(t *testing.T) { - l, err := Loader("testdata/frobnitz.v2.reqs") - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - c, err := l.Load() - if err != nil { - t.Fatalf("Failed to load testdata: %s", err) - } - verifyDependencies(t, c) - verifyDependenciesLock(t, c) -} - -func TestLoadInvalidArchive(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "helm-test-") - if err != nil { - t.Fatal(err) - } - defer os.Remove(tmpdir) - - writeTar := func(filename, internalPath string, body []byte) { - dest, err := os.Create(filename) - if err != nil { - t.Fatal(err) - } - zipper := gzip.NewWriter(dest) - tw := tar.NewWriter(zipper) - - h := &tar.Header{ - Name: internalPath, - Mode: 0755, - Size: int64(len(body)), - ModTime: time.Now(), - } - if err := tw.WriteHeader(h); err != nil { - t.Fatal(err) - } - if _, err := tw.Write(body); err != nil { - t.Fatal(err) - } - tw.Close() - zipper.Close() - dest.Close() - } - - for _, tt := range []struct { - chartname string - internal string - expectError string - }{ - {"illegal-dots.tgz", "../../malformed-helm-test", "chart illegally references parent directory"}, - {"illegal-dots2.tgz", "/foo/../../malformed-helm-test", "chart illegally references parent directory"}, - {"illegal-dots3.tgz", "/../../malformed-helm-test", "chart illegally references parent directory"}, - {"illegal-dots4.tgz", "./../../malformed-helm-test", "chart illegally references parent directory"}, - {"illegal-name.tgz", "./.", "chart illegally contains content outside the base directory"}, - {"illegal-name2.tgz", "/./.", "chart illegally contains content outside the base directory"}, - {"illegal-name3.tgz", "missing-leading-slash", "chart illegally contains content outside the base directory"}, - {"illegal-name4.tgz", "/missing-leading-slash", "validation: chart.metadata is required"}, - {"illegal-abspath.tgz", "//foo", "chart illegally contains absolute paths"}, - {"illegal-abspath2.tgz", "///foo", "chart illegally contains absolute paths"}, - {"illegal-abspath3.tgz", "\\\\foo", "chart illegally contains absolute paths"}, - {"illegal-abspath3.tgz", "\\..\\..\\foo", "chart illegally references parent directory"}, - - // Under special circumstances, this can get normalized to things that look like absolute Windows paths - {"illegal-abspath4.tgz", "\\.\\c:\\\\foo", "chart contains illegally named files"}, - {"illegal-abspath5.tgz", "/./c://foo", "chart contains illegally named files"}, - {"illegal-abspath6.tgz", "\\\\?\\Some\\windows\\magic", "chart illegally contains absolute paths"}, - } { - illegalChart := filepath.Join(tmpdir, tt.chartname) - writeTar(illegalChart, tt.internal, []byte("hello: world")) - _, err = Load(illegalChart) - if err == nil { - t.Fatal("expected error when unpacking illegal files") - } - if !strings.Contains(err.Error(), tt.expectError) { - t.Errorf("Expected error to contain %q, got %q for %s", tt.expectError, err.Error(), tt.chartname) - } - } - - // Make sure that absolute path gets interpreted as relative - illegalChart := filepath.Join(tmpdir, "abs-path.tgz") - writeTar(illegalChart, "/Chart.yaml", []byte("hello: world")) - _, err = Load(illegalChart) - if err.Error() != "validation: chart.metadata.name is required" { - t.Error(err) - } - - // And just to validate that the above was not spurious - illegalChart = filepath.Join(tmpdir, "abs-path2.tgz") - writeTar(illegalChart, "files/whatever.yaml", []byte("hello: world")) - _, err = Load(illegalChart) - if err.Error() != "validation: chart.metadata is required" { - t.Error(err) - } - - // Finally, test that drive letter gets stripped off on Windows - illegalChart = filepath.Join(tmpdir, "abs-winpath.tgz") - writeTar(illegalChart, "c:\\Chart.yaml", []byte("hello: world")) - _, err = Load(illegalChart) - if err.Error() != "validation: chart.metadata.name is required" { - t.Error(err) - } -} - -func verifyChart(t *testing.T, c *chart.Chart) { - t.Helper() - if c.Name() == "" { - t.Fatalf("No chart metadata found on %v", c) - } - t.Logf("Verifying chart %s", c.Name()) - if len(c.Templates) != 1 { - t.Errorf("Expected 1 template, got %d", len(c.Templates)) - } - - numfiles := 6 - if len(c.Files) != numfiles { - t.Errorf("Expected %d extra files, got %d", numfiles, len(c.Files)) - for _, n := range c.Files { - t.Logf("\t%s", n.Name) - } - } - - if len(c.Dependencies()) != 2 { - t.Errorf("Expected 2 dependencies, got %d (%v)", len(c.Dependencies()), c.Dependencies()) - for _, d := range c.Dependencies() { - t.Logf("\tSubchart: %s\n", d.Name()) - } - } - - expect := map[string]map[string]string{ - "alpine": { - "version": "0.1.0", - }, - "mariner": { - "version": "4.3.2", - }, - } - - for _, dep := range c.Dependencies() { - if dep.Metadata == nil { - t.Fatalf("expected metadata on dependency: %v", dep) - } - exp, ok := expect[dep.Name()] - if !ok { - t.Fatalf("Unknown dependency %s", dep.Name()) - } - if exp["version"] != dep.Metadata.Version { - t.Errorf("Expected %s version %s, got %s", dep.Name(), exp["version"], dep.Metadata.Version) - } - } - -} - -func verifyDependencies(t *testing.T, c *chart.Chart) { - if len(c.Metadata.Dependencies) != 2 { - t.Errorf("Expected 2 dependencies, got %d", len(c.Metadata.Dependencies)) - } - tests := []*chart.Dependency{ - {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, - {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, - } - for i, tt := range tests { - d := c.Metadata.Dependencies[i] - if d.Name != tt.Name { - t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name) - } - if d.Version != tt.Version { - t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version) - } - if d.Repository != tt.Repository { - t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository) - } - } -} - -func verifyDependenciesLock(t *testing.T, c *chart.Chart) { - if len(c.Metadata.Dependencies) != 2 { - t.Errorf("Expected 2 dependencies, got %d", len(c.Metadata.Dependencies)) - } - tests := []*chart.Dependency{ - {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, - {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, - } - for i, tt := range tests { - d := c.Metadata.Dependencies[i] - if d.Name != tt.Name { - t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name) - } - if d.Version != tt.Version { - t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version) - } - if d.Repository != tt.Repository { - t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository) - } - } -} - -func verifyFrobnitz(t *testing.T, c *chart.Chart) { - verifyChartFileAndTemplate(t, c, "frobnitz") -} - -func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) { - if c.Metadata == nil { - t.Fatal("Metadata is nil") - } - if c.Name() != name { - t.Errorf("Expected %s, got %s", name, c.Name()) - } - if len(c.Templates) != 1 { - t.Fatalf("Expected 1 template, got %d", len(c.Templates)) - } - if c.Templates[0].Name != "templates/template.tpl" { - t.Errorf("Unexpected template: %s", c.Templates[0].Name) - } - if len(c.Templates[0].Data) == 0 { - t.Error("No template data.") - } - if len(c.Files) != 6 { - t.Fatalf("Expected 6 Files, got %d", len(c.Files)) - } - if len(c.Dependencies()) != 2 { - t.Fatalf("Expected 2 Dependency, got %d", len(c.Dependencies())) - } - if len(c.Metadata.Dependencies) != 2 { - t.Fatalf("Expected 2 Dependencies.Dependency, got %d", len(c.Metadata.Dependencies)) - } - if len(c.Lock.Dependencies) != 2 { - t.Fatalf("Expected 2 Lock.Dependency, got %d", len(c.Lock.Dependencies)) - } - - for _, dep := range c.Dependencies() { - switch dep.Name() { - case "mariner": - case "alpine": - if len(dep.Templates) != 1 { - t.Fatalf("Expected 1 template, got %d", len(dep.Templates)) - } - if dep.Templates[0].Name != "templates/alpine-pod.yaml" { - t.Errorf("Unexpected template: %s", dep.Templates[0].Name) - } - if len(dep.Templates[0].Data) == 0 { - t.Error("No template data.") - } - if len(dep.Files) != 1 { - t.Fatalf("Expected 1 Files, got %d", len(dep.Files)) - } - if len(dep.Dependencies()) != 2 { - t.Fatalf("Expected 2 Dependency, got %d", len(dep.Dependencies())) - } - default: - t.Errorf("Unexpected dependency %s", dep.Name()) - } - } -} diff --git a/pkg/chart/loader/testdata/LICENSE b/pkg/chart/loader/testdata/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chart/loader/testdata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chart/loader/testdata/albatross/Chart.yaml b/pkg/chart/loader/testdata/albatross/Chart.yaml deleted file mode 100644 index eeef737..0000000 --- a/pkg/chart/loader/testdata/albatross/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -name: albatross -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chart/loader/testdata/albatross/values.yaml b/pkg/chart/loader/testdata/albatross/values.yaml deleted file mode 100644 index 3121cd7..0000000 --- a/pkg/chart/loader/testdata/albatross/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -albatross: "true" - -global: - author: Coleridge diff --git a/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz b/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz deleted file mode 100644 index b2b76a8..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz.v1.tgz b/pkg/chart/loader/testdata/frobnitz.v1.tgz deleted file mode 100644 index 6282f9b..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz.v1.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz.v1/.helmignore b/pkg/chart/loader/testdata/frobnitz.v1/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chart/loader/testdata/frobnitz.v1/Chart.lock b/pkg/chart/loader/testdata/frobnitz.v1/Chart.lock deleted file mode 100644 index 6fcc2ed..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/Chart.lock +++ /dev/null @@ -1,8 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts -digest: invalid diff --git a/pkg/chart/loader/testdata/frobnitz.v1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz.v1/Chart.yaml deleted file mode 100644 index 134cd11..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/Chart.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -annotations: - extrakey: extravalue - anotherkey: anothervalue diff --git a/pkg/chart/loader/testdata/frobnitz.v1/INSTALL.txt b/pkg/chart/loader/testdata/frobnitz.v1/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chart/loader/testdata/frobnitz.v1/LICENSE b/pkg/chart/loader/testdata/frobnitz.v1/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chart/loader/testdata/frobnitz.v1/README.md b/pkg/chart/loader/testdata/frobnitz.v1/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chart/loader/testdata/frobnitz.v1/charts/_ignore_me b/pkg/chart/loader/testdata/frobnitz.v1/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/README.md b/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/charts/mast1/values.yaml b/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 21ae20a..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - app.kubernetes.io/name: {{.Chart.Name}} - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.9" - command: ["/bin/sleep","9000"] diff --git a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/values.yaml b/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chart/loader/testdata/frobnitz.v1/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz.v1/charts/mariner-4.3.2.tgz deleted file mode 100644 index 3190136..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz.v1/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz.v1/docs/README.md b/pkg/chart/loader/testdata/frobnitz.v1/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chart/loader/testdata/frobnitz.v1/icon.svg b/pkg/chart/loader/testdata/frobnitz.v1/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chart/loader/testdata/frobnitz.v1/ignore/me.txt b/pkg/chart/loader/testdata/frobnitz.v1/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chart/loader/testdata/frobnitz.v1/requirements.yaml b/pkg/chart/loader/testdata/frobnitz.v1/requirements.yaml deleted file mode 100644 index 5eb0bc9..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/requirements.yaml +++ /dev/null @@ -1,7 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts diff --git a/pkg/chart/loader/testdata/frobnitz.v1/templates/template.tpl b/pkg/chart/loader/testdata/frobnitz.v1/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chart/loader/testdata/frobnitz.v1/values.yaml b/pkg/chart/loader/testdata/frobnitz.v1/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v1/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/.helmignore b/pkg/chart/loader/testdata/frobnitz.v2.reqs/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/Chart.yaml b/pkg/chart/loader/testdata/frobnitz.v2.reqs/Chart.yaml deleted file mode 100644 index f3ab302..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/Chart.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: v2 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -annotations: - extrakey: extravalue - anotherkey: anothervalue diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/INSTALL.txt b/pkg/chart/loader/testdata/frobnitz.v2.reqs/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/LICENSE b/pkg/chart/loader/testdata/frobnitz.v2.reqs/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/README.md b/pkg/chart/loader/testdata/frobnitz.v2.reqs/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/_ignore_me b/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/README.md b/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/charts/mast1/values.yaml b/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 21ae20a..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - app.kubernetes.io/name: {{.Chart.Name}} - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.9" - command: ["/bin/sleep","9000"] diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/values.yaml b/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/mariner-4.3.2.tgz deleted file mode 100644 index 3190136..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz.v2.reqs/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/docs/README.md b/pkg/chart/loader/testdata/frobnitz.v2.reqs/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/icon.svg b/pkg/chart/loader/testdata/frobnitz.v2.reqs/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/ignore/me.txt b/pkg/chart/loader/testdata/frobnitz.v2.reqs/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/requirements.yaml b/pkg/chart/loader/testdata/frobnitz.v2.reqs/requirements.yaml deleted file mode 100644 index 5eb0bc9..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/requirements.yaml +++ /dev/null @@ -1,7 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/templates/template.tpl b/pkg/chart/loader/testdata/frobnitz.v2.reqs/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chart/loader/testdata/frobnitz.v2.reqs/values.yaml b/pkg/chart/loader/testdata/frobnitz.v2.reqs/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chart/loader/testdata/frobnitz.v2.reqs/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chart/loader/testdata/frobnitz/.helmignore b/pkg/chart/loader/testdata/frobnitz/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chart/loader/testdata/frobnitz/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chart/loader/testdata/frobnitz/Chart.lock b/pkg/chart/loader/testdata/frobnitz/Chart.lock deleted file mode 100644 index 6fcc2ed..0000000 --- a/pkg/chart/loader/testdata/frobnitz/Chart.lock +++ /dev/null @@ -1,8 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts -digest: invalid diff --git a/pkg/chart/loader/testdata/frobnitz/Chart.yaml b/pkg/chart/loader/testdata/frobnitz/Chart.yaml deleted file mode 100644 index fcd4a4a..0000000 --- a/pkg/chart/loader/testdata/frobnitz/Chart.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -annotations: - extrakey: extravalue - anotherkey: anothervalue -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts diff --git a/pkg/chart/loader/testdata/frobnitz/INSTALL.txt b/pkg/chart/loader/testdata/frobnitz/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chart/loader/testdata/frobnitz/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chart/loader/testdata/frobnitz/LICENSE b/pkg/chart/loader/testdata/frobnitz/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chart/loader/testdata/frobnitz/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chart/loader/testdata/frobnitz/README.md b/pkg/chart/loader/testdata/frobnitz/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chart/loader/testdata/frobnitz/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chart/loader/testdata/frobnitz/charts/_ignore_me b/pkg/chart/loader/testdata/frobnitz/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chart/loader/testdata/frobnitz/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/README.md b/pkg/chart/loader/testdata/frobnitz/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chart/loader/testdata/frobnitz/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 21ae20a..0000000 --- a/pkg/chart/loader/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - app.kubernetes.io/name: {{.Chart.Name}} - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.9" - command: ["/bin/sleep","9000"] diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/values.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chart/loader/testdata/frobnitz/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz deleted file mode 100644 index 3190136..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz/docs/README.md b/pkg/chart/loader/testdata/frobnitz/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chart/loader/testdata/frobnitz/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chart/loader/testdata/frobnitz/icon.svg b/pkg/chart/loader/testdata/frobnitz/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chart/loader/testdata/frobnitz/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chart/loader/testdata/frobnitz/ignore/me.txt b/pkg/chart/loader/testdata/frobnitz/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chart/loader/testdata/frobnitz/templates/template.tpl b/pkg/chart/loader/testdata/frobnitz/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chart/loader/testdata/frobnitz/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chart/loader/testdata/frobnitz/values.yaml b/pkg/chart/loader/testdata/frobnitz/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chart/loader/testdata/frobnitz/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz b/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz deleted file mode 100644 index a9d4c11..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/.helmignore b/pkg/chart/loader/testdata/frobnitz_backslash/.helmignore deleted file mode 100755 index 9973a57..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/Chart.lock b/pkg/chart/loader/testdata/frobnitz_backslash/Chart.lock deleted file mode 100755 index 6fcc2ed..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/Chart.lock +++ /dev/null @@ -1,8 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts -digest: invalid diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/Chart.yaml deleted file mode 100755 index b1dd40a..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/Chart.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -name: frobnitz_backslash -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -annotations: - extrakey: extravalue - anotherkey: anothervalue -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/INSTALL.txt b/pkg/chart/loader/testdata/frobnitz_backslash/INSTALL.txt deleted file mode 100755 index 2010438..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/LICENSE b/pkg/chart/loader/testdata/frobnitz_backslash/LICENSE deleted file mode 100755 index 6121943..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/README.md b/pkg/chart/loader/testdata/frobnitz_backslash/README.md deleted file mode 100755 index 8cf4cc3..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/_ignore_me b/pkg/chart/loader/testdata/frobnitz_backslash/charts/_ignore_me deleted file mode 100755 index 2cecca6..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml deleted file mode 100755 index 79e0d65..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/README.md b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/README.md deleted file mode 100755 index b30b949..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100755 index 1c9dd5f..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml deleted file mode 100755 index 42c39c2..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100755 index 61cb620..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml deleted file mode 100755 index 0ac5ca6..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service | quote }} - app.kubernetes.io/name: {{.Chart.Name}} - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.9" - command: ["/bin/sleep","9000"] diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/values.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/values.yaml deleted file mode 100755 index 6c2aab7..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz deleted file mode 100755 index 3190136..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/docs/README.md b/pkg/chart/loader/testdata/frobnitz_backslash/docs/README.md deleted file mode 100755 index d40747c..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/icon.svg b/pkg/chart/loader/testdata/frobnitz_backslash/icon.svg deleted file mode 100755 index 8921306..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/ignore/me.txt b/pkg/chart/loader/testdata/frobnitz_backslash/ignore/me.txt deleted file mode 100755 index e69de29..0000000 diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/templates/template.tpl b/pkg/chart/loader/testdata/frobnitz_backslash/templates/template.tpl deleted file mode 100755 index c651ee6..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/values.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/values.yaml deleted file mode 100755 index 61f5012..0000000 --- a/pkg/chart/loader/testdata/frobnitz_backslash/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/.helmignore b/pkg/chart/loader/testdata/frobnitz_with_dev_null/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/Chart.lock b/pkg/chart/loader/testdata/frobnitz_with_dev_null/Chart.lock deleted file mode 100644 index 6fcc2ed..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/Chart.lock +++ /dev/null @@ -1,8 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts -digest: invalid diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_with_dev_null/Chart.yaml deleted file mode 100644 index fcd4a4a..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/Chart.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -annotations: - extrakey: extravalue - anotherkey: anothervalue -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/INSTALL.txt b/pkg/chart/loader/testdata/frobnitz_with_dev_null/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/LICENSE b/pkg/chart/loader/testdata/frobnitz_with_dev_null/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/README.md b/pkg/chart/loader/testdata/frobnitz_with_dev_null/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/_ignore_me b/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/README.md b/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/charts/mast1/values.yaml b/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 21ae20a..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - app.kubernetes.io/name: {{.Chart.Name}} - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.9" - command: ["/bin/sleep","9000"] diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/values.yaml b/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/mariner-4.3.2.tgz deleted file mode 100644 index 3190136..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz_with_dev_null/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/docs/README.md b/pkg/chart/loader/testdata/frobnitz_with_dev_null/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/icon.svg b/pkg/chart/loader/testdata/frobnitz_with_dev_null/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/ignore/me.txt b/pkg/chart/loader/testdata/frobnitz_with_dev_null/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/null b/pkg/chart/loader/testdata/frobnitz_with_dev_null/null deleted file mode 120000 index dc1dc0c..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/null +++ /dev/null @@ -1 +0,0 @@ -/dev/null \ No newline at end of file diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/templates/template.tpl b/pkg/chart/loader/testdata/frobnitz_with_dev_null/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chart/loader/testdata/frobnitz_with_dev_null/values.yaml b/pkg/chart/loader/testdata/frobnitz_with_dev_null/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_dev_null/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/.helmignore b/pkg/chart/loader/testdata/frobnitz_with_symlink/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/Chart.lock b/pkg/chart/loader/testdata/frobnitz_with_symlink/Chart.lock deleted file mode 100644 index 6fcc2ed..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/Chart.lock +++ /dev/null @@ -1,8 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts -digest: invalid diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_with_symlink/Chart.yaml deleted file mode 100644 index fcd4a4a..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/Chart.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -annotations: - extrakey: extravalue - anotherkey: anothervalue -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/INSTALL.txt b/pkg/chart/loader/testdata/frobnitz_with_symlink/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/README.md b/pkg/chart/loader/testdata/frobnitz_with_symlink/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/_ignore_me b/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/README.md b/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/charts/mast1/values.yaml b/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/templates/alpine-pod.yaml b/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 21ae20a..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - app.kubernetes.io/name: {{.Chart.Name}} - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.9" - command: ["/bin/sleep","9000"] diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/values.yaml b/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/mariner-4.3.2.tgz deleted file mode 100644 index 3190136..0000000 Binary files a/pkg/chart/loader/testdata/frobnitz_with_symlink/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/docs/README.md b/pkg/chart/loader/testdata/frobnitz_with_symlink/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/icon.svg b/pkg/chart/loader/testdata/frobnitz_with_symlink/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/ignore/me.txt b/pkg/chart/loader/testdata/frobnitz_with_symlink/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/templates/template.tpl b/pkg/chart/loader/testdata/frobnitz_with_symlink/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chart/loader/testdata/frobnitz_with_symlink/values.yaml b/pkg/chart/loader/testdata/frobnitz_with_symlink/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chart/loader/testdata/frobnitz_with_symlink/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chart/loader/testdata/genfrob.sh b/pkg/chart/loader/testdata/genfrob.sh deleted file mode 100755 index 35fdd59..0000000 --- a/pkg/chart/loader/testdata/genfrob.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -# Pack the albatross chart into the mariner chart. -echo "Packing albatross into mariner" -tar -zcvf mariner/charts/albatross-0.1.0.tgz albatross - -echo "Packing mariner into frobnitz" -tar -zcvf frobnitz/charts/mariner-4.3.2.tgz mariner -tar -zcvf frobnitz_backslash/charts/mariner-4.3.2.tgz mariner - -# Pack the frobnitz chart. -echo "Packing frobnitz" -tar --exclude=ignore/* -zcvf frobnitz-1.2.3.tgz frobnitz -tar --exclude=ignore/* -zcvf frobnitz_backslash-1.2.3.tgz frobnitz_backslash diff --git a/pkg/chart/loader/testdata/mariner/Chart.yaml b/pkg/chart/loader/testdata/mariner/Chart.yaml deleted file mode 100644 index 92dc4b3..0000000 --- a/pkg/chart/loader/testdata/mariner/Chart.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -name: mariner -description: A Helm chart for Kubernetes -version: 4.3.2 -home: "" -dependencies: - - name: albatross - repository: https://example.com/mariner/charts - version: "0.1.0" diff --git a/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz b/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz deleted file mode 100644 index 128ef82..0000000 Binary files a/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz and /dev/null differ diff --git a/pkg/chart/loader/testdata/mariner/templates/placeholder.tpl b/pkg/chart/loader/testdata/mariner/templates/placeholder.tpl deleted file mode 100644 index 29c1184..0000000 --- a/pkg/chart/loader/testdata/mariner/templates/placeholder.tpl +++ /dev/null @@ -1 +0,0 @@ -# This is a placeholder. diff --git a/pkg/chart/loader/testdata/mariner/values.yaml b/pkg/chart/loader/testdata/mariner/values.yaml deleted file mode 100644 index b0ccb00..0000000 --- a/pkg/chart/loader/testdata/mariner/values.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Default values for . -# This is a YAML-formatted file. https://github.com/toml-lang/toml -# Declare name/value pairs to be passed into your templates. -# name: "value" - -: - test: true diff --git a/pkg/chart/metadata.go b/pkg/chart/metadata.go deleted file mode 100644 index 96a3965..0000000 --- a/pkg/chart/metadata.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -// Maintainer describes a Chart maintainer. -type Maintainer struct { - // Name is a user name or organization name - Name string `json:"name,omitempty"` - // Email is an optional email address to contact the named maintainer - Email string `json:"email,omitempty"` - // URL is an optional URL to an address for the named maintainer - URL string `json:"url,omitempty"` -} - -// Metadata for a Chart file. This models the structure of a Chart.yaml file. -type Metadata struct { - // The name of the chart - Name string `json:"name,omitempty"` - // The URL to a relevant project page, git repo, or contact person - Home string `json:"home,omitempty"` - // Source is the URL to the source code of this chart - Sources []string `json:"sources,omitempty"` - // A SemVer 2 conformant version string of the chart - Version string `json:"version,omitempty"` - // A one-sentence description of the chart - Description string `json:"description,omitempty"` - // A list of string keywords - Keywords []string `json:"keywords,omitempty"` - // A list of name and URL/email address combinations for the maintainer(s) - Maintainers []*Maintainer `json:"maintainers,omitempty"` - // The URL to an icon file. - Icon string `json:"icon,omitempty"` - // The API Version of this chart. - APIVersion string `json:"apiVersion,omitempty"` - // The condition to check to enable chart - Condition string `json:"condition,omitempty"` - // The tags to check to enable chart - Tags string `json:"tags,omitempty"` - // The version of the application enclosed inside of this chart. - AppVersion string `json:"appVersion,omitempty"` - // Whether or not this chart is deprecated - Deprecated bool `json:"deprecated,omitempty"` - // Annotations are additional mappings uninterpreted by Helm, - // made available for inspection by other applications. - Annotations map[string]string `json:"annotations,omitempty"` - // KubeVersion is a SemVer constraint specifying the version of Kubernetes required. - KubeVersion string `json:"kubeVersion,omitempty"` - // Dependencies are a list of dependencies for a chart. - Dependencies []*Dependency `json:"dependencies,omitempty"` - // Specifies the chart type: application or library - Type string `json:"type,omitempty"` -} - -// Validate checks the metadata for known issues, returning an error if metadata is not correct -func (md *Metadata) Validate() error { - if md == nil { - return ValidationError("chart.metadata is required") - } - if md.APIVersion == "" { - return ValidationError("chart.metadata.apiVersion is required") - } - if md.Name == "" { - return ValidationError("chart.metadata.name is required") - } - if md.Version == "" { - return ValidationError("chart.metadata.version is required") - } - if !isValidChartType(md.Type) { - return ValidationError("chart.metadata.type must be application or library") - } - // TODO validate valid semver here? - return nil -} - -func isValidChartType(in string) bool { - switch in { - case "", "application", "library": - return true - } - return false -} diff --git a/pkg/chartutil/capabilities.go b/pkg/chartutil/capabilities.go deleted file mode 100644 index f46350b..0000000 --- a/pkg/chartutil/capabilities.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "k8s.io/client-go/kubernetes/scheme" - - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" -) - -var ( - // DefaultVersionSet is the default version set, which includes only Core V1 ("v1"). - DefaultVersionSet = allKnownVersions() - - // DefaultCapabilities is the default set of capabilities. - DefaultCapabilities = &Capabilities{ - KubeVersion: KubeVersion{ - Version: "v1.16.0", - Major: "1", - Minor: "16", - }, - APIVersions: DefaultVersionSet, - } -) - -// Capabilities describes the capabilities of the Kubernetes cluster. -type Capabilities struct { - // KubeVersion is the Kubernetes version. - KubeVersion KubeVersion - // APIversions are supported Kubernetes API versions. - APIVersions VersionSet -} - -// KubeVersion is the Kubernetes version. -type KubeVersion struct { - Version string // Kubernetes version - Major string // Kubernetes major version - Minor string // Kubernetes minor version -} - -// String implements fmt.Stringer -func (kv *KubeVersion) String() string { return kv.Version } - -// GitVersion returns the Kubernetes version string. -// -// Deprecated: use KubeVersion.Version. -func (kv *KubeVersion) GitVersion() string { return kv.Version } - -// VersionSet is a set of Kubernetes API versions. -type VersionSet []string - -// Has returns true if the version string is in the set. -// -// vs.Has("apps/v1") -func (v VersionSet) Has(apiVersion string) bool { - for _, x := range v { - if x == apiVersion { - return true - } - } - return false -} - -func allKnownVersions() VersionSet { - // We should register the built in extension APIs as well so CRDs are - // supported in the default version set. This has caused problems with `helm - // template` in the past, so let's be safe - apiextensionsv1beta1.AddToScheme(scheme.Scheme) - apiextensionsv1.AddToScheme(scheme.Scheme) - - groups := scheme.Scheme.PrioritizedVersionsAllGroups() - vs := make(VersionSet, 0, len(groups)) - for _, gv := range groups { - vs = append(vs, gv.String()) - } - return vs -} diff --git a/pkg/chartutil/capabilities_test.go b/pkg/chartutil/capabilities_test.go deleted file mode 100644 index 3eeac76..0000000 --- a/pkg/chartutil/capabilities_test.go +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "testing" -) - -func TestVersionSet(t *testing.T) { - vs := VersionSet{"v1", "apps/v1"} - if d := len(vs); d != 2 { - t.Errorf("Expected 2 versions, got %d", d) - } - - if !vs.Has("apps/v1") { - t.Error("Expected to find apps/v1") - } - - if vs.Has("Spanish/inquisition") { - t.Error("No one expects the Spanish/inquisition") - } -} - -func TestDefaultVersionSet(t *testing.T) { - if !DefaultVersionSet.Has("v1") { - t.Error("Expected core v1 version set") - } -} - -func TestDefaultCapabilities(t *testing.T) { - kv := DefaultCapabilities.KubeVersion - if kv.String() != "v1.16.0" { - t.Errorf("Expected default KubeVersion.String() to be v1.16.0, got %q", kv.String()) - } - if kv.Version != "v1.16.0" { - t.Errorf("Expected default KubeVersion.Version to be v1.16.0, got %q", kv.Version) - } - if kv.GitVersion() != "v1.16.0" { - t.Errorf("Expected default KubeVersion.GitVersion() to be v1.16.0, got %q", kv.Version) - } - if kv.Major != "1" { - t.Errorf("Expected default KubeVersion.Major to be 1, got %q", kv.Major) - } - if kv.Minor != "16" { - t.Errorf("Expected default KubeVersion.Minor to be 16, got %q", kv.Minor) - } -} diff --git a/pkg/chartutil/chartfile.go b/pkg/chartutil/chartfile.go deleted file mode 100644 index 808a902..0000000 --- a/pkg/chartutil/chartfile.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "io/ioutil" - "os" - "path/filepath" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart" -) - -// LoadChartfile loads a Chart.yaml file into a *chart.Metadata. -func LoadChartfile(filename string) (*chart.Metadata, error) { - b, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - y := new(chart.Metadata) - err = yaml.Unmarshal(b, y) - return y, err -} - -// SaveChartfile saves the given metadata as a Chart.yaml file at the given path. -// -// 'filename' should be the complete path and filename ('foo/Chart.yaml') -func SaveChartfile(filename string, cf *chart.Metadata) error { - // Pull out the dependencies of a v1 Chart, since there's no way - // to tell the serializer to skip a field for just this use case - savedDependencies := cf.Dependencies - if cf.APIVersion == chart.APIVersionV1 { - cf.Dependencies = nil - } - out, err := yaml.Marshal(cf) - if cf.APIVersion == chart.APIVersionV1 { - cf.Dependencies = savedDependencies - } - if err != nil { - return err - } - return ioutil.WriteFile(filename, out, 0644) -} - -// IsChartDir validate a chart directory. -// -// Checks for a valid Chart.yaml. -func IsChartDir(dirName string) (bool, error) { - if fi, err := os.Stat(dirName); err != nil { - return false, err - } else if !fi.IsDir() { - return false, errors.Errorf("%q is not a directory", dirName) - } - - chartYaml := filepath.Join(dirName, ChartfileName) - if _, err := os.Stat(chartYaml); os.IsNotExist(err) { - return false, errors.Errorf("no %s exists in directory %q", ChartfileName, dirName) - } - - chartYamlContent, err := ioutil.ReadFile(chartYaml) - if err != nil { - return false, errors.Errorf("cannot read %s in directory %q", ChartfileName, dirName) - } - - chartContent := new(chart.Metadata) - if err := yaml.Unmarshal(chartYamlContent, &chartContent); err != nil { - return false, err - } - if chartContent == nil { - return false, errors.Errorf("chart metadata (%s) missing", ChartfileName) - } - if chartContent.Name == "" { - return false, errors.Errorf("invalid chart (%s): name must not be empty", ChartfileName) - } - - return true, nil -} diff --git a/pkg/chartutil/chartfile_test.go b/pkg/chartutil/chartfile_test.go deleted file mode 100644 index fb5f153..0000000 --- a/pkg/chartutil/chartfile_test.go +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "testing" - - "helm.sh/helm/v3/pkg/chart" -) - -const testfile = "testdata/chartfiletest.yaml" - -func TestLoadChartfile(t *testing.T) { - f, err := LoadChartfile(testfile) - if err != nil { - t.Errorf("Failed to open %s: %s", testfile, err) - return - } - verifyChartfile(t, f, "frobnitz") -} - -func verifyChartfile(t *testing.T, f *chart.Metadata, name string) { - - if f == nil { - t.Fatal("Failed verifyChartfile because f is nil") - } - - if f.APIVersion != chart.APIVersionV1 { - t.Errorf("Expected API Version %q, got %q", chart.APIVersionV1, f.APIVersion) - } - - if f.Name != name { - t.Errorf("Expected %s, got %s", name, f.Name) - } - - if f.Description != "This is a frobnitz." { - t.Errorf("Unexpected description %q", f.Description) - } - - if f.Version != "1.2.3" { - t.Errorf("Unexpected version %q", f.Version) - } - - if len(f.Maintainers) != 2 { - t.Errorf("Expected 2 maintainers, got %d", len(f.Maintainers)) - } - - if f.Maintainers[0].Name != "The Helm Team" { - t.Errorf("Unexpected maintainer name.") - } - - if f.Maintainers[1].Email != "nobody@example.com" { - t.Errorf("Unexpected maintainer email.") - } - - if len(f.Sources) != 1 { - t.Fatalf("Unexpected number of sources") - } - - if f.Sources[0] != "https://example.com/foo/bar" { - t.Errorf("Expected https://example.com/foo/bar, got %s", f.Sources) - } - - if f.Home != "http://example.com" { - t.Error("Unexpected home.") - } - - if f.Icon != "https://example.com/64x64.png" { - t.Errorf("Unexpected icon: %q", f.Icon) - } - - if len(f.Keywords) != 3 { - t.Error("Unexpected keywords") - } - - if len(f.Annotations) != 2 { - t.Fatalf("Unexpected annotations") - } - - if want, got := "extravalue", f.Annotations["extrakey"]; want != got { - t.Errorf("Want %q, but got %q", want, got) - } - - if want, got := "anothervalue", f.Annotations["anotherkey"]; want != got { - t.Errorf("Want %q, but got %q", want, got) - } - - kk := []string{"frobnitz", "sprocket", "dodad"} - for i, k := range f.Keywords { - if kk[i] != k { - t.Errorf("Expected %q, got %q", kk[i], k) - } - } -} - -func TestIsChartDir(t *testing.T) { - validChartDir, err := IsChartDir("testdata/frobnitz") - if !validChartDir { - t.Errorf("unexpected error while reading chart-directory: (%v)", err) - return - } - validChartDir, err = IsChartDir("testdata") - if validChartDir || err == nil { - t.Errorf("expected error but did not get any") - return - } -} diff --git a/pkg/chartutil/coalesce.go b/pkg/chartutil/coalesce.go deleted file mode 100644 index bbdd5f2..0000000 --- a/pkg/chartutil/coalesce.go +++ /dev/null @@ -1,205 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "log" - - "github.com/mitchellh/copystructure" - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chart" -) - -// CoalesceValues coalesces all of the values in a chart (and its subcharts). -// -// Values are coalesced together using the following rules: -// -// - Values in a higher level chart always override values in a lower-level -// dependency chart -// - Scalar values and arrays are replaced, maps are merged -// - A chart has access to all of the variables for it, as well as all of -// the values destined for its dependencies. -func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, error) { - // create a copy of vals and then pass it to coalesce - // and coalesceDeps, as both will mutate the passed values - v, err := copystructure.Copy(vals) - if err != nil { - return vals, err - } - - valsCopy := v.(map[string]interface{}) - // if we have an empty map, make sure it is initialized - if valsCopy == nil { - valsCopy = make(map[string]interface{}) - } - if _, err := coalesce(chrt, valsCopy); err != nil { - return valsCopy, err - } - return coalesceDeps(chrt, valsCopy) -} - -// coalesce coalesces the dest values and the chart values, giving priority to the dest values. -// -// This is a helper function for CoalesceValues. -func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) { - coalesceValues(ch, dest) - return coalesceDeps(ch, dest) -} - -// coalesceDeps coalesces the dependencies of the given chart. -func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) { - for _, subchart := range chrt.Dependencies() { - if c, ok := dest[subchart.Name()]; !ok { - // If dest doesn't already have the key, create it. - dest[subchart.Name()] = make(map[string]interface{}) - } else if !istable(c) { - return dest, errors.Errorf("type mismatch on %s: %t", subchart.Name(), c) - } - if dv, ok := dest[subchart.Name()]; ok { - dvmap := dv.(map[string]interface{}) - - // Get globals out of dest and merge them into dvmap. - coalesceGlobals(dvmap, dest) - - // Now coalesce the rest of the values. - var err error - dest[subchart.Name()], err = coalesce(subchart, dvmap) - if err != nil { - return dest, err - } - } - } - return dest, nil -} - -// coalesceGlobals copies the globals out of src and merges them into dest. -// -// For convenience, returns dest. -func coalesceGlobals(dest, src map[string]interface{}) { - var dg, sg map[string]interface{} - - if destglob, ok := dest[GlobalKey]; !ok { - dg = make(map[string]interface{}) - } else if dg, ok = destglob.(map[string]interface{}); !ok { - log.Printf("warning: skipping globals because destination %s is not a table.", GlobalKey) - return - } - - if srcglob, ok := src[GlobalKey]; !ok { - sg = make(map[string]interface{}) - } else if sg, ok = srcglob.(map[string]interface{}); !ok { - log.Printf("warning: skipping globals because source %s is not a table.", GlobalKey) - return - } - - // EXPERIMENTAL: In the past, we have disallowed globals to test tables. This - // reverses that decision. It may somehow be possible to introduce a loop - // here, but I haven't found a way. So for the time being, let's allow - // tables in globals. - for key, val := range sg { - if istable(val) { - vv := copyMap(val.(map[string]interface{})) - if destv, ok := dg[key]; !ok { - // Here there is no merge. We're just adding. - dg[key] = vv - } else { - if destvmap, ok := destv.(map[string]interface{}); !ok { - log.Printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key) - } else { - // Basically, we reverse order of coalesce here to merge - // top-down. - CoalesceTables(vv, destvmap) - dg[key] = vv - continue - } - } - } else if dv, ok := dg[key]; ok && istable(dv) { - // It's not clear if this condition can actually ever trigger. - log.Printf("key %s is table. Skipping", key) - continue - } - // TODO: Do we need to do any additional checking on the value? - dg[key] = val - } - dest[GlobalKey] = dg -} - -func copyMap(src map[string]interface{}) map[string]interface{} { - m := make(map[string]interface{}, len(src)) - for k, v := range src { - m[k] = v - } - return m -} - -// coalesceValues builds up a values map for a particular chart. -// -// Values in v will override the values in the chart. -func coalesceValues(c *chart.Chart, v map[string]interface{}) { - for key, val := range c.Values { - if value, ok := v[key]; ok { - if value == nil { - // When the YAML value is null, we remove the value's key. - // This allows Helm's various sources of values (value files or --set) to - // remove incompatible keys from any previous chart, file, or set values. - delete(v, key) - } else if dest, ok := value.(map[string]interface{}); ok { - // if v[key] is a table, merge nv's val table into v[key]. - src, ok := val.(map[string]interface{}) - if !ok { - log.Printf("warning: skipped value for %s: Not a table.", key) - continue - } - // Because v has higher precedence than nv, dest values override src - // values. - CoalesceTables(dest, src) - } - } else { - // If the key is not in v, copy it from nv. - v[key] = val - } - } -} - -// CoalesceTables merges a source map into a destination map. -// -// dest is considered authoritative. -func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} { - if dst == nil || src == nil { - return src - } - // Because dest has higher precedence than src, dest values override src - // values. - for key, val := range src { - if istable(val) { - switch innerdst, ok := dst[key]; { - case !ok: - dst[key] = val - case istable(innerdst): - CoalesceTables(innerdst.(map[string]interface{}), val.(map[string]interface{})) - default: - log.Printf("warning: cannot overwrite table with non table for %s (%v)", key, val) - } - } else if dv, ok := dst[key]; ok && istable(dv) { - log.Printf("warning: destination for %s is a table. Ignoring non-table value %v", key, val) - } else if !ok { // <- ok is still in scope from preceding conditional. - dst[key] = val - } - } - return dst -} diff --git a/pkg/chartutil/coalesce_test.go b/pkg/chartutil/coalesce_test.go deleted file mode 100644 index 6e82de5..0000000 --- a/pkg/chartutil/coalesce_test.go +++ /dev/null @@ -1,182 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" -) - -// ref: http://www.yaml.org/spec/1.2/spec.html#id2803362 -var testCoalesceValuesYaml = []byte(` -top: yup -bottom: null -right: Null -left: NULL -front: ~ -back: "" - -global: - name: Ishmael - subject: Queequeg - nested: - boat: true - -pequod: - global: - name: Stinky - harpooner: Tashtego - nested: - boat: false - sail: true - ahab: - scope: whale -`) - -func TestCoalesceValues(t *testing.T) { - is := assert.New(t) - c := loadChart(t, "testdata/moby") - - vals, err := ReadValues(testCoalesceValuesYaml) - if err != nil { - t.Fatal(err) - } - - // taking a copy of the values before passing it - // to CoalesceValues as argument, so that we can - // use it for asserting later - valsCopy := make(Values, len(vals)) - for key, value := range vals { - valsCopy[key] = value - } - - v, err := CoalesceValues(c, vals) - if err != nil { - t.Fatal(err) - } - j, _ := json.MarshalIndent(v, "", " ") - t.Logf("Coalesced Values: %s", string(j)) - - tests := []struct { - tpl string - expect string - }{ - {"{{.top}}", "yup"}, - {"{{.back}}", ""}, - {"{{.name}}", "moby"}, - {"{{.global.name}}", "Ishmael"}, - {"{{.global.subject}}", "Queequeg"}, - {"{{.global.harpooner}}", ""}, - {"{{.pequod.name}}", "pequod"}, - {"{{.pequod.ahab.name}}", "ahab"}, - {"{{.pequod.ahab.scope}}", "whale"}, - {"{{.pequod.ahab.global.name}}", "Ishmael"}, - {"{{.pequod.ahab.global.subject}}", "Queequeg"}, - {"{{.pequod.ahab.global.harpooner}}", "Tashtego"}, - {"{{.pequod.global.name}}", "Ishmael"}, - {"{{.pequod.global.subject}}", "Queequeg"}, - {"{{.spouter.global.name}}", "Ishmael"}, - {"{{.spouter.global.harpooner}}", ""}, - - {"{{.global.nested.boat}}", "true"}, - {"{{.pequod.global.nested.boat}}", "true"}, - {"{{.spouter.global.nested.boat}}", "true"}, - {"{{.pequod.global.nested.sail}}", "true"}, - {"{{.spouter.global.nested.sail}}", ""}, - } - - for _, tt := range tests { - if o, err := ttpl(tt.tpl, v); err != nil || o != tt.expect { - t.Errorf("Expected %q to expand to %q, got %q", tt.tpl, tt.expect, o) - } - } - - nullKeys := []string{"bottom", "right", "left", "front"} - for _, nullKey := range nullKeys { - if _, ok := v[nullKey]; ok { - t.Errorf("Expected key %q to be removed, still present", nullKey) - } - } - - // CoalesceValues should not mutate the passed arguments - is.Equal(valsCopy, vals) -} - -func TestCoalesceTables(t *testing.T) { - dst := map[string]interface{}{ - "name": "Ishmael", - "address": map[string]interface{}{ - "street": "123 Spouter Inn Ct.", - "city": "Nantucket", - }, - "details": map[string]interface{}{ - "friends": []string{"Tashtego"}, - }, - "boat": "pequod", - } - src := map[string]interface{}{ - "occupation": "whaler", - "address": map[string]interface{}{ - "state": "MA", - "street": "234 Spouter Inn Ct.", - }, - "details": "empty", - "boat": map[string]interface{}{ - "mast": true, - }, - } - - // What we expect is that anything in dst overrides anything in src, but that - // otherwise the values are coalesced. - CoalesceTables(dst, src) - - if dst["name"] != "Ishmael" { - t.Errorf("Unexpected name: %s", dst["name"]) - } - if dst["occupation"] != "whaler" { - t.Errorf("Unexpected occupation: %s", dst["occupation"]) - } - - addr, ok := dst["address"].(map[string]interface{}) - if !ok { - t.Fatal("Address went away.") - } - - if addr["street"].(string) != "123 Spouter Inn Ct." { - t.Errorf("Unexpected address: %v", addr["street"]) - } - - if addr["city"].(string) != "Nantucket" { - t.Errorf("Unexpected city: %v", addr["city"]) - } - - if addr["state"].(string) != "MA" { - t.Errorf("Unexpected state: %v", addr["state"]) - } - - if det, ok := dst["details"].(map[string]interface{}); !ok { - t.Fatalf("Details is the wrong type: %v", dst["details"]) - } else if _, ok := det["friends"]; !ok { - t.Error("Could not find your friends. Maybe you don't have any. :-(") - } - - if dst["boat"].(string) != "pequod" { - t.Errorf("Expected boat string, got %v", dst["boat"]) - } -} diff --git a/pkg/chartutil/compatible.go b/pkg/chartutil/compatible.go deleted file mode 100644 index f4656c9..0000000 --- a/pkg/chartutil/compatible.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import "github.com/Masterminds/semver/v3" - -// IsCompatibleRange compares a version to a constraint. -// It returns true if the version matches the constraint, and false in all other cases. -func IsCompatibleRange(constraint, ver string) bool { - sv, err := semver.NewVersion(ver) - if err != nil { - return false - } - - c, err := semver.NewConstraint(constraint) - if err != nil { - return false - } - return c.Check(sv) -} diff --git a/pkg/chartutil/compatible_test.go b/pkg/chartutil/compatible_test.go deleted file mode 100644 index df7be61..0000000 --- a/pkg/chartutil/compatible_test.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package version represents the current version of the project. -package chartutil - -import "testing" - -func TestIsCompatibleRange(t *testing.T) { - tests := []struct { - constraint string - ver string - expected bool - }{ - {"v2.0.0-alpha.4", "v2.0.0-alpha.4", true}, - {"v2.0.0-alpha.3", "v2.0.0-alpha.4", false}, - {"v2.0.0", "v2.0.0-alpha.4", false}, - {"v2.0.0-alpha.4", "v2.0.0", false}, - {"~v2.0.0", "v2.0.1", true}, - {"v2", "v2.0.0", true}, - {">2.0.0", "v2.1.1", true}, - {"v2.1.*", "v2.1.1", true}, - } - - for _, tt := range tests { - if IsCompatibleRange(tt.constraint, tt.ver) != tt.expected { - t.Errorf("expected constraint %s to be %v for %s", tt.constraint, tt.expected, tt.ver) - } - } -} diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go deleted file mode 100644 index 496f201..0000000 --- a/pkg/chartutil/create.go +++ /dev/null @@ -1,564 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" -) - -const ( - // ChartfileName is the default Chart file name. - ChartfileName = "Chart.yaml" - // ValuesfileName is the default values file name. - ValuesfileName = "values.yaml" - // SchemafileName is the default values schema file name. - SchemafileName = "values.schema.json" - // TemplatesDir is the relative directory name for templates. - TemplatesDir = "templates" - // ChartsDir is the relative directory name for charts dependencies. - ChartsDir = "charts" - // TemplatesTestsDir is the relative directory name for tests. - TemplatesTestsDir = TemplatesDir + sep + "tests" - // IgnorefileName is the name of the Helm ignore file. - IgnorefileName = ".helmignore" - // IngressFileName is the name of the example ingress file. - IngressFileName = TemplatesDir + sep + "ingress.yaml" - // DeploymentName is the name of the example deployment file. - DeploymentName = TemplatesDir + sep + "deployment.yaml" - // ServiceName is the name of the example service file. - ServiceName = TemplatesDir + sep + "service.yaml" - // ServiceAccountName is the name of the example serviceaccount file. - ServiceAccountName = TemplatesDir + sep + "serviceaccount.yaml" - // NotesName is the name of the example NOTES.txt file. - NotesName = TemplatesDir + sep + "NOTES.txt" - // HelpersName is the name of the example helpers file. - HelpersName = TemplatesDir + sep + "_helpers.tpl" - // TestConnectionName is the name of the example test file. - TestConnectionName = TemplatesTestsDir + sep + "test-connection.yaml" -) - -const sep = string(filepath.Separator) - -const defaultChartfile = `apiVersion: v2 -name: %s -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. -appVersion: 1.16.0 -` - -const defaultValues = `# Default values for %s. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: [] - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} -` - -const defaultIgnore = `# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ -` - -const defaultIngress = `{{- if .Values.ingress.enabled -}} -{{- $fullName := include ".fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include ".labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ . }} - backend: - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} -{{- end }} -` - -const defaultDeployment = `apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include ".fullname" . }} - labels: - {{- include ".labels" . | nindent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - {{- include ".selectorLabels" . | nindent 6 }} - template: - metadata: - labels: - {{- include ".selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include ".serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} -` - -const defaultService = `apiVersion: v1 -kind: Service -metadata: - name: {{ include ".fullname" . }} - labels: - {{- include ".labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include ".selectorLabels" . | nindent 4 }} -` - -const defaultServiceAccount = `{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include ".serviceAccountName" . }} - labels: - {{- include ".labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end -}} -` - -const defaultNotes = `1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include ".fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include ".fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include ".fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include ".name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 -{{- end }} -` - -const defaultHelpers = `{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define ".name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define ".fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define ".chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define ".labels" -}} -helm.sh/chart: {{ include ".chart" . }} -{{ include ".selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Selector labels -*/}} -{{- define ".selectorLabels" -}} -app.kubernetes.io/name: {{ include ".name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define ".serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include ".fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} -` - -const defaultTestConnection = `apiVersion: v1 -kind: Pod -metadata: - name: "{{ include ".fullname" . }}-test-connection" - labels: - {{- include ".labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test-success -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include ".fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never -` - -// CreateFrom creates a new chart, but scaffolds it from the src chart. -func CreateFrom(chartfile *chart.Metadata, dest, src string) error { - schart, err := loader.Load(src) - if err != nil { - return errors.Wrapf(err, "could not load %s", src) - } - - schart.Metadata = chartfile - - var updatedTemplates []*chart.File - - for _, template := range schart.Templates { - newData := transform(string(template.Data), schart.Name()) - updatedTemplates = append(updatedTemplates, &chart.File{Name: template.Name, Data: newData}) - } - - schart.Templates = updatedTemplates - b, err := yaml.Marshal(schart.Values) - if err != nil { - return errors.Wrap(err, "reading values file") - } - - var m map[string]interface{} - if err := yaml.Unmarshal(transform(string(b), schart.Name()), &m); err != nil { - return errors.Wrap(err, "transforming values file") - } - schart.Values = m - - return SaveDir(schart, dest) -} - -// Create creates a new chart in a directory. -// -// Inside of dir, this will create a directory based on the name of -// chartfile.Name. It will then write the Chart.yaml into this directory and -// create the (empty) appropriate directories. -// -// The returned string will point to the newly created directory. It will be -// an absolute path, even if the provided base directory was relative. -// -// If dir does not exist, this will return an error. -// If Chart.yaml or any directories cannot be created, this will return an -// error. In such a case, this will attempt to clean up by removing the -// new chart directory. -func Create(name, dir string) (string, error) { - path, err := filepath.Abs(dir) - if err != nil { - return path, err - } - - if fi, err := os.Stat(path); err != nil { - return path, err - } else if !fi.IsDir() { - return path, errors.Errorf("no such directory %s", path) - } - - cdir := filepath.Join(path, name) - if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() { - return cdir, errors.Errorf("file %s already exists and is not a directory", cdir) - } - - files := []struct { - path string - content []byte - }{ - { - // Chart.yaml - path: filepath.Join(cdir, ChartfileName), - content: []byte(fmt.Sprintf(defaultChartfile, name)), - }, - { - // values.yaml - path: filepath.Join(cdir, ValuesfileName), - content: []byte(fmt.Sprintf(defaultValues, name)), - }, - { - // .helmignore - path: filepath.Join(cdir, IgnorefileName), - content: []byte(defaultIgnore), - }, - { - // ingress.yaml - path: filepath.Join(cdir, IngressFileName), - content: transform(defaultIngress, name), - }, - { - // deployment.yaml - path: filepath.Join(cdir, DeploymentName), - content: transform(defaultDeployment, name), - }, - { - // service.yaml - path: filepath.Join(cdir, ServiceName), - content: transform(defaultService, name), - }, - { - // serviceaccount.yaml - path: filepath.Join(cdir, ServiceAccountName), - content: transform(defaultServiceAccount, name), - }, - { - // NOTES.txt - path: filepath.Join(cdir, NotesName), - content: transform(defaultNotes, name), - }, - { - // _helpers.tpl - path: filepath.Join(cdir, HelpersName), - content: transform(defaultHelpers, name), - }, - { - // test-connection.yaml - path: filepath.Join(cdir, TestConnectionName), - content: transform(defaultTestConnection, name), - }, - } - - for _, file := range files { - if _, err := os.Stat(file.path); err == nil { - // File exists and is okay. Skip it. - continue - } - if err := writeFile(file.path, file.content); err != nil { - return cdir, err - } - } - // Need to add the ChartsDir explicitly as it does not contain any file OOTB - if err := os.MkdirAll(filepath.Join(cdir, ChartsDir), 0755); err != nil { - return cdir, err - } - return cdir, nil -} - -// transform performs a string replacement of the specified source for -// a given key with the replacement string -func transform(src, replacement string) []byte { - return []byte(strings.ReplaceAll(src, "", replacement)) -} - -func writeFile(name string, content []byte) error { - if err := os.MkdirAll(filepath.Dir(name), 0755); err != nil { - return err - } - return ioutil.WriteFile(name, content, 0644) -} diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go deleted file mode 100644 index 82fde58..0000000 --- a/pkg/chartutil/create_test.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" -) - -func TestCreate(t *testing.T) { - tdir, err := ioutil.TempDir("", "helm-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tdir) - - c, err := Create("foo", tdir) - if err != nil { - t.Fatal(err) - } - - dir := filepath.Join(tdir, "foo") - - mychart, err := loader.LoadDir(c) - if err != nil { - t.Fatalf("Failed to load newly created chart %q: %s", c, err) - } - - if mychart.Name() != "foo" { - t.Errorf("Expected name to be 'foo', got %q", mychart.Name()) - } - - for _, f := range []string{ - ChartfileName, - DeploymentName, - HelpersName, - IgnorefileName, - NotesName, - ServiceAccountName, - ServiceName, - TemplatesDir, - TemplatesTestsDir, - TestConnectionName, - ValuesfileName, - } { - if _, err := os.Stat(filepath.Join(dir, f)); err != nil { - t.Errorf("Expected %s file: %s", f, err) - } - } -} - -func TestCreateFrom(t *testing.T) { - tdir, err := ioutil.TempDir("", "helm-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tdir) - - cf := &chart.Metadata{ - APIVersion: chart.APIVersionV1, - Name: "foo", - Version: "0.1.0", - } - srcdir := "./testdata/mariner" - - if err := CreateFrom(cf, tdir, srcdir); err != nil { - t.Fatal(err) - } - - dir := filepath.Join(tdir, "foo") - c := filepath.Join(tdir, cf.Name) - mychart, err := loader.LoadDir(c) - if err != nil { - t.Fatalf("Failed to load newly created chart %q: %s", c, err) - } - - if mychart.Name() != "foo" { - t.Errorf("Expected name to be 'foo', got %q", mychart.Name()) - } - - for _, f := range []string{ - ChartfileName, - ValuesfileName, - filepath.Join(TemplatesDir, "placeholder.tpl"), - } { - if _, err := os.Stat(filepath.Join(dir, f)); err != nil { - t.Errorf("Expected %s file: %s", f, err) - } - } -} diff --git a/pkg/chartutil/dependencies.go b/pkg/chartutil/dependencies.go deleted file mode 100644 index 4b389dc..0000000 --- a/pkg/chartutil/dependencies.go +++ /dev/null @@ -1,274 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "log" - "strings" - - "helm.sh/helm/v3/pkg/chart" -) - -// ProcessDependencies checks through this chart's dependencies, processing accordingly. -func ProcessDependencies(c *chart.Chart, v Values) error { - if err := processDependencyEnabled(c, v, ""); err != nil { - return err - } - return processDependencyImportValues(c) -} - -// processDependencyConditions disables charts based on condition path value in values -func processDependencyConditions(reqs []*chart.Dependency, cvals Values, cpath string) { - if reqs == nil { - return - } - for _, r := range reqs { - for _, c := range strings.Split(strings.TrimSpace(r.Condition), ",") { - if len(c) > 0 { - // retrieve value - vv, err := cvals.PathValue(cpath + c) - if err == nil { - // if not bool, warn - if bv, ok := vv.(bool); ok { - r.Enabled = bv - break - } else { - log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name) - } - } else if _, ok := err.(ErrNoValue); !ok { - // this is a real error - log.Printf("Warning: PathValue returned error %v", err) - } - } - } - } -} - -// processDependencyTags disables charts based on tags in values -func processDependencyTags(reqs []*chart.Dependency, cvals Values) { - if reqs == nil { - return - } - vt, err := cvals.Table("tags") - if err != nil { - return - } - for _, r := range reqs { - var hasTrue, hasFalse bool - for _, k := range r.Tags { - if b, ok := vt[k]; ok { - // if not bool, warn - if bv, ok := b.(bool); ok { - if bv { - hasTrue = true - } else { - hasFalse = true - } - } else { - log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name) - } - } - } - if !hasTrue && hasFalse { - r.Enabled = false - } else if hasTrue || !hasTrue && !hasFalse { - r.Enabled = true - } - } -} - -func getAliasDependency(charts []*chart.Chart, dep *chart.Dependency) *chart.Chart { - for _, c := range charts { - if c == nil { - continue - } - if c.Name() != dep.Name { - continue - } - if !IsCompatibleRange(dep.Version, c.Metadata.Version) { - continue - } - - out := *c - md := *c.Metadata - out.Metadata = &md - - if dep.Alias != "" { - md.Name = dep.Alias - } - return &out - } - return nil -} - -// processDependencyEnabled removes disabled charts from dependencies -func processDependencyEnabled(c *chart.Chart, v map[string]interface{}, path string) error { - if c.Metadata.Dependencies == nil { - return nil - } - - var chartDependencies []*chart.Chart - // If any dependency is not a part of Chart.yaml - // then this should be added to chartDependencies. - // However, if the dependency is already specified in Chart.yaml - // we should not add it, as it would be anyways processed from Chart.yaml - -Loop: - for _, existing := range c.Dependencies() { - for _, req := range c.Metadata.Dependencies { - if existing.Name() == req.Name && IsCompatibleRange(req.Version, existing.Metadata.Version) { - continue Loop - } - } - chartDependencies = append(chartDependencies, existing) - } - - for _, req := range c.Metadata.Dependencies { - if chartDependency := getAliasDependency(c.Dependencies(), req); chartDependency != nil { - chartDependencies = append(chartDependencies, chartDependency) - } - if req.Alias != "" { - req.Name = req.Alias - } - } - c.SetDependencies(chartDependencies...) - - // set all to true - for _, lr := range c.Metadata.Dependencies { - lr.Enabled = true - } - cvals, err := CoalesceValues(c, v) - if err != nil { - return err - } - // flag dependencies as enabled/disabled - processDependencyTags(c.Metadata.Dependencies, cvals) - processDependencyConditions(c.Metadata.Dependencies, cvals, path) - // make a map of charts to remove - rm := map[string]struct{}{} - for _, r := range c.Metadata.Dependencies { - if !r.Enabled { - // remove disabled chart - rm[r.Name] = struct{}{} - } - } - // don't keep disabled charts in new slice - cd := []*chart.Chart{} - copy(cd, c.Dependencies()[:0]) - for _, n := range c.Dependencies() { - if _, ok := rm[n.Metadata.Name]; !ok { - cd = append(cd, n) - } - } - - // recursively call self to process sub dependencies - for _, t := range cd { - subpath := path + t.Metadata.Name + "." - if err := processDependencyEnabled(t, cvals, subpath); err != nil { - return err - } - } - c.SetDependencies(cd...) - - return nil -} - -// pathToMap creates a nested map given a YAML path in dot notation. -func pathToMap(path string, data map[string]interface{}) map[string]interface{} { - if path == "." { - return data - } - return set(parsePath(path), data) -} - -func set(path []string, data map[string]interface{}) map[string]interface{} { - if len(path) == 0 { - return nil - } - cur := data - for i := len(path) - 1; i >= 0; i-- { - cur = map[string]interface{}{path[i]: cur} - } - return cur -} - -// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field. -func processImportValues(c *chart.Chart) error { - if c.Metadata.Dependencies == nil { - return nil - } - // combine chart values and empty config to get Values - cvals, err := CoalesceValues(c, nil) - if err != nil { - return err - } - b := make(map[string]interface{}) - // import values from each dependency if specified in import-values - for _, r := range c.Metadata.Dependencies { - var outiv []interface{} - for _, riv := range r.ImportValues { - switch iv := riv.(type) { - case map[string]interface{}: - child := iv["child"].(string) - parent := iv["parent"].(string) - - outiv = append(outiv, map[string]string{ - "child": child, - "parent": parent, - }) - - // get child table - vv, err := cvals.Table(r.Name + "." + child) - if err != nil { - log.Printf("Warning: ImportValues missing table: %v", err) - continue - } - // create value map from child to be merged into parent - b = CoalesceTables(cvals, pathToMap(parent, vv.AsMap())) - case string: - child := "exports." + iv - outiv = append(outiv, map[string]string{ - "child": child, - "parent": ".", - }) - vm, err := cvals.Table(r.Name + "." + child) - if err != nil { - log.Printf("Warning: ImportValues missing table: %v", err) - continue - } - b = CoalesceTables(b, vm.AsMap()) - } - } - // set our formatted import values - r.ImportValues = outiv - } - - // set the new values - c.Values = CoalesceTables(b, cvals) - - return nil -} - -// processDependencyImportValues imports specified chart values from child to parent. -func processDependencyImportValues(c *chart.Chart) error { - for _, d := range c.Dependencies() { - // recurse - if err := processDependencyImportValues(d); err != nil { - return err - } - } - return processImportValues(c) -} diff --git a/pkg/chartutil/dependencies_test.go b/pkg/chartutil/dependencies_test.go deleted file mode 100644 index ecd6325..0000000 --- a/pkg/chartutil/dependencies_test.go +++ /dev/null @@ -1,377 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package chartutil - -import ( - "os" - "path/filepath" - "sort" - "strconv" - "testing" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" -) - -func loadChart(t *testing.T, path string) *chart.Chart { - t.Helper() - c, err := loader.Load(path) - if err != nil { - t.Fatalf("failed to load testdata: %s", err) - } - return c -} - -func TestLoadDependency(t *testing.T) { - tests := []*chart.Dependency{ - {Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"}, - {Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"}, - } - - check := func(deps []*chart.Dependency) { - if len(deps) != 2 { - t.Errorf("expected 2 dependencies, got %d", len(deps)) - } - for i, tt := range tests { - if deps[i].Name != tt.Name { - t.Errorf("expected dependency named %q, got %q", tt.Name, deps[i].Name) - } - if deps[i].Version != tt.Version { - t.Errorf("expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, deps[i].Version) - } - if deps[i].Repository != tt.Repository { - t.Errorf("expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, deps[i].Repository) - } - } - } - c := loadChart(t, "testdata/frobnitz") - check(c.Metadata.Dependencies) - check(c.Lock.Dependencies) -} - -func TestDependencyEnabled(t *testing.T) { - type M = map[string]interface{} - tests := []struct { - name string - v M - e []string // expected charts including duplicates in alphanumeric order - }{{ - "tags with no effect", - M{"tags": M{"nothinguseful": false}}, - []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb"}, - }, { - "tags disabling a group", - M{"tags": M{"front-end": false}}, - []string{"parentchart"}, - }, { - "tags disabling a group and enabling a different group", - M{"tags": M{"front-end": false, "back-end": true}}, - []string{"parentchart", "parentchart.subchart2", "parentchart.subchart2.subchartb", "parentchart.subchart2.subchartc"}, - }, { - "tags disabling only children, children still enabled since tag front-end=true in values.yaml", - M{"tags": M{"subcharta": false, "subchartb": false}}, - []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb"}, - }, { - "tags disabling all parents/children with additional tag re-enabling a parent", - M{"tags": M{"front-end": false, "subchart1": true, "back-end": false}}, - []string{"parentchart", "parentchart.subchart1"}, - }, { - "conditions enabling the parent charts, but back-end (b, c) is still disabled via values.yaml", - M{"subchart1": M{"enabled": true}, "subchart2": M{"enabled": true}}, - []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb", "parentchart.subchart2"}, - }, { - "conditions disabling the parent charts, effectively disabling children", - M{"subchart1": M{"enabled": false}, "subchart2": M{"enabled": false}}, - []string{"parentchart"}, - }, { - "conditions a child using the second condition path of child's condition", - M{"subchart1": M{"subcharta": M{"enabled": false}}}, - []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subchartb"}, - }, { - "tags enabling a parent/child group with condition disabling one child", - M{"subchart2": M{"subchartc": M{"enabled": false}}, "tags": M{"back-end": true}}, - []string{"parentchart", "parentchart.subchart1", "parentchart.subchart1.subcharta", "parentchart.subchart1.subchartb", "parentchart.subchart2", "parentchart.subchart2.subchartb"}, - }, { - "tags will not enable a child if parent is explicitly disabled with condition", - M{"subchart1": M{"enabled": false}, "tags": M{"front-end": true}}, - []string{"parentchart"}, - }, { - "subcharts with alias also respect conditions", - M{"subchart1": M{"enabled": false}, "subchart2alias": M{"enabled": true, "subchartb": M{"enabled": true}}}, - []string{"parentchart", "parentchart.subchart2alias", "parentchart.subchart2alias.subchartb"}, - }} - - for _, tc := range tests { - c := loadChart(t, "testdata/subpop") - t.Run(tc.name, func(t *testing.T) { - if err := processDependencyEnabled(c, tc.v, ""); err != nil { - t.Fatalf("error processing enabled dependencies %v", err) - } - - names := extractChartNames(c) - if len(names) != len(tc.e) { - t.Fatalf("slice lengths do not match got %v, expected %v", len(names), len(tc.e)) - } - for i := range names { - if names[i] != tc.e[i] { - t.Fatalf("slice values do not match got %v, expected %v", names, tc.e) - } - } - }) - } -} - -// extractCharts recursively searches chart dependencies returning all charts found -func extractChartNames(c *chart.Chart) []string { - var out []string - var fn func(c *chart.Chart) - fn = func(c *chart.Chart) { - out = append(out, c.ChartPath()) - for _, d := range c.Dependencies() { - fn(d) - } - } - fn(c) - sort.Strings(out) - return out -} - -func TestProcessDependencyImportValues(t *testing.T) { - c := loadChart(t, "testdata/subpop") - - e := make(map[string]string) - - e["imported-chart1.SC1bool"] = "true" - e["imported-chart1.SC1float"] = "3.14" - e["imported-chart1.SC1int"] = "100" - e["imported-chart1.SC1string"] = "dollywood" - e["imported-chart1.SC1extra1"] = "11" - e["imported-chart1.SPextra1"] = "helm rocks" - e["imported-chart1.SC1extra1"] = "11" - - e["imported-chartA.SCAbool"] = "false" - e["imported-chartA.SCAfloat"] = "3.1" - e["imported-chartA.SCAint"] = "55" - e["imported-chartA.SCAstring"] = "jabba" - e["imported-chartA.SPextra3"] = "1.337" - e["imported-chartA.SC1extra2"] = "1.337" - e["imported-chartA.SCAnested1.SCAnested2"] = "true" - - e["imported-chartA-B.SCAbool"] = "false" - e["imported-chartA-B.SCAfloat"] = "3.1" - e["imported-chartA-B.SCAint"] = "55" - e["imported-chartA-B.SCAstring"] = "jabba" - - e["imported-chartA-B.SCBbool"] = "true" - e["imported-chartA-B.SCBfloat"] = "7.77" - e["imported-chartA-B.SCBint"] = "33" - e["imported-chartA-B.SCBstring"] = "boba" - e["imported-chartA-B.SPextra5"] = "k8s" - e["imported-chartA-B.SC1extra5"] = "tiller" - - e["overridden-chart1.SC1bool"] = "false" - e["overridden-chart1.SC1float"] = "3.141592" - e["overridden-chart1.SC1int"] = "99" - e["overridden-chart1.SC1string"] = "pollywog" - e["overridden-chart1.SPextra2"] = "42" - - e["overridden-chartA.SCAbool"] = "true" - e["overridden-chartA.SCAfloat"] = "41.3" - e["overridden-chartA.SCAint"] = "808" - e["overridden-chartA.SCAstring"] = "jaberwocky" - e["overridden-chartA.SPextra4"] = "true" - - e["overridden-chartA-B.SCAbool"] = "true" - e["overridden-chartA-B.SCAfloat"] = "41.3" - e["overridden-chartA-B.SCAint"] = "808" - e["overridden-chartA-B.SCAstring"] = "jaberwocky" - e["overridden-chartA-B.SCBbool"] = "false" - e["overridden-chartA-B.SCBfloat"] = "1.99" - e["overridden-chartA-B.SCBint"] = "77" - e["overridden-chartA-B.SCBstring"] = "jango" - e["overridden-chartA-B.SPextra6"] = "111" - e["overridden-chartA-B.SCAextra1"] = "23" - e["overridden-chartA-B.SCBextra1"] = "13" - e["overridden-chartA-B.SC1extra6"] = "77" - - // `exports` style - e["SCBexported1B"] = "1965" - e["SC1extra7"] = "true" - e["SCBexported2A"] = "blaster" - e["global.SC1exported2.all.SC1exported3"] = "SC1expstr" - - if err := processDependencyImportValues(c); err != nil { - t.Fatalf("processing import values dependencies %v", err) - } - cc := Values(c.Values) - for kk, vv := range e { - pv, err := cc.PathValue(kk) - if err != nil { - t.Fatalf("retrieving import values table %v %v", kk, err) - } - - switch pv := pv.(type) { - case float64: - if s := strconv.FormatFloat(pv, 'f', -1, 64); s != vv { - t.Errorf("failed to match imported float value %v with expected %v", s, vv) - } - case bool: - if b := strconv.FormatBool(pv); b != vv { - t.Errorf("failed to match imported bool value %v with expected %v", b, vv) - } - default: - if pv != vv { - t.Errorf("failed to match imported string value %q with expected %q", pv, vv) - } - } - } -} - -func TestGetAliasDependency(t *testing.T) { - c := loadChart(t, "testdata/frobnitz") - req := c.Metadata.Dependencies - - if len(req) == 0 { - t.Fatalf("there are no dependencies to test") - } - - // Success case - aliasChart := getAliasDependency(c.Dependencies(), req[0]) - if aliasChart == nil { - t.Fatalf("failed to get dependency chart for alias %s", req[0].Name) - } - if req[0].Alias != "" { - if aliasChart.Name() != req[0].Alias { - t.Fatalf("dependency chart name should be %s but got %s", req[0].Alias, aliasChart.Name()) - } - } else if aliasChart.Name() != req[0].Name { - t.Fatalf("dependency chart name should be %s but got %s", req[0].Name, aliasChart.Name()) - } - - if req[0].Version != "" { - if !IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) { - t.Fatalf("dependency chart version is not in the compatible range") - } - } - - // Failure case - req[0].Name = "something-else" - if aliasChart := getAliasDependency(c.Dependencies(), req[0]); aliasChart != nil { - t.Fatalf("expected no chart but got %s", aliasChart.Name()) - } - - req[0].Version = "something else which is not in the compatible range" - if IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) { - t.Fatalf("dependency chart version which is not in the compatible range should cause a failure other than a success ") - } -} - -func TestDependentChartAliases(t *testing.T) { - c := loadChart(t, "testdata/dependent-chart-alias") - - if len(c.Dependencies()) != 2 { - t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) - } - - if err := processDependencyEnabled(c, c.Values, ""); err != nil { - t.Fatalf("expected no errors but got %q", err) - } - - if len(c.Dependencies()) != 3 { - t.Fatal("expected alias dependencies to be added") - } - - if len(c.Dependencies()) != len(c.Metadata.Dependencies) { - t.Fatalf("expected number of chart dependencies %d, but got %d", len(c.Metadata.Dependencies), len(c.Dependencies())) - } - // FIXME test for correct aliases -} - -func TestDependentChartWithSubChartsAbsentInDependency(t *testing.T) { - c := loadChart(t, "testdata/dependent-chart-no-requirements-yaml") - - if len(c.Dependencies()) != 2 { - t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) - } - - if err := processDependencyEnabled(c, c.Values, ""); err != nil { - t.Fatalf("expected no errors but got %q", err) - } - - if len(c.Dependencies()) != 2 { - t.Fatal("expected no changes in dependencies") - } -} - -func TestDependentChartWithSubChartsHelmignore(t *testing.T) { - // FIXME what does this test? - loadChart(t, "testdata/dependent-chart-helmignore") -} - -func TestDependentChartsWithSubChartsSymlink(t *testing.T) { - joonix := filepath.Join("testdata", "joonix") - if err := os.Symlink(filepath.Join("..", "..", "frobnitz"), filepath.Join(joonix, "charts", "frobnitz")); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(filepath.Join(joonix, "charts", "frobnitz")) - c := loadChart(t, joonix) - - if c.Name() != "joonix" { - t.Fatalf("unexpected chart name: %s", c.Name()) - } - if n := len(c.Dependencies()); n != 1 { - t.Fatalf("expected 1 dependency for this chart, but got %d", n) - } -} - -func TestDependentChartsWithSubchartsAllSpecifiedInDependency(t *testing.T) { - c := loadChart(t, "testdata/dependent-chart-with-all-in-requirements-yaml") - - if len(c.Dependencies()) != 2 { - t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) - } - - if err := processDependencyEnabled(c, c.Values, ""); err != nil { - t.Fatalf("expected no errors but got %q", err) - } - - if len(c.Dependencies()) != 2 { - t.Fatal("expected no changes in dependencies") - } - - if len(c.Dependencies()) != len(c.Metadata.Dependencies) { - t.Fatalf("expected number of chart dependencies %d, but got %d", len(c.Metadata.Dependencies), len(c.Dependencies())) - } -} - -func TestDependentChartsWithSomeSubchartsSpecifiedInDependency(t *testing.T) { - c := loadChart(t, "testdata/dependent-chart-with-mixed-requirements-yaml") - - if len(c.Dependencies()) != 2 { - t.Fatalf("expected 2 dependencies for this chart, but got %d", len(c.Dependencies())) - } - - if err := processDependencyEnabled(c, c.Values, ""); err != nil { - t.Fatalf("expected no errors but got %q", err) - } - - if len(c.Dependencies()) != 2 { - t.Fatal("expected no changes in dependencies") - } - - if len(c.Metadata.Dependencies) != 1 { - t.Fatalf("expected 1 dependency specified in Chart.yaml, got %d", len(c.Metadata.Dependencies)) - } -} diff --git a/pkg/chartutil/doc.go b/pkg/chartutil/doc.go deleted file mode 100644 index 8f06bcc..0000000 --- a/pkg/chartutil/doc.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package chartutil contains tools for working with charts. - -Charts are described in the chart package (pkg/chart). -This package provides utilities for serializing and deserializing charts. - -A chart can be represented on the file system in one of two ways: - - - As a directory that contains a Chart.yaml file and other chart things. - - As a tarred gzipped file containing a directory that then contains a - Chart.yaml file. - -This package provides utilities for working with those file formats. - -The preferred way of loading a chart is using 'loader.Load`: - - chart, err := loader.Load(filename) - -This will attempt to discover whether the file at 'filename' is a directory or -a chart archive. It will then load accordingly. - -For accepting raw compressed tar file data from an io.Reader, the -'loader.LoadArchive()' will read in the data, uncompress it, and unpack it -into a Chart. - -When creating charts in memory, use the 'helm.sh/helm/pkg/chart' -package directly. -*/ -package chartutil // import "helm.sh/helm/v3/pkg/chartutil" diff --git a/pkg/chartutil/errors.go b/pkg/chartutil/errors.go deleted file mode 100644 index fcdcc27..0000000 --- a/pkg/chartutil/errors.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" -) - -// ErrNoTable indicates that a chart does not have a matching table. -type ErrNoTable struct { - Key string -} - -func (e ErrNoTable) Error() string { return fmt.Sprintf("%q is not a table", e.Key) } - -// ErrNoValue indicates that Values does not contain a key with a value -type ErrNoValue struct { - Key string -} - -func (e ErrNoValue) Error() string { return fmt.Sprintf("%q is not a value", e.Key) } diff --git a/pkg/chartutil/errors_test.go b/pkg/chartutil/errors_test.go deleted file mode 100644 index 3f63e37..0000000 --- a/pkg/chartutil/errors_test.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "testing" -) - -func TestErrorNoTableDoesNotPanic(t *testing.T) { - x := "empty" - - y := ErrNoTable{x} - - t.Logf("error is: %s", y) -} - -func TestErrorNoValueDoesNotPanic(t *testing.T) { - x := "empty" - - y := ErrNoValue{x} - - t.Logf("error is: %s", y) -} diff --git a/pkg/chartutil/expand.go b/pkg/chartutil/expand.go deleted file mode 100644 index 6ad09e4..0000000 --- a/pkg/chartutil/expand.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "io" - "io/ioutil" - "os" - "path/filepath" - - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" -) - -// Expand uncompresses and extracts a chart into the specified directory. -func Expand(dir string, r io.Reader) error { - files, err := loader.LoadArchiveFiles(r) - if err != nil { - return err - } - - // Get the name of the chart - var chartName string - for _, file := range files { - if file.Name == "Chart.yaml" { - ch := &chart.Metadata{} - if err := yaml.Unmarshal(file.Data, ch); err != nil { - return errors.Wrap(err, "cannot load Chart.yaml") - } - chartName = ch.Name - } - } - if chartName == "" { - return errors.New("chart name not specified") - } - - // Find the base directory - chartdir, err := securejoin.SecureJoin(dir, chartName) - if err != nil { - return err - } - - // Copy all files verbatim. We don't parse these files because parsing can remove - // comments. - for _, file := range files { - outpath, err := securejoin.SecureJoin(chartdir, file.Name) - if err != nil { - return err - } - - // Make sure the necessary subdirs get created. - basedir := filepath.Dir(outpath) - if err := os.MkdirAll(basedir, 0755); err != nil { - return err - } - - if err := ioutil.WriteFile(outpath, file.Data, 0644); err != nil { - return err - } - } - - return nil -} - -// ExpandFile expands the src file into the dest directory. -func ExpandFile(dest, src string) error { - h, err := os.Open(src) - if err != nil { - return err - } - defer h.Close() - return Expand(dest, h) -} diff --git a/pkg/chartutil/expand_test.go b/pkg/chartutil/expand_test.go deleted file mode 100644 index 0eb35ae..0000000 --- a/pkg/chartutil/expand_test.go +++ /dev/null @@ -1,134 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" -) - -func TestExpand(t *testing.T) { - dest, err := ioutil.TempDir("", "helm-testing-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dest) - - reader, err := os.Open("testdata/frobnitz-1.2.3.tgz") - if err != nil { - t.Fatal(err) - } - - if err := Expand(dest, reader); err != nil { - t.Fatal(err) - } - - files, err := ioutil.ReadDir(dest) - if err != nil { - t.Fatalf("error reading output directory %s: %s", dest, err) - } - - if len(files) != 1 { - t.Fatalf("expected a single chart directory in output directory %s", dest) - } - - if !files[0].IsDir() { - t.Fatalf("expected a chart directory in output directory %s", dest) - } - - expectedChartPath := filepath.Join(dest, "frobnitz") - fi, err := os.Stat(expectedChartPath) - if err != nil { - t.Fatal(err) - } - if !fi.IsDir() { - t.Fatalf("expected a chart directory at %s", expectedChartPath) - } - - dir, err := os.Open(expectedChartPath) - if err != nil { - t.Fatal(err) - } - - fis, err := dir.Readdir(0) - if err != nil { - t.Fatal(err) - } - - expectLen := 11 - if len(fis) != expectLen { - t.Errorf("Expected %d files, but got %d", expectLen, len(fis)) - } - - for _, fi := range fis { - expect, err := os.Stat(filepath.Join("testdata", "frobnitz", fi.Name())) - if err != nil { - t.Fatal(err) - } - if fi.Size() != expect.Size() { - t.Errorf("Expected %s to have size %d, got %d", fi.Name(), expect.Size(), fi.Size()) - } - } -} - -func TestExpandFile(t *testing.T) { - dest, err := ioutil.TempDir("", "helm-testing-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(dest) - - if err := ExpandFile(dest, "testdata/frobnitz-1.2.3.tgz"); err != nil { - t.Fatal(err) - } - - expectedChartPath := filepath.Join(dest, "frobnitz") - fi, err := os.Stat(expectedChartPath) - if err != nil { - t.Fatal(err) - } - if !fi.IsDir() { - t.Fatalf("expected a chart directory at %s", expectedChartPath) - } - - dir, err := os.Open(expectedChartPath) - if err != nil { - t.Fatal(err) - } - - fis, err := dir.Readdir(0) - if err != nil { - t.Fatal(err) - } - - expectLen := 11 - if len(fis) != expectLen { - t.Errorf("Expected %d files, but got %d", expectLen, len(fis)) - } - - for _, fi := range fis { - expect, err := os.Stat(filepath.Join("testdata", "frobnitz", fi.Name())) - if err != nil { - t.Fatal(err) - } - if fi.Size() != expect.Size() { - t.Errorf("Expected %s to have size %d, got %d", fi.Name(), expect.Size(), fi.Size()) - } - } -} diff --git a/pkg/chartutil/jsonschema.go b/pkg/chartutil/jsonschema.go deleted file mode 100644 index 753dc98..0000000 --- a/pkg/chartutil/jsonschema.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "bytes" - "fmt" - "strings" - - "github.com/pkg/errors" - "github.com/xeipuuv/gojsonschema" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart" -) - -// ValidateAgainstSchema checks that values does not violate the structure laid out in schema -func ValidateAgainstSchema(chrt *chart.Chart, values map[string]interface{}) error { - var sb strings.Builder - if chrt.Schema != nil { - err := ValidateAgainstSingleSchema(values, chrt.Schema) - if err != nil { - sb.WriteString(fmt.Sprintf("%s:\n", chrt.Name())) - sb.WriteString(err.Error()) - } - } - - // For each dependency, recursively call this function with the coalesced values - for _, subchart := range chrt.Dependencies() { - subchartValues := values[subchart.Name()].(map[string]interface{}) - if err := ValidateAgainstSchema(subchart, subchartValues); err != nil { - sb.WriteString(err.Error()) - } - } - - if sb.Len() > 0 { - return errors.New(sb.String()) - } - - return nil -} - -// ValidateAgainstSingleSchema checks that values does not violate the structure laid out in this schema -func ValidateAgainstSingleSchema(values Values, schemaJSON []byte) error { - valuesData, err := yaml.Marshal(values) - if err != nil { - return err - } - valuesJSON, err := yaml.YAMLToJSON(valuesData) - if err != nil { - return err - } - if bytes.Equal(valuesJSON, []byte("null")) { - valuesJSON = []byte("{}") - } - schemaLoader := gojsonschema.NewBytesLoader(schemaJSON) - valuesLoader := gojsonschema.NewBytesLoader(valuesJSON) - - result, err := gojsonschema.Validate(schemaLoader, valuesLoader) - if err != nil { - return err - } - - if !result.Valid() { - var sb strings.Builder - for _, desc := range result.Errors() { - sb.WriteString(fmt.Sprintf("- %s\n", desc)) - } - return errors.New(sb.String()) - } - - return nil -} diff --git a/pkg/chartutil/jsonschema_test.go b/pkg/chartutil/jsonschema_test.go deleted file mode 100644 index 33f0092..0000000 --- a/pkg/chartutil/jsonschema_test.go +++ /dev/null @@ -1,143 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "io/ioutil" - "testing" - - "helm.sh/helm/v3/pkg/chart" -) - -func TestValidateAgainstSingleSchema(t *testing.T) { - values, err := ReadValuesFile("./testdata/test-values.yaml") - if err != nil { - t.Fatalf("Error reading YAML file: %s", err) - } - schema, err := ioutil.ReadFile("./testdata/test-values.schema.json") - if err != nil { - t.Fatalf("Error reading YAML file: %s", err) - } - - if err := ValidateAgainstSingleSchema(values, schema); err != nil { - t.Errorf("Error validating Values against Schema: %s", err) - } -} - -func TestValidateAgainstSingleSchemaNegative(t *testing.T) { - values, err := ReadValuesFile("./testdata/test-values-negative.yaml") - if err != nil { - t.Fatalf("Error reading YAML file: %s", err) - } - schema, err := ioutil.ReadFile("./testdata/test-values.schema.json") - if err != nil { - t.Fatalf("Error reading YAML file: %s", err) - } - - var errString string - if err := ValidateAgainstSingleSchema(values, schema); err == nil { - t.Fatalf("Expected an error, but got nil") - } else { - errString = err.Error() - } - - expectedErrString := `- (root): employmentInfo is required -- age: Must be greater than or equal to 0/1 -` - if errString != expectedErrString { - t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString) - } -} - -const subchartSchema = `{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Values", - "type": "object", - "properties": { - "age": { - "description": "Age", - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "age" - ] -} -` - -func TestValidateAgainstSchema(t *testing.T) { - subchartJSON := []byte(subchartSchema) - subchart := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "subchart", - }, - Schema: subchartJSON, - } - chrt := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "chrt", - }, - } - chrt.AddDependency(subchart) - - vals := map[string]interface{}{ - "name": "John", - "subchart": map[string]interface{}{ - "age": 25, - }, - } - - if err := ValidateAgainstSchema(chrt, vals); err != nil { - t.Errorf("Error validating Values against Schema: %s", err) - } -} - -func TestValidateAgainstSchemaNegative(t *testing.T) { - subchartJSON := []byte(subchartSchema) - subchart := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "subchart", - }, - Schema: subchartJSON, - } - chrt := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "chrt", - }, - } - chrt.AddDependency(subchart) - - vals := map[string]interface{}{ - "name": "John", - "subchart": map[string]interface{}{}, - } - - var errString string - if err := ValidateAgainstSchema(chrt, vals); err == nil { - t.Fatalf("Expected an error, but got nil") - } else { - errString = err.Error() - } - - expectedErrString := `subchart: -- (root): age is required -` - if errString != expectedErrString { - t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString) - } -} diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go deleted file mode 100644 index be0dfdc..0000000 --- a/pkg/chartutil/save.go +++ /dev/null @@ -1,222 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "archive/tar" - "compress/gzip" - "encoding/json" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart" -) - -var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") - -// SaveDir saves a chart as files in a directory. -func SaveDir(c *chart.Chart, dest string) error { - // Create the chart directory - outdir := filepath.Join(dest, c.Name()) - if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() { - return errors.Errorf("file %s already exists and is not a directory", outdir) - } - if err := os.MkdirAll(outdir, 0755); err != nil { - return err - } - - // Save the chart file. - if err := SaveChartfile(filepath.Join(outdir, ChartfileName), c.Metadata); err != nil { - return err - } - - // Save values.yaml - for _, f := range c.Raw { - if f.Name == ValuesfileName { - vf := filepath.Join(outdir, ValuesfileName) - if err := writeFile(vf, f.Data); err != nil { - return err - } - } - } - - // Save values.schema.json if it exists - if c.Schema != nil { - filename := filepath.Join(outdir, SchemafileName) - if err := writeFile(filename, c.Schema); err != nil { - return err - } - } - - // Save templates and files - for _, o := range [][]*chart.File{c.Templates, c.Files} { - for _, f := range o { - n := filepath.Join(outdir, f.Name) - if err := writeFile(n, f.Data); err != nil { - return err - } - } - } - - // Save dependencies - base := filepath.Join(outdir, ChartsDir) - for _, dep := range c.Dependencies() { - // Here, we write each dependency as a tar file. - if _, err := Save(dep, base); err != nil { - return errors.Wrapf(err, "saving %s", dep.ChartFullPath()) - } - } - return nil -} - -// Save creates an archived chart to the given directory. -// -// This takes an existing chart and a destination directory. -// -// If the directory is /foo, and the chart is named bar, with version 1.0.0, this -// will generate /foo/bar-1.0.0.tgz. -// -// This returns the absolute path to the chart archive file. -func Save(c *chart.Chart, outDir string) (string, error) { - if err := c.Validate(); err != nil { - return "", errors.Wrap(err, "chart validation") - } - - filename := fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version) - filename = filepath.Join(outDir, filename) - if stat, err := os.Stat(filepath.Dir(filename)); os.IsNotExist(err) { - if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { - return "", err - } - } else if !stat.IsDir() { - return "", errors.Errorf("is not a directory: %s", filepath.Dir(filename)) - } - - f, err := os.Create(filename) - if err != nil { - return "", err - } - - // Wrap in gzip writer - zipper := gzip.NewWriter(f) - zipper.Header.Extra = headerBytes - zipper.Header.Comment = "Helm" - - // Wrap in tar writer - twriter := tar.NewWriter(zipper) - rollback := false - defer func() { - twriter.Close() - zipper.Close() - f.Close() - if rollback { - os.Remove(filename) - } - }() - - if err := writeTarContents(twriter, c, ""); err != nil { - rollback = true - return filename, err - } - return filename, nil -} - -func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { - base := filepath.Join(prefix, c.Name()) - - // Pull out the dependencies of a v1 Chart, since there's no way - // to tell the serializer to skip a field for just this use case - savedDependencies := c.Metadata.Dependencies - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Metadata.Dependencies = nil - } - // Save Chart.yaml - cdata, err := yaml.Marshal(c.Metadata) - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Metadata.Dependencies = savedDependencies - } - if err != nil { - return err - } - if err := writeToTar(out, filepath.Join(base, ChartfileName), cdata); err != nil { - return err - } - - // Save values.yaml - for _, f := range c.Raw { - if f.Name == ValuesfileName { - if err := writeToTar(out, filepath.Join(base, ValuesfileName), f.Data); err != nil { - return err - } - } - } - - // Save values.schema.json if it exists - if c.Schema != nil { - if !json.Valid(c.Schema) { - return errors.New("Invalid JSON in " + SchemafileName) - } - if err := writeToTar(out, filepath.Join(base, SchemafileName), c.Schema); err != nil { - return err - } - } - - // Save templates - for _, f := range c.Templates { - n := filepath.Join(base, f.Name) - if err := writeToTar(out, n, f.Data); err != nil { - return err - } - } - - // Save files - for _, f := range c.Files { - n := filepath.Join(base, f.Name) - if err := writeToTar(out, n, f.Data); err != nil { - return err - } - } - - // Save dependencies - for _, dep := range c.Dependencies() { - if err := writeTarContents(out, dep, filepath.Join(base, ChartsDir)); err != nil { - return err - } - } - return nil -} - -// writeToTar writes a single file to a tar archive. -func writeToTar(out *tar.Writer, name string, body []byte) error { - // TODO: Do we need to create dummy parent directory names if none exist? - h := &tar.Header{ - Name: name, - Mode: 0644, - Size: int64(len(body)), - ModTime: time.Now(), - } - if err := out.WriteHeader(h); err != nil { - return err - } - _, err := out.Write(body) - return err -} diff --git a/pkg/chartutil/save_test.go b/pkg/chartutil/save_test.go deleted file mode 100644 index f367d42..0000000 --- a/pkg/chartutil/save_test.go +++ /dev/null @@ -1,226 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "io" - "io/ioutil" - "os" - "path" - "path/filepath" - "regexp" - "strings" - "testing" - "time" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" -) - -func TestSave(t *testing.T) { - tmp, err := ioutil.TempDir("", "helm-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - for _, dest := range []string{tmp, path.Join(tmp, "newdir")} { - t.Run("outDir="+dest, func(t *testing.T) { - c := &chart.Chart{ - Metadata: &chart.Metadata{ - APIVersion: chart.APIVersionV1, - Name: "ahab", - Version: "1.2.3", - }, - Files: []*chart.File{ - {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, - }, - Schema: []byte("{\n \"title\": \"Values\"\n}"), - } - chartWithInvalidJSON := withSchema(*c, []byte("{")) - - where, err := Save(c, dest) - if err != nil { - t.Fatalf("Failed to save: %s", err) - } - if !strings.HasPrefix(where, dest) { - t.Fatalf("Expected %q to start with %q", where, dest) - } - if !strings.HasSuffix(where, ".tgz") { - t.Fatalf("Expected %q to end with .tgz", where) - } - - c2, err := loader.LoadFile(where) - if err != nil { - t.Fatal(err) - } - if c2.Name() != c.Name() { - t.Fatalf("Expected chart archive to have %q, got %q", c.Name(), c2.Name()) - } - if len(c2.Files) != 1 || c2.Files[0].Name != "scheherazade/shahryar.txt" { - t.Fatal("Files data did not match") - } - - if !bytes.Equal(c.Schema, c2.Schema) { - indentation := 4 - formattedExpected := Indent(indentation, string(c.Schema)) - formattedActual := Indent(indentation, string(c2.Schema)) - t.Fatalf("Schema data did not match.\nExpected:\n%s\nActual:\n%s", formattedExpected, formattedActual) - } - if _, err := Save(&chartWithInvalidJSON, dest); err == nil { - t.Fatalf("Invalid JSON was not caught while saving chart") - } - }) - } -} - -// Creates a copy with a different schema; does not modify anything. -func withSchema(chart chart.Chart, schema []byte) chart.Chart { - chart.Schema = schema - return chart -} - -func Indent(n int, text string) string { - startOfLine := regexp.MustCompile(`(?m)^`) - indentation := strings.Repeat(" ", n) - return startOfLine.ReplaceAllLiteralString(text, indentation) -} - -func TestSavePreservesTimestamps(t *testing.T) { - // Test executes so quickly that if we don't subtract a second, the - // check will fail because `initialCreateTime` will be identical to the - // written timestamp for the files. - initialCreateTime := time.Now().Add(-1 * time.Second) - - tmp, err := ioutil.TempDir("", "helm-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - c := &chart.Chart{ - Metadata: &chart.Metadata{ - APIVersion: chart.APIVersionV1, - Name: "ahab", - Version: "1.2.3.4", - }, - Values: map[string]interface{}{ - "imageName": "testimage", - "imageId": 42, - }, - Files: []*chart.File{ - {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, - }, - Schema: []byte("{\n \"title\": \"Values\"\n}"), - } - - where, err := Save(c, tmp) - if err != nil { - t.Fatalf("Failed to save: %s", err) - } - - allHeaders, err := retrieveAllHeadersFromTar(where) - if err != nil { - t.Fatalf("Failed to parse tar: %v", err) - } - - for _, header := range allHeaders { - if header.ModTime.Before(initialCreateTime) { - t.Fatalf("File timestamp not preserved: %v", header.ModTime) - } - } -} - -// We could refactor `load.go` to use this `retrieveAllHeadersFromTar` function -// as well, so we are not duplicating components of the code which iterate -// through the tar. -func retrieveAllHeadersFromTar(path string) ([]*tar.Header, error) { - raw, err := os.Open(path) - if err != nil { - return nil, err - } - defer raw.Close() - - unzipped, err := gzip.NewReader(raw) - if err != nil { - return nil, err - } - defer unzipped.Close() - - tr := tar.NewReader(unzipped) - headers := []*tar.Header{} - for { - hd, err := tr.Next() - if err == io.EOF { - break - } - - if err != nil { - return nil, err - } - - headers = append(headers, hd) - } - - return headers, nil -} - -func TestSaveDir(t *testing.T) { - tmp, err := ioutil.TempDir("", "helm-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmp) - - c := &chart.Chart{ - Metadata: &chart.Metadata{ - APIVersion: chart.APIVersionV1, - Name: "ahab", - Version: "1.2.3", - }, - Files: []*chart.File{ - {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, - }, - Templates: []*chart.File{ - {Name: filepath.Join(TemplatesDir, "nested", "dir", "thing.yaml"), Data: []byte("abc: {{ .Values.abc }}")}, - }, - } - - if err := SaveDir(c, tmp); err != nil { - t.Fatalf("Failed to save: %s", err) - } - - c2, err := loader.LoadDir(tmp + "/ahab") - if err != nil { - t.Fatal(err) - } - - if c2.Name() != c.Name() { - t.Fatalf("Expected chart archive to have %q, got %q", c.Name(), c2.Name()) - } - - if len(c2.Templates) != 1 || c2.Templates[0].Name != filepath.Join(TemplatesDir, "nested", "dir", "thing.yaml") { - t.Fatal("Templates data did not match") - } - - if len(c2.Files) != 1 || c2.Files[0].Name != "scheherazade/shahryar.txt" { - t.Fatal("Files data did not match") - } -} diff --git a/pkg/chartutil/testdata/albatross/Chart.yaml b/pkg/chartutil/testdata/albatross/Chart.yaml deleted file mode 100644 index b5188fd..0000000 --- a/pkg/chartutil/testdata/albatross/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: albatross -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chartutil/testdata/albatross/values.yaml b/pkg/chartutil/testdata/albatross/values.yaml deleted file mode 100644 index 3121cd7..0000000 --- a/pkg/chartutil/testdata/albatross/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -albatross: "true" - -global: - author: Coleridge diff --git a/pkg/chartutil/testdata/chartfiletest.yaml b/pkg/chartutil/testdata/chartfiletest.yaml deleted file mode 100644 index 134cd11..0000000 --- a/pkg/chartutil/testdata/chartfiletest.yaml +++ /dev/null @@ -1,20 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -annotations: - extrakey: extravalue - anotherkey: anothervalue diff --git a/pkg/chartutil/testdata/coleridge.yaml b/pkg/chartutil/testdata/coleridge.yaml deleted file mode 100644 index b657962..0000000 --- a/pkg/chartutil/testdata/coleridge.yaml +++ /dev/null @@ -1,12 +0,0 @@ -poet: "Coleridge" -title: "Rime of the Ancient Mariner" -stanza: ["at", "length", "did", "cross", "an", "Albatross"] - -mariner: - with: "crossbow" - shot: "ALBATROSS" - -water: - water: - where: "everywhere" - nor: "any drop to drink" diff --git a/pkg/chartutil/testdata/dependent-chart-alias/.helmignore b/pkg/chartutil/testdata/dependent-chart-alias/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chartutil/testdata/dependent-chart-alias/Chart.lock b/pkg/chartutil/testdata/dependent-chart-alias/Chart.lock deleted file mode 100644 index 6fcc2ed..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/Chart.lock +++ /dev/null @@ -1,8 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts -digest: invalid diff --git a/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml deleted file mode 100644 index 751a3aa..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts - alias: mariners2 - - name: mariner - version: "4.3.2" - repository: https://example.com/charts - alias: mariners1 diff --git a/pkg/chartutil/testdata/dependent-chart-alias/INSTALL.txt b/pkg/chartutil/testdata/dependent-chart-alias/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/LICENSE b/pkg/chartutil/testdata/dependent-chart-alias/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/README.md b/pkg/chartutil/testdata/dependent-chart-alias/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/_ignore_me b/pkg/chartutil/testdata/dependent-chart-alias/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/README.md b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 5bbae10..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/values.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz deleted file mode 100644 index 3190136..0000000 Binary files a/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/dependent-chart-alias/docs/README.md b/pkg/chartutil/testdata/dependent-chart-alias/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/icon.svg b/pkg/chartutil/testdata/dependent-chart-alias/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chartutil/testdata/dependent-chart-alias/ignore/me.txt b/pkg/chartutil/testdata/dependent-chart-alias/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chartutil/testdata/dependent-chart-alias/templates/template.tpl b/pkg/chartutil/testdata/dependent-chart-alias/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chartutil/testdata/dependent-chart-alias/values.yaml b/pkg/chartutil/testdata/dependent-chart-alias/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chartutil/testdata/dependent-chart-alias/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/.helmignore b/pkg/chartutil/testdata/dependent-chart-helmignore/.helmignore deleted file mode 100644 index 8a71bc8..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/.helmignore +++ /dev/null @@ -1,2 +0,0 @@ -ignore/ -.* diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-helmignore/Chart.yaml deleted file mode 100644 index 7c071c2..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/Chart.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/.ignore_me b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/.ignore_me deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/_ignore_me b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/README.md b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/values.yaml b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/templates/alpine-pod.yaml b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 5bbae10..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/values.yaml b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/templates/template.tpl b/pkg/chartutil/testdata/dependent-chart-helmignore/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/values.yaml b/pkg/chartutil/testdata/dependent-chart-helmignore/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/.helmignore b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/Chart.yaml deleted file mode 100644 index 7c071c2..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/Chart.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/INSTALL.txt b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/LICENSE b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/README.md b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/_ignore_me b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/README.md b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/values.yaml b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/templates/alpine-pod.yaml b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 5bbae10..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/values.yaml b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz deleted file mode 100644 index 3190136..0000000 Binary files a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/docs/README.md b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/icon.svg b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/ignore/me.txt b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/templates/template.tpl b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/values.yaml b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/.helmignore b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml deleted file mode 100644 index fe7a996..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/INSTALL.txt b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/README.md b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/_ignore_me b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/README.md b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/values.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/templates/alpine-pod.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 5bbae10..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/values.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz deleted file mode 100644 index 3190136..0000000 Binary files a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/docs/README.md b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/icon.svg b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/ignore/me.txt b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/templates/template.tpl b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/values.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/.helmignore b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml deleted file mode 100644 index 7fc39e2..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/Chart.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/INSTALL.txt b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/README.md b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/_ignore_me b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/README.md b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/values.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/templates/alpine-pod.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 5bbae10..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/values.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz deleted file mode 100644 index 3190136..0000000 Binary files a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/docs/README.md b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/icon.svg b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/ignore/me.txt b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/templates/template.tpl b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/values.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz b/pkg/chartutil/testdata/frobnitz-1.2.3.tgz deleted file mode 100644 index 8731dce..0000000 Binary files a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/frobnitz/.helmignore b/pkg/chartutil/testdata/frobnitz/.helmignore deleted file mode 100644 index 9973a57..0000000 --- a/pkg/chartutil/testdata/frobnitz/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chartutil/testdata/frobnitz/Chart.lock b/pkg/chartutil/testdata/frobnitz/Chart.lock deleted file mode 100644 index 6fcc2ed..0000000 --- a/pkg/chartutil/testdata/frobnitz/Chart.lock +++ /dev/null @@ -1,8 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts -digest: invalid diff --git a/pkg/chartutil/testdata/frobnitz/Chart.yaml b/pkg/chartutil/testdata/frobnitz/Chart.yaml deleted file mode 100644 index fcd4a4a..0000000 --- a/pkg/chartutil/testdata/frobnitz/Chart.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -name: frobnitz -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -annotations: - extrakey: extravalue - anotherkey: anothervalue -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts diff --git a/pkg/chartutil/testdata/frobnitz/INSTALL.txt b/pkg/chartutil/testdata/frobnitz/INSTALL.txt deleted file mode 100644 index 2010438..0000000 --- a/pkg/chartutil/testdata/frobnitz/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chartutil/testdata/frobnitz/LICENSE b/pkg/chartutil/testdata/frobnitz/LICENSE deleted file mode 100644 index 6121943..0000000 --- a/pkg/chartutil/testdata/frobnitz/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chartutil/testdata/frobnitz/README.md b/pkg/chartutil/testdata/frobnitz/README.md deleted file mode 100644 index 8cf4cc3..0000000 --- a/pkg/chartutil/testdata/frobnitz/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chartutil/testdata/frobnitz/charts/_ignore_me b/pkg/chartutil/testdata/frobnitz/charts/_ignore_me deleted file mode 100644 index 2cecca6..0000000 --- a/pkg/chartutil/testdata/frobnitz/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/frobnitz/charts/alpine/Chart.yaml deleted file mode 100644 index 79e0d65..0000000 --- a/pkg/chartutil/testdata/frobnitz/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/README.md b/pkg/chartutil/testdata/frobnitz/charts/alpine/README.md deleted file mode 100644 index b30b949..0000000 --- a/pkg/chartutil/testdata/frobnitz/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100644 index 1c9dd5f..0000000 --- a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml b/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml deleted file mode 100644 index 42c39c2..0000000 --- a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100644 index 61cb620..0000000 Binary files a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml b/pkg/chartutil/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 5bbae10..0000000 --- a/pkg/chartutil/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/values.yaml b/pkg/chartutil/testdata/frobnitz/charts/alpine/values.yaml deleted file mode 100644 index 6c2aab7..0000000 --- a/pkg/chartutil/testdata/frobnitz/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz deleted file mode 100644 index 5648f6f..0000000 Binary files a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/frobnitz/docs/README.md b/pkg/chartutil/testdata/frobnitz/docs/README.md deleted file mode 100644 index d40747c..0000000 --- a/pkg/chartutil/testdata/frobnitz/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chartutil/testdata/frobnitz/icon.svg b/pkg/chartutil/testdata/frobnitz/icon.svg deleted file mode 100644 index 8921306..0000000 --- a/pkg/chartutil/testdata/frobnitz/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chartutil/testdata/frobnitz/ignore/me.txt b/pkg/chartutil/testdata/frobnitz/ignore/me.txt deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chartutil/testdata/frobnitz/templates/template.tpl b/pkg/chartutil/testdata/frobnitz/templates/template.tpl deleted file mode 100644 index c651ee6..0000000 --- a/pkg/chartutil/testdata/frobnitz/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chartutil/testdata/frobnitz/values.yaml b/pkg/chartutil/testdata/frobnitz/values.yaml deleted file mode 100644 index 61f5012..0000000 --- a/pkg/chartutil/testdata/frobnitz/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz b/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz deleted file mode 100644 index 6929659..0000000 Binary files a/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/frobnitz_backslash/.helmignore b/pkg/chartutil/testdata/frobnitz_backslash/.helmignore deleted file mode 100755 index 9973a57..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/.helmignore +++ /dev/null @@ -1 +0,0 @@ -ignore/ diff --git a/pkg/chartutil/testdata/frobnitz_backslash/Chart.lock b/pkg/chartutil/testdata/frobnitz_backslash/Chart.lock deleted file mode 100755 index 6fcc2ed..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/Chart.lock +++ /dev/null @@ -1,8 +0,0 @@ -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts -digest: invalid diff --git a/pkg/chartutil/testdata/frobnitz_backslash/Chart.yaml b/pkg/chartutil/testdata/frobnitz_backslash/Chart.yaml deleted file mode 100755 index b1dd40a..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/Chart.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: v1 -name: frobnitz_backslash -description: This is a frobnitz. -version: "1.2.3" -keywords: - - frobnitz - - sprocket - - dodad -maintainers: - - name: The Helm Team - email: helm@example.com - - name: Someone Else - email: nobody@example.com -sources: - - https://example.com/foo/bar -home: http://example.com -icon: https://example.com/64x64.png -annotations: - extrakey: extravalue - anotherkey: anothervalue -dependencies: - - name: alpine - version: "0.1.0" - repository: https://example.com/charts - - name: mariner - version: "4.3.2" - repository: https://example.com/charts diff --git a/pkg/chartutil/testdata/frobnitz_backslash/INSTALL.txt b/pkg/chartutil/testdata/frobnitz_backslash/INSTALL.txt deleted file mode 100755 index 2010438..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/INSTALL.txt +++ /dev/null @@ -1 +0,0 @@ -This is an install document. The client may display this. diff --git a/pkg/chartutil/testdata/frobnitz_backslash/LICENSE b/pkg/chartutil/testdata/frobnitz_backslash/LICENSE deleted file mode 100755 index 6121943..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/LICENSE +++ /dev/null @@ -1 +0,0 @@ -LICENSE placeholder. diff --git a/pkg/chartutil/testdata/frobnitz_backslash/README.md b/pkg/chartutil/testdata/frobnitz_backslash/README.md deleted file mode 100755 index 8cf4cc3..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Frobnitz - -This is an example chart. - -## Usage - -This is an example. It has no usage. - -## Development - -For developer info, see the top-level repository. diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/_ignore_me b/pkg/chartutil/testdata/frobnitz_backslash/charts/_ignore_me deleted file mode 100755 index 2cecca6..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/_ignore_me +++ /dev/null @@ -1 +0,0 @@ -This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/Chart.yaml deleted file mode 100755 index 79e0d65..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: alpine -description: Deploy a basic Alpine Linux pod -version: 0.1.0 -home: https://helm.sh/helm diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/README.md b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/README.md deleted file mode 100755 index b30b949..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.toml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml deleted file mode 100755 index 1c9dd5f..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: mast1 -description: A Helm chart for Kubernetes -version: 0.1.0 -home: "" diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml deleted file mode 100755 index 42c39c2..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for mast1. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name = "value" diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz deleted file mode 100755 index 61cb620..0000000 Binary files a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml deleted file mode 100755 index 5bbae10..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/values.yaml b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/values.yaml deleted file mode 100755 index 6c2aab7..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: "my-alpine" diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz deleted file mode 100755 index 5648f6f..0000000 Binary files a/pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/frobnitz_backslash/docs/README.md b/pkg/chartutil/testdata/frobnitz_backslash/docs/README.md deleted file mode 100755 index d40747c..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -This is a placeholder for documentation. diff --git a/pkg/chartutil/testdata/frobnitz_backslash/icon.svg b/pkg/chartutil/testdata/frobnitz_backslash/icon.svg deleted file mode 100755 index 8921306..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - Example icon - - - diff --git a/pkg/chartutil/testdata/frobnitz_backslash/ignore/me.txt b/pkg/chartutil/testdata/frobnitz_backslash/ignore/me.txt deleted file mode 100755 index e69de29..0000000 diff --git a/pkg/chartutil/testdata/frobnitz_backslash/templates/template.tpl b/pkg/chartutil/testdata/frobnitz_backslash/templates/template.tpl deleted file mode 100755 index c651ee6..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/templates/template.tpl +++ /dev/null @@ -1 +0,0 @@ -Hello {{.Name | default "world"}} diff --git a/pkg/chartutil/testdata/frobnitz_backslash/values.yaml b/pkg/chartutil/testdata/frobnitz_backslash/values.yaml deleted file mode 100755 index 61f5012..0000000 --- a/pkg/chartutil/testdata/frobnitz_backslash/values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name: "Some Name" - -section: - name: "Name in a section" diff --git a/pkg/chartutil/testdata/genfrob.sh b/pkg/chartutil/testdata/genfrob.sh deleted file mode 100755 index 35fdd59..0000000 --- a/pkg/chartutil/testdata/genfrob.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -# Pack the albatross chart into the mariner chart. -echo "Packing albatross into mariner" -tar -zcvf mariner/charts/albatross-0.1.0.tgz albatross - -echo "Packing mariner into frobnitz" -tar -zcvf frobnitz/charts/mariner-4.3.2.tgz mariner -tar -zcvf frobnitz_backslash/charts/mariner-4.3.2.tgz mariner - -# Pack the frobnitz chart. -echo "Packing frobnitz" -tar --exclude=ignore/* -zcvf frobnitz-1.2.3.tgz frobnitz -tar --exclude=ignore/* -zcvf frobnitz_backslash-1.2.3.tgz frobnitz_backslash diff --git a/pkg/chartutil/testdata/joonix/Chart.yaml b/pkg/chartutil/testdata/joonix/Chart.yaml deleted file mode 100644 index c3464c5..0000000 --- a/pkg/chartutil/testdata/joonix/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: joonix -version: 1.2.3 diff --git a/pkg/chartutil/testdata/joonix/charts/.gitkeep b/pkg/chartutil/testdata/joonix/charts/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/chartutil/testdata/mariner/Chart.yaml b/pkg/chartutil/testdata/mariner/Chart.yaml deleted file mode 100644 index 92dc4b3..0000000 --- a/pkg/chartutil/testdata/mariner/Chart.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -name: mariner -description: A Helm chart for Kubernetes -version: 4.3.2 -home: "" -dependencies: - - name: albatross - repository: https://example.com/mariner/charts - version: "0.1.0" diff --git a/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz b/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz deleted file mode 100644 index 22c1fe5..0000000 Binary files a/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz and /dev/null differ diff --git a/pkg/chartutil/testdata/mariner/templates/placeholder.tpl b/pkg/chartutil/testdata/mariner/templates/placeholder.tpl deleted file mode 100644 index 29c1184..0000000 --- a/pkg/chartutil/testdata/mariner/templates/placeholder.tpl +++ /dev/null @@ -1 +0,0 @@ -# This is a placeholder. diff --git a/pkg/chartutil/testdata/mariner/values.yaml b/pkg/chartutil/testdata/mariner/values.yaml deleted file mode 100644 index b0ccb00..0000000 --- a/pkg/chartutil/testdata/mariner/values.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# Default values for . -# This is a YAML-formatted file. https://github.com/toml-lang/toml -# Declare name/value pairs to be passed into your templates. -# name: "value" - -: - test: true diff --git a/pkg/chartutil/testdata/moby/Chart.yaml b/pkg/chartutil/testdata/moby/Chart.yaml deleted file mode 100644 index a5f992c..0000000 --- a/pkg/chartutil/testdata/moby/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: moby -version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml b/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml deleted file mode 100644 index f1a8ef7..0000000 --- a/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: pequod -version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml deleted file mode 100644 index a7ee7bf..0000000 --- a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: ahab -version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml deleted file mode 100644 index 86c3f63..0000000 --- a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -scope: ahab -name: ahab diff --git a/pkg/chartutil/testdata/moby/charts/pequod/values.yaml b/pkg/chartutil/testdata/moby/charts/pequod/values.yaml deleted file mode 100644 index d6e34b2..0000000 --- a/pkg/chartutil/testdata/moby/charts/pequod/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -scope: pequod -name: pequod diff --git a/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml b/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml deleted file mode 100644 index 0525085..0000000 --- a/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: spouter -version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/spouter/values.yaml b/pkg/chartutil/testdata/moby/charts/spouter/values.yaml deleted file mode 100644 index f71d92a..0000000 --- a/pkg/chartutil/testdata/moby/charts/spouter/values.yaml +++ /dev/null @@ -1 +0,0 @@ -scope: spouter diff --git a/pkg/chartutil/testdata/moby/values.yaml b/pkg/chartutil/testdata/moby/values.yaml deleted file mode 100644 index 54e1ce4..0000000 --- a/pkg/chartutil/testdata/moby/values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -scope: moby -name: moby -override: bad -top: nope -bottom: exists -right: exists -left: exists -front: exists -back: exists diff --git a/pkg/chartutil/testdata/subpop/Chart.yaml b/pkg/chartutil/testdata/subpop/Chart.yaml deleted file mode 100644 index 2711867..0000000 --- a/pkg/chartutil/testdata/subpop/Chart.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: parentchart -version: 0.1.0 -dependencies: - - name: subchart1 - repository: http://localhost:10191 - version: 0.1.0 - condition: subchart1.enabled - tags: - - front-end - - subchart1 - import-values: - - child: SC1data - parent: imported-chart1 - - child: SC1data - parent: overridden-chart1 - - child: imported-chartA - parent: imported-chartA - - child: imported-chartA-B - parent: imported-chartA-B - - child: overridden-chartA-B - parent: overridden-chartA-B - - child: SCBexported1A - parent: . - - SCBexported2 - - SC1exported1 - - - name: subchart2 - repository: http://localhost:10191 - version: 0.1.0 - condition: subchart2.enabled - tags: - - back-end - - subchart2 - - - name: subchart2 - alias: subchart2alias - repository: http://localhost:10191 - version: 0.1.0 - condition: subchart2alias.enabled diff --git a/pkg/chartutil/testdata/subpop/README.md b/pkg/chartutil/testdata/subpop/README.md deleted file mode 100644 index e43fbfe..0000000 --- a/pkg/chartutil/testdata/subpop/README.md +++ /dev/null @@ -1,18 +0,0 @@ -## Subpop - -This chart is for testing the processing of enabled/disabled charts -via conditions and tags. - -Currently there are three levels: - -```` -parent --1 tags: front-end, subchart1 ---A tags: front-end, subchartA ---B tags: front-end, subchartB --2 tags: back-end, subchart2 ---B tags: back-end, subchartB ---C tags: back-end, subchartC -```` - -Tags and conditions are currently in requirements.yaml files. \ No newline at end of file diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/Chart.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/Chart.yaml deleted file mode 100644 index 9d8c03e..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/Chart.yaml +++ /dev/null @@ -1,36 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: subchart1 -version: 0.1.0 -dependencies: - - name: subcharta - repository: http://localhost:10191 - version: 0.1.0 - condition: subcharta.enabled - tags: - - front-end - - subcharta - import-values: - - child: SCAdata - parent: imported-chartA - - child: SCAdata - parent: overridden-chartA - - child: SCAdata - parent: imported-chartA-B - - - name: subchartb - repository: http://localhost:10191 - version: 0.1.0 - condition: subchartb.enabled - import-values: - - child: SCBdata - parent: imported-chartB - - child: SCBdata - parent: imported-chartA-B - - child: exports.SCBexported2 - parent: exports.SCBexported2 - - SCBexported1 - - tags: - - front-end - - subchartb diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/Chart.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/Chart.yaml deleted file mode 100644 index be3edce..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: subcharta -version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/templates/service.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/templates/service.yaml deleted file mode 100644 index 27501e1..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ .Chart.Name }} - labels: - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.externalPort }} - targetPort: {{ .Values.service.internalPort }} - protocol: TCP - name: {{ .Values.service.name }} - selector: - app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml deleted file mode 100644 index f0381ae..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Default values for subchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. -# subchartA -service: - name: apache - type: ClusterIP - externalPort: 80 - internalPort: 80 -SCAdata: - SCAbool: false - SCAfloat: 3.1 - SCAint: 55 - SCAstring: "jabba" - SCAnested1: - SCAnested2: true - diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/Chart.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/Chart.yaml deleted file mode 100644 index c3c6bba..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: subchartb -version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/templates/service.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/templates/service.yaml deleted file mode 100644 index 27501e1..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ .Chart.Name }} - labels: - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.externalPort }} - targetPort: {{ .Values.service.internalPort }} - protocol: TCP - name: {{ .Values.service.name }} - selector: - app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml deleted file mode 100644 index 774fdd7..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# Default values for subchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. -service: - name: nginx - type: ClusterIP - externalPort: 80 - internalPort: 80 - -SCBdata: - SCBbool: true - SCBfloat: 7.77 - SCBint: 33 - SCBstring: "boba" - -exports: - SCBexported1: - SCBexported1A: - SCBexported1B: 1965 - - SCBexported2: - SCBexported2A: "blaster" - -global: - kolla: - nova: - api: - all: - port: 8774 - metadata: - all: - port: 8775 - - - diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml deleted file mode 100644 index fca77fd..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/crds/crdA.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: testCRDs -spec: - group: testCRDGroups - names: - kind: TestCRD - listKind: TestCRDList - plural: TestCRDs - shortNames: - - tc - singular: authconfig diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/NOTES.txt b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/NOTES.txt deleted file mode 100644 index 4bdf443..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/NOTES.txt +++ /dev/null @@ -1 +0,0 @@ -Sample notes for {{ .Chart.Name }} \ No newline at end of file diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/service.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/service.yaml deleted file mode 100644 index fee94dc..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/service.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ .Chart.Name }} - labels: - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - kube-version/major: "{{ .Capabilities.KubeVersion.Major }}" - kube-version/minor: "{{ .Capabilities.KubeVersion.Minor }}" - kube-version/version: "v{{ .Capabilities.KubeVersion.Major }}.{{ .Capabilities.KubeVersion.Minor }}.0" -{{- if .Capabilities.APIVersions.Has "helm.k8s.io/test" }} - kube-api-version/test: v1 -{{- end }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.externalPort }} - targetPort: {{ .Values.service.internalPort }} - protocol: TCP - name: {{ .Values.service.name }} - selector: - app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml deleted file mode 100644 index a974e31..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# Default values for subchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. -# subchart1 -service: - name: nginx - type: ClusterIP - externalPort: 80 - internalPort: 80 - - -SC1data: - SC1bool: true - SC1float: 3.14 - SC1int: 100 - SC1string: "dollywood" - SC1extra1: 11 - -imported-chartA: - SC1extra2: 1.337 - -overridden-chartA: - SCAbool: true - SCAfloat: 3.14 - SCAint: 100 - SCAstring: "jabbathehut" - SC1extra3: true - -imported-chartA-B: - SC1extra5: "tiller" - -overridden-chartA-B: - SCAbool: true - SCAfloat: 3.33 - SCAint: 555 - SCAstring: "wormwood" - SCAextra1: 23 - - SCBbool: true - SCBfloat: 0.25 - SCBint: 98 - SCBstring: "murkwood" - SCBextra1: 13 - - SC1extra6: 77 - -SCBexported1A: - SC1extra7: true - -exports: - SC1exported1: - global: - SC1exported2: - all: - SC1exported3: "SC1expstr" \ No newline at end of file diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/Chart.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/Chart.yaml deleted file mode 100644 index f936528..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/Chart.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: subchart2 -version: 0.1.0 -dependencies: - - name: subchartb - repository: http://localhost:10191 - version: 0.1.0 - condition: subchartb.enabled - tags: - - back-end - - subchartb - - name: subchartc - repository: http://localhost:10191 - version: 0.1.0 - condition: subchartc.enabled - tags: - - back-end - - subchartc diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/Chart.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/Chart.yaml deleted file mode 100644 index c3c6bba..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: subchartb -version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/templates/service.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/templates/service.yaml deleted file mode 100644 index 3f168bd..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: subchart2-{{ .Chart.Name }} - labels: - helm.sh/hart: "{{ .Chart.Name }}-{{ .Chart.Version }}" -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.externalPort }} - targetPort: {{ .Values.service.internalPort }} - protocol: TCP - name: subchart2-{{ .Values.service.name }} - selector: - app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/values.yaml deleted file mode 100644 index 5e5b210..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartB/values.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Default values for subchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent -service: - name: nginx - type: ClusterIP - externalPort: 80 - internalPort: 80 -resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi - diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/Chart.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/Chart.yaml deleted file mode 100644 index dcc45c0..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: subchartc -version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/templates/service.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/templates/service.yaml deleted file mode 100644 index 27501e1..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ .Chart.Name }} - labels: - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.externalPort }} - targetPort: {{ .Values.service.internalPort }} - protocol: TCP - name: {{ .Values.service.name }} - selector: - app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/values.yaml deleted file mode 100644 index 5e5b210..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/charts/subchartC/values.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Default values for subchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent -service: - name: nginx - type: ClusterIP - externalPort: 80 - internalPort: 80 -resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi - diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/templates/service.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/templates/service.yaml deleted file mode 100644 index 27501e1..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ .Chart.Name }} - labels: - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.externalPort }} - targetPort: {{ .Values.service.internalPort }} - protocol: TCP - name: {{ .Values.service.name }} - selector: - app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/values.yaml deleted file mode 100644 index 5e5b210..0000000 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/values.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Default values for subchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent -service: - name: nginx - type: ClusterIP - externalPort: 80 - internalPort: 80 -resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi - diff --git a/pkg/chartutil/testdata/subpop/noreqs/Chart.yaml b/pkg/chartutil/testdata/subpop/noreqs/Chart.yaml deleted file mode 100644 index bbb0941..0000000 --- a/pkg/chartutil/testdata/subpop/noreqs/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: parentchart -version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/noreqs/templates/service.yaml b/pkg/chartutil/testdata/subpop/noreqs/templates/service.yaml deleted file mode 100644 index 27501e1..0000000 --- a/pkg/chartutil/testdata/subpop/noreqs/templates/service.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ .Chart.Name }} - labels: - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.externalPort }} - targetPort: {{ .Values.service.internalPort }} - protocol: TCP - name: {{ .Values.service.name }} - selector: - app.kubernetes.io/name: {{ .Chart.Name }} diff --git a/pkg/chartutil/testdata/subpop/noreqs/values.yaml b/pkg/chartutil/testdata/subpop/noreqs/values.yaml deleted file mode 100644 index 4ed3b7a..0000000 --- a/pkg/chartutil/testdata/subpop/noreqs/values.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# Default values for subchart. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent -service: - name: nginx - type: ClusterIP - externalPort: 80 - internalPort: 80 -resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi - - -# switch-like -tags: - front-end: true - back-end: false diff --git a/pkg/chartutil/testdata/subpop/values.yaml b/pkg/chartutil/testdata/subpop/values.yaml deleted file mode 100644 index 4e5022b..0000000 --- a/pkg/chartutil/testdata/subpop/values.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# parent/values.yaml - -imported-chart1: - SPextra1: "helm rocks" - -overridden-chart1: - SC1bool: false - SC1float: 3.141592 - SC1int: 99 - SC1string: "pollywog" - SPextra2: 42 - - -imported-chartA: - SPextra3: 1.337 - -overridden-chartA: - SCAbool: true - SCAfloat: 41.3 - SCAint: 808 - SCAstring: "jaberwocky" - SPextra4: true - -imported-chartA-B: - SPextra5: "k8s" - -overridden-chartA-B: - SCAbool: true - SCAfloat: 41.3 - SCAint: 808 - SCAstring: "jaberwocky" - SCBbool: false - SCBfloat: 1.99 - SCBint: 77 - SCBstring: "jango" - SPextra6: 111 - -tags: - front-end: true - back-end: false - -subchart2alias: - enabled: false diff --git a/pkg/chartutil/testdata/test-values-negative.yaml b/pkg/chartutil/testdata/test-values-negative.yaml deleted file mode 100644 index 5a1250b..0000000 --- a/pkg/chartutil/testdata/test-values-negative.yaml +++ /dev/null @@ -1,14 +0,0 @@ -firstname: John -lastname: Doe -age: -5 -likesCoffee: true -addresses: - - city: Springfield - street: Main - number: 12345 - - city: New York - street: Broadway - number: 67890 -phoneNumbers: - - "(888) 888-8888" - - "(555) 555-5555" diff --git a/pkg/chartutil/testdata/test-values.schema.json b/pkg/chartutil/testdata/test-values.schema.json deleted file mode 100644 index 4df89bb..0000000 --- a/pkg/chartutil/testdata/test-values.schema.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "properties": { - "addresses": { - "description": "List of addresses", - "items": { - "properties": { - "city": { - "type": "string" - }, - "number": { - "type": "number" - }, - "street": { - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "age": { - "description": "Age", - "minimum": 0, - "type": "integer" - }, - "employmentInfo": { - "properties": { - "salary": { - "minimum": 0, - "type": "number" - }, - "title": { - "type": "string" - } - }, - "required": [ - "salary" - ], - "type": "object" - }, - "firstname": { - "description": "First name", - "type": "string" - }, - "lastname": { - "type": "string" - }, - "likesCoffee": { - "type": "boolean" - }, - "phoneNumbers": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "firstname", - "lastname", - "addresses", - "employmentInfo" - ], - "title": "Values", - "type": "object" -} diff --git a/pkg/chartutil/testdata/test-values.yaml b/pkg/chartutil/testdata/test-values.yaml deleted file mode 100644 index 042dea6..0000000 --- a/pkg/chartutil/testdata/test-values.yaml +++ /dev/null @@ -1,17 +0,0 @@ -firstname: John -lastname: Doe -age: 25 -likesCoffee: true -employmentInfo: - title: Software Developer - salary: 100000 -addresses: - - city: Springfield - street: Main - number: 12345 - - city: New York - street: Broadway - number: 67890 -phoneNumbers: - - "(888) 888-8888" - - "(555) 555-5555" diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go deleted file mode 100644 index e1cdf46..0000000 --- a/pkg/chartutil/values.go +++ /dev/null @@ -1,212 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "io" - "io/ioutil" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart" -) - -// GlobalKey is the name of the Values key that is used for storing global vars. -const GlobalKey = "global" - -// Values represents a collection of chart values. -type Values map[string]interface{} - -// YAML encodes the Values into a YAML string. -func (v Values) YAML() (string, error) { - b, err := yaml.Marshal(v) - return string(b), err -} - -// Table gets a table (YAML subsection) from a Values object. -// -// The table is returned as a Values. -// -// Compound table names may be specified with dots: -// -// foo.bar -// -// The above will be evaluated as "The table bar inside the table -// foo". -// -// An ErrNoTable is returned if the table does not exist. -func (v Values) Table(name string) (Values, error) { - table := v - var err error - - for _, n := range parsePath(name) { - if table, err = tableLookup(table, n); err != nil { - break - } - } - return table, err -} - -// AsMap is a utility function for converting Values to a map[string]interface{}. -// -// It protects against nil map panics. -func (v Values) AsMap() map[string]interface{} { - if v == nil || len(v) == 0 { - return map[string]interface{}{} - } - return v -} - -// Encode writes serialized Values information to the given io.Writer. -func (v Values) Encode(w io.Writer) error { - out, err := yaml.Marshal(v) - if err != nil { - return err - } - _, err = w.Write(out) - return err -} - -func tableLookup(v Values, simple string) (Values, error) { - v2, ok := v[simple] - if !ok { - return v, ErrNoTable{simple} - } - if vv, ok := v2.(map[string]interface{}); ok { - return vv, nil - } - - // This catches a case where a value is of type Values, but doesn't (for some - // reason) match the map[string]interface{}. This has been observed in the - // wild, and might be a result of a nil map of type Values. - if vv, ok := v2.(Values); ok { - return vv, nil - } - - return Values{}, ErrNoTable{simple} -} - -// ReadValues will parse YAML byte data into a Values. -func ReadValues(data []byte) (vals Values, err error) { - err = yaml.Unmarshal(data, &vals) - if len(vals) == 0 { - vals = Values{} - } - return vals, err -} - -// ReadValuesFile will parse a YAML file into a map of values. -func ReadValuesFile(filename string) (Values, error) { - data, err := ioutil.ReadFile(filename) - if err != nil { - return map[string]interface{}{}, err - } - return ReadValues(data) -} - -// ReleaseOptions represents the additional release options needed -// for the composition of the final values struct -type ReleaseOptions struct { - Name string - Namespace string - Revision int - IsUpgrade bool - IsInstall bool -} - -// ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files -// -// This takes both ReleaseOptions and Capabilities to merge into the render values. -func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options ReleaseOptions, caps *Capabilities) (Values, error) { - if caps == nil { - caps = DefaultCapabilities - } - top := map[string]interface{}{ - "Chart": chrt.Metadata, - "Capabilities": caps, - "Release": map[string]interface{}{ - "Name": options.Name, - "Namespace": options.Namespace, - "IsUpgrade": options.IsUpgrade, - "IsInstall": options.IsInstall, - "Revision": options.Revision, - "Service": "Helm", - }, - } - - vals, err := CoalesceValues(chrt, chrtVals) - if err != nil { - return top, err - } - - if err := ValidateAgainstSchema(chrt, vals); err != nil { - errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s" - return top, fmt.Errorf(errFmt, err.Error()) - } - - top["Values"] = vals - return top, nil -} - -// istable is a special-purpose function to see if the present thing matches the definition of a YAML table. -func istable(v interface{}) bool { - _, ok := v.(map[string]interface{}) - return ok -} - -// PathValue takes a path that traverses a YAML structure and returns the value at the end of that path. -// The path starts at the root of the YAML structure and is comprised of YAML keys separated by periods. -// Given the following YAML data the value at path "chapter.one.title" is "Loomings". -// -// chapter: -// one: -// title: "Loomings" -func (v Values) PathValue(path string) (interface{}, error) { - if path == "" { - return nil, errors.New("YAML path cannot be empty") - } - return v.pathValue(parsePath(path)) -} - -func (v Values) pathValue(path []string) (interface{}, error) { - if len(path) == 1 { - // if exists must be root key not table - if _, ok := v[path[0]]; ok && !istable(v[path[0]]) { - return v[path[0]], nil - } - return nil, ErrNoValue{path[0]} - } - - key, path := path[len(path)-1], path[:len(path)-1] - // get our table for table path - t, err := v.Table(joinPath(path...)) - if err != nil { - return nil, ErrNoValue{key} - } - // check table for key and ensure value is not a table - if k, ok := t[key]; ok && !istable(k) { - return k, nil - } - return nil, ErrNoValue{key} -} - -func parsePath(key string) []string { return strings.Split(key, ".") } - -func joinPath(path ...string) string { return strings.Join(path, ".") } diff --git a/pkg/chartutil/values_test.go b/pkg/chartutil/values_test.go deleted file mode 100644 index c95fa50..0000000 --- a/pkg/chartutil/values_test.go +++ /dev/null @@ -1,292 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "bytes" - "fmt" - "testing" - "text/template" - - "helm.sh/helm/v3/pkg/chart" -) - -func TestReadValues(t *testing.T) { - doc := `# Test YAML parse -poet: "Coleridge" -title: "Rime of the Ancient Mariner" -stanza: - - "at" - - "length" - - "did" - - cross - - an - - Albatross - -mariner: - with: "crossbow" - shot: "ALBATROSS" - -water: - water: - where: "everywhere" - nor: "any drop to drink" -` - - data, err := ReadValues([]byte(doc)) - if err != nil { - t.Fatalf("Error parsing bytes: %s", err) - } - matchValues(t, data) - - tests := []string{`poet: "Coleridge"`, "# Just a comment", ""} - - for _, tt := range tests { - data, err = ReadValues([]byte(tt)) - if err != nil { - t.Fatalf("Error parsing bytes (%s): %s", tt, err) - } - if data == nil { - t.Errorf(`YAML string "%s" gave a nil map`, tt) - } - } -} - -func TestToRenderValues(t *testing.T) { - - chartValues := map[string]interface{}{ - "name": "al Rashid", - "where": map[string]interface{}{ - "city": "Basrah", - "title": "caliph", - }, - } - - overrideValues := map[string]interface{}{ - "name": "Haroun", - "where": map[string]interface{}{ - "city": "Baghdad", - "date": "809 CE", - }, - } - - c := &chart.Chart{ - Metadata: &chart.Metadata{Name: "test"}, - Templates: []*chart.File{}, - Values: chartValues, - Files: []*chart.File{ - {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, - }, - } - c.AddDependency(&chart.Chart{ - Metadata: &chart.Metadata{Name: "where"}, - }) - - o := ReleaseOptions{ - Name: "Seven Voyages", - Namespace: "default", - Revision: 1, - IsInstall: true, - } - - res, err := ToRenderValues(c, overrideValues, o, nil) - if err != nil { - t.Fatal(err) - } - - // Ensure that the top-level values are all set. - if name := res["Chart"].(*chart.Metadata).Name; name != "test" { - t.Errorf("Expected chart name 'test', got %q", name) - } - relmap := res["Release"].(map[string]interface{}) - if name := relmap["Name"]; name.(string) != "Seven Voyages" { - t.Errorf("Expected release name 'Seven Voyages', got %q", name) - } - if namespace := relmap["Namespace"]; namespace.(string) != "default" { - t.Errorf("Expected namespace 'default', got %q", namespace) - } - if revision := relmap["Revision"]; revision.(int) != 1 { - t.Errorf("Expected revision '1', got %d", revision) - } - if relmap["IsUpgrade"].(bool) { - t.Error("Expected upgrade to be false.") - } - if !relmap["IsInstall"].(bool) { - t.Errorf("Expected install to be true.") - } - if !res["Capabilities"].(*Capabilities).APIVersions.Has("v1") { - t.Error("Expected Capabilities to have v1 as an API") - } - if res["Capabilities"].(*Capabilities).KubeVersion.Major != "1" { - t.Error("Expected Capabilities to have a Kube version") - } - - vals := res["Values"].(Values) - if vals["name"] != "Haroun" { - t.Errorf("Expected 'Haroun', got %q (%v)", vals["name"], vals) - } - where := vals["where"].(map[string]interface{}) - expects := map[string]string{ - "city": "Baghdad", - "date": "809 CE", - "title": "caliph", - } - for field, expect := range expects { - if got := where[field]; got != expect { - t.Errorf("Expected %q, got %q (%v)", expect, got, where) - } - } -} - -func TestReadValuesFile(t *testing.T) { - data, err := ReadValuesFile("./testdata/coleridge.yaml") - if err != nil { - t.Fatalf("Error reading YAML file: %s", err) - } - matchValues(t, data) -} - -func ExampleValues() { - doc := ` -title: "Moby Dick" -chapter: - one: - title: "Loomings" - two: - title: "The Carpet-Bag" - three: - title: "The Spouter Inn" -` - d, err := ReadValues([]byte(doc)) - if err != nil { - panic(err) - } - ch1, err := d.Table("chapter.one") - if err != nil { - panic("could not find chapter one") - } - fmt.Print(ch1["title"]) - // Output: - // Loomings -} - -func TestTable(t *testing.T) { - doc := ` -title: "Moby Dick" -chapter: - one: - title: "Loomings" - two: - title: "The Carpet-Bag" - three: - title: "The Spouter Inn" -` - d, err := ReadValues([]byte(doc)) - if err != nil { - t.Fatalf("Failed to parse the White Whale: %s", err) - } - - if _, err := d.Table("title"); err == nil { - t.Fatalf("Title is not a table.") - } - - if _, err := d.Table("chapter"); err != nil { - t.Fatalf("Failed to get the chapter table: %s\n%v", err, d) - } - - if v, err := d.Table("chapter.one"); err != nil { - t.Errorf("Failed to get chapter.one: %s", err) - } else if v["title"] != "Loomings" { - t.Errorf("Unexpected title: %s", v["title"]) - } - - if _, err := d.Table("chapter.three"); err != nil { - t.Errorf("Chapter three is missing: %s\n%v", err, d) - } - - if _, err := d.Table("chapter.OneHundredThirtySix"); err == nil { - t.Errorf("I think you mean 'Epilogue'") - } -} - -func matchValues(t *testing.T, data map[string]interface{}) { - if data["poet"] != "Coleridge" { - t.Errorf("Unexpected poet: %s", data["poet"]) - } - - if o, err := ttpl("{{len .stanza}}", data); err != nil { - t.Errorf("len stanza: %s", err) - } else if o != "6" { - t.Errorf("Expected 6, got %s", o) - } - - if o, err := ttpl("{{.mariner.shot}}", data); err != nil { - t.Errorf(".mariner.shot: %s", err) - } else if o != "ALBATROSS" { - t.Errorf("Expected that mariner shot ALBATROSS") - } - - if o, err := ttpl("{{.water.water.where}}", data); err != nil { - t.Errorf(".water.water.where: %s", err) - } else if o != "everywhere" { - t.Errorf("Expected water water everywhere") - } -} - -func ttpl(tpl string, v map[string]interface{}) (string, error) { - var b bytes.Buffer - tt := template.Must(template.New("t").Parse(tpl)) - err := tt.Execute(&b, v) - return b.String(), err -} - -func TestPathValue(t *testing.T) { - doc := ` -title: "Moby Dick" -chapter: - one: - title: "Loomings" - two: - title: "The Carpet-Bag" - three: - title: "The Spouter Inn" -` - d, err := ReadValues([]byte(doc)) - if err != nil { - t.Fatalf("Failed to parse the White Whale: %s", err) - } - - if v, err := d.PathValue("chapter.one.title"); err != nil { - t.Errorf("Got error instead of title: %s\n%v", err, d) - } else if v != "Loomings" { - t.Errorf("No error but got wrong value for title: %s\n%v", err, d) - } - if _, err := d.PathValue("chapter.one.doesnotexist"); err == nil { - t.Errorf("Non-existent key should return error: %s\n%v", err, d) - } - if _, err := d.PathValue("chapter.doesnotexist.one"); err == nil { - t.Errorf("Non-existent key in middle of path should return error: %s\n%v", err, d) - } - if _, err := d.PathValue(""); err == nil { - t.Error("Asking for the value from an empty path should yield an error") - } - if v, err := d.PathValue("title"); err == nil { - if v != "Moby Dick" { - t.Errorf("Failed to return values for root key title") - } - } -} diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go deleted file mode 100644 index 5f947ae..0000000 --- a/pkg/cli/environment.go +++ /dev/null @@ -1,130 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package cli describes the operating environment for the Helm CLI. - -Helm's environment encapsulates all of the service dependencies Helm has. -These dependencies are expressed as interfaces so that alternate implementations -(mocks, etc.) can be easily generated. -*/ -package cli - -import ( - "fmt" - "os" - "strconv" - "sync" - - "github.com/spf13/pflag" - - "k8s.io/cli-runtime/pkg/genericclioptions" - - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/kube" -) - -// EnvSettings describes all of the environment settings. -type EnvSettings struct { - namespace string - config genericclioptions.RESTClientGetter - configOnce sync.Once - - // KubeConfig is the path to the kubeconfig file - KubeConfig string - // KubeContext is the name of the kubeconfig context. - KubeContext string - // Debug indicates whether or not Helm is running in Debug mode. - Debug bool - // RegistryConfig is the path to the registry config file. - RegistryConfig string - // RepositoryConfig is the path to the repositories file. - RepositoryConfig string - // RepositoryCache is the path to the repository cache directory. - RepositoryCache string - // PluginsDirectory is the path to the plugins directory. - PluginsDirectory string -} - -func New() *EnvSettings { - - env := EnvSettings{ - namespace: os.Getenv("HELM_NAMESPACE"), - KubeContext: os.Getenv("HELM_KUBECONTEXT"), - PluginsDirectory: envOr("HELM_PLUGINS", helmpath.DataPath("plugins")), - RegistryConfig: envOr("HELM_REGISTRY_CONFIG", helmpath.ConfigPath("registry.json")), - RepositoryConfig: envOr("HELM_REPOSITORY_CONFIG", helmpath.ConfigPath("repositories.yaml")), - RepositoryCache: envOr("HELM_REPOSITORY_CACHE", helmpath.CachePath("repository")), - } - env.Debug, _ = strconv.ParseBool(os.Getenv("HELM_DEBUG")) - return &env -} - -// AddFlags binds flags to the given flagset. -func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) { - fs.StringVarP(&s.namespace, "namespace", "n", s.namespace, "namespace scope for this request") - fs.StringVar(&s.KubeConfig, "kubeconfig", "", "path to the kubeconfig file") - fs.StringVar(&s.KubeContext, "kube-context", s.KubeContext, "name of the kubeconfig context to use") - fs.BoolVar(&s.Debug, "debug", s.Debug, "enable verbose output") - fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file") - fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the file containing repository names and URLs") - fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the file containing cached repository indexes") -} - -func envOr(name, def string) string { - if v, ok := os.LookupEnv(name); ok { - return v - } - return def -} - -func (s *EnvSettings) EnvVars() map[string]string { - envvars := map[string]string{ - "HELM_BIN": os.Args[0], - "HELM_DEBUG": fmt.Sprint(s.Debug), - "HELM_PLUGINS": s.PluginsDirectory, - "HELM_REGISTRY_CONFIG": s.RegistryConfig, - "HELM_REPOSITORY_CACHE": s.RepositoryCache, - "HELM_REPOSITORY_CONFIG": s.RepositoryConfig, - "HELM_NAMESPACE": s.Namespace(), - "HELM_KUBECONTEXT": s.KubeContext, - } - - if s.KubeConfig != "" { - envvars["KUBECONFIG"] = s.KubeConfig - } - - return envvars -} - -//Namespace gets the namespace from the configuration -func (s *EnvSettings) Namespace() string { - if s.namespace != "" { - return s.namespace - } - - if ns, _, err := s.RESTClientGetter().ToRawKubeConfigLoader().Namespace(); err == nil { - return ns - } - return "default" -} - -//RESTClientGetter gets the kubeconfig from EnvSettings -func (s *EnvSettings) RESTClientGetter() genericclioptions.RESTClientGetter { - s.configOnce.Do(func() { - s.config = kube.GetConfig(s.KubeConfig, s.KubeContext, s.namespace) - }) - return s.config -} diff --git a/pkg/cli/environment_test.go b/pkg/cli/environment_test.go deleted file mode 100644 index fadc298..0000000 --- a/pkg/cli/environment_test.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package cli - -import ( - "os" - "strings" - "testing" - - "github.com/spf13/pflag" -) - -func TestEnvSettings(t *testing.T) { - tests := []struct { - name string - - // input - args string - envvars map[string]string - - // expected values - ns, kcontext string - debug bool - }{ - { - name: "defaults", - ns: "default", - }, - { - name: "with flags set", - args: "--debug --namespace=myns", - ns: "myns", - debug: true, - }, - { - name: "with envvars set", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"}, - ns: "yourns", - debug: true, - }, - { - name: "with flags and envvars set", - args: "--debug --namespace=myns", - envvars: map[string]string{"HELM_DEBUG": "1", "HELM_NAMESPACE": "yourns"}, - ns: "myns", - debug: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer resetEnv()() - - for k, v := range tt.envvars { - os.Setenv(k, v) - } - - flags := pflag.NewFlagSet("testing", pflag.ContinueOnError) - - settings := New() - settings.AddFlags(flags) - flags.Parse(strings.Split(tt.args, " ")) - - if settings.Debug != tt.debug { - t.Errorf("expected debug %t, got %t", tt.debug, settings.Debug) - } - if settings.Namespace() != tt.ns { - t.Errorf("expected namespace %q, got %q", tt.ns, settings.Namespace()) - } - if settings.KubeContext != tt.kcontext { - t.Errorf("expected kube-context %q, got %q", tt.kcontext, settings.KubeContext) - } - }) - } -} - -func resetEnv() func() { - origEnv := os.Environ() - - // ensure any local envvars do not hose us - for e := range New().EnvVars() { - os.Unsetenv(e) - } - - return func() { - for _, pair := range origEnv { - kv := strings.SplitN(pair, "=", 2) - os.Setenv(kv[0], kv[1]) - } - } -} diff --git a/pkg/cli/output/output.go b/pkg/cli/output/output.go deleted file mode 100644 index e4eb046..0000000 --- a/pkg/cli/output/output.go +++ /dev/null @@ -1,130 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package output - -import ( - "encoding/json" - "fmt" - "io" - - "github.com/gosuri/uitable" - "github.com/pkg/errors" - "sigs.k8s.io/yaml" -) - -// Format is a type for capturing supported output formats -type Format string - -const ( - Table Format = "table" - JSON Format = "json" - YAML Format = "yaml" -) - -// Formats returns a list of the string representation of the supported formats -func Formats() []string { - return []string{Table.String(), JSON.String(), YAML.String()} -} - -// ErrInvalidFormatType is returned when an unsupported format type is used -var ErrInvalidFormatType = fmt.Errorf("invalid format type") - -// String returns the string representation of the Format -func (o Format) String() string { - return string(o) -} - -// Write the output in the given format to the io.Writer. Unsupported formats -// will return an error -func (o Format) Write(out io.Writer, w Writer) error { - switch o { - case Table: - return w.WriteTable(out) - case JSON: - return w.WriteJSON(out) - case YAML: - return w.WriteYAML(out) - } - return ErrInvalidFormatType -} - -// ParseFormat takes a raw string and returns the matching Format. -// If the format does not exists, ErrInvalidFormatType is returned -func ParseFormat(s string) (out Format, err error) { - switch s { - case Table.String(): - out, err = Table, nil - case JSON.String(): - out, err = JSON, nil - case YAML.String(): - out, err = YAML, nil - default: - out, err = "", ErrInvalidFormatType - } - return -} - -// Writer is an interface that any type can implement to write supported formats -type Writer interface { - // WriteTable will write tabular output into the given io.Writer, returning - // an error if any occur - WriteTable(out io.Writer) error - // WriteJSON will write JSON formatted output into the given io.Writer, - // returning an error if any occur - WriteJSON(out io.Writer) error - // WriteYAML will write YAML formatted output into the given io.Writer, - // returning an error if any occur - WriteYAML(out io.Writer) error -} - -// EncodeJSON is a helper function to decorate any error message with a bit more -// context and avoid writing the same code over and over for printers. -func EncodeJSON(out io.Writer, obj interface{}) error { - enc := json.NewEncoder(out) - err := enc.Encode(obj) - if err != nil { - return errors.Wrap(err, "unable to write JSON output") - } - return nil -} - -// EncodeYAML is a helper function to decorate any error message with a bit more -// context and avoid writing the same code over and over for printers -func EncodeYAML(out io.Writer, obj interface{}) error { - raw, err := yaml.Marshal(obj) - if err != nil { - return errors.Wrap(err, "unable to write YAML output") - } - - _, err = out.Write(raw) - if err != nil { - return errors.Wrap(err, "unable to write YAML output") - } - return nil -} - -// EncodeTable is a helper function to decorate any error message with a bit -// more context and avoid writing the same code over and over for printers -func EncodeTable(out io.Writer, table *uitable.Table) error { - raw := table.Bytes() - raw = append(raw, []byte("\n")...) - _, err := out.Write(raw) - if err != nil { - return errors.Wrap(err, "unable to write table output") - } - return nil -} diff --git a/pkg/cli/values/options.go b/pkg/cli/values/options.go deleted file mode 100644 index e6ad717..0000000 --- a/pkg/cli/values/options.go +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package values - -import ( - "io/ioutil" - "net/url" - "os" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/strvals" -) - -type Options struct { - ValueFiles []string - StringValues []string - Values []string - FileValues []string -} - -// MergeValues merges values from files specified via -f/--values and directly -// via --set, --set-string, or --set-file, marshaling them to YAML -func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, error) { - base := map[string]interface{}{} - - // User specified a values files via -f/--values - for _, filePath := range opts.ValueFiles { - currentMap := map[string]interface{}{} - - bytes, err := readFile(filePath, p) - if err != nil { - return nil, err - } - - if err := yaml.Unmarshal(bytes, ¤tMap); err != nil { - return nil, errors.Wrapf(err, "failed to parse %s", filePath) - } - // Merge with the previous map - base = mergeMaps(base, currentMap) - } - - // User specified a value via --set - for _, value := range opts.Values { - if err := strvals.ParseInto(value, base); err != nil { - return nil, errors.Wrap(err, "failed parsing --set data") - } - } - - // User specified a value via --set-string - for _, value := range opts.StringValues { - if err := strvals.ParseIntoString(value, base); err != nil { - return nil, errors.Wrap(err, "failed parsing --set-string data") - } - } - - // User specified a value via --set-file - for _, value := range opts.FileValues { - reader := func(rs []rune) (interface{}, error) { - bytes, err := readFile(string(rs), p) - return string(bytes), err - } - if err := strvals.ParseIntoFile(value, base, reader); err != nil { - return nil, errors.Wrap(err, "failed parsing --set-file data") - } - } - - return base, nil -} - -func mergeMaps(a, b map[string]interface{}) map[string]interface{} { - out := make(map[string]interface{}, len(a)) - for k, v := range a { - out[k] = v - } - for k, v := range b { - if v, ok := v.(map[string]interface{}); ok { - if bv, ok := out[k]; ok { - if bv, ok := bv.(map[string]interface{}); ok { - out[k] = mergeMaps(bv, v) - continue - } - } - } - out[k] = v - } - return out -} - -// readFile load a file from stdin, the local directory, or a remote file with a url. -func readFile(filePath string, p getter.Providers) ([]byte, error) { - if strings.TrimSpace(filePath) == "-" { - return ioutil.ReadAll(os.Stdin) - } - u, _ := url.Parse(filePath) - - // FIXME: maybe someone handle other protocols like ftp. - g, err := p.ByScheme(u.Scheme) - if err != nil { - return ioutil.ReadFile(filePath) - } - data, err := g.Get(filePath, getter.WithURL(filePath)) - return data.Bytes(), err -} diff --git a/pkg/cli/values/options_test.go b/pkg/cli/values/options_test.go deleted file mode 100644 index d988274..0000000 --- a/pkg/cli/values/options_test.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package values - -import ( - "reflect" - "testing" -) - -func TestMergeValues(t *testing.T) { - nestedMap := map[string]interface{}{ - "foo": "bar", - "baz": map[string]string{ - "cool": "stuff", - }, - } - anotherNestedMap := map[string]interface{}{ - "foo": "bar", - "baz": map[string]string{ - "cool": "things", - "awesome": "stuff", - }, - } - flatMap := map[string]interface{}{ - "foo": "bar", - "baz": "stuff", - } - anotherFlatMap := map[string]interface{}{ - "testing": "fun", - } - - testMap := mergeMaps(flatMap, nestedMap) - equal := reflect.DeepEqual(testMap, nestedMap) - if !equal { - t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap) - } - - testMap = mergeMaps(nestedMap, flatMap) - equal = reflect.DeepEqual(testMap, flatMap) - if !equal { - t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap) - } - - testMap = mergeMaps(nestedMap, anotherNestedMap) - equal = reflect.DeepEqual(testMap, anotherNestedMap) - if !equal { - t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap) - } - - testMap = mergeMaps(anotherFlatMap, anotherNestedMap) - expectedMap := map[string]interface{}{ - "testing": "fun", - "foo": "bar", - "baz": map[string]string{ - "cool": "things", - "awesome": "stuff", - }, - } - equal = reflect.DeepEqual(testMap, expectedMap) - if !equal { - t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap) - } -} diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go deleted file mode 100644 index f3d4321..0000000 --- a/pkg/downloader/chart_downloader.go +++ /dev/null @@ -1,368 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package downloader - -import ( - "fmt" - "io" - "io/ioutil" - "net/url" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/internal/urlutil" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/provenance" - "helm.sh/helm/v3/pkg/repo" -) - -// VerificationStrategy describes a strategy for determining whether to verify a chart. -type VerificationStrategy int - -const ( - // VerifyNever will skip all verification of a chart. - VerifyNever VerificationStrategy = iota - // VerifyIfPossible will attempt a verification, it will not error if verification - // data is missing. But it will not stop processing if verification fails. - VerifyIfPossible - // VerifyAlways will always attempt a verification, and will fail if the - // verification fails. - VerifyAlways - // VerifyLater will fetch verification data, but not do any verification. - // This is to accommodate the case where another step of the process will - // perform verification. - VerifyLater -) - -// ErrNoOwnerRepo indicates that a given chart URL can't be found in any repos. -var ErrNoOwnerRepo = errors.New("could not find a repo containing the given URL") - -// ChartDownloader handles downloading a chart. -// -// It is capable of performing verifications on charts as well. -type ChartDownloader struct { - // Out is the location to write warning and info messages. - Out io.Writer - // Verify indicates what verification strategy to use. - Verify VerificationStrategy - // Keyring is the keyring file used for verification. - Keyring string - // Getter collection for the operation - Getters getter.Providers - // Options provide parameters to be passed along to the Getter being initialized. - Options []getter.Option - RepositoryConfig string - RepositoryCache string -} - -// DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file. -// -// If Verify is set to VerifyNever, the verification will be nil. -// If Verify is set to VerifyIfPossible, this will return a verification (or nil on failure), and print a warning on failure. -// If Verify is set to VerifyAlways, this will return a verification or an error if the verification fails. -// If Verify is set to VerifyLater, this will download the prov file (if it exists), but not verify it. -// -// For VerifyNever and VerifyIfPossible, the Verification may be empty. -// -// Returns a string path to the location where the file was downloaded and a verification -// (if provenance was verified), or an error if something bad happened. -func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *provenance.Verification, error) { - u, err := c.ResolveChartVersion(ref, version) - if err != nil { - return "", nil, err - } - - g, err := c.Getters.ByScheme(u.Scheme) - if err != nil { - return "", nil, err - } - - data, err := g.Get(u.String(), c.Options...) - if err != nil { - return "", nil, err - } - - name := filepath.Base(u.Path) - destfile := filepath.Join(dest, name) - if err := ioutil.WriteFile(destfile, data.Bytes(), 0644); err != nil { - return destfile, nil, err - } - - // If provenance is requested, verify it. - ver := &provenance.Verification{} - if c.Verify > VerifyNever { - body, err := g.Get(u.String() + ".prov") - if err != nil { - if c.Verify == VerifyAlways { - return destfile, ver, errors.Errorf("failed to fetch provenance %q", u.String()+".prov") - } - fmt.Fprintf(c.Out, "WARNING: Verification not found for %s: %s\n", ref, err) - return destfile, ver, nil - } - provfile := destfile + ".prov" - if err := ioutil.WriteFile(provfile, body.Bytes(), 0644); err != nil { - return destfile, nil, err - } - - if c.Verify != VerifyLater { - ver, err = VerifyChart(destfile, c.Keyring) - if err != nil { - // Fail always in this case, since it means the verification step - // failed. - return destfile, ver, err - } - } - } - return destfile, ver, nil -} - -// ResolveChartVersion resolves a chart reference to a URL. -// -// It returns the URL and sets the ChartDownloader's Options that can fetch -// the URL using the appropriate Getter. -// -// A reference may be an HTTP URL, a 'reponame/chartname' reference, or a local path. -// -// A version is a SemVer string (1.2.3-beta.1+f334a6789). -// -// - For fully qualified URLs, the version will be ignored (since URLs aren't versioned) -// - For a chart reference -// * If version is non-empty, this will return the URL for that version -// * If version is empty, this will return the URL for the latest version -// * If no version can be found, an error is returned -func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, error) { - u, err := url.Parse(ref) - if err != nil { - return nil, errors.Errorf("invalid chart URL format: %s", ref) - } - c.Options = append(c.Options, getter.WithURL(ref)) - - rf, err := loadRepoConfig(c.RepositoryConfig) - if err != nil { - return u, err - } - - if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 { - // In this case, we have to find the parent repo that contains this chart - // URL. And this is an unfortunate problem, as it requires actually going - // through each repo cache file and finding a matching URL. But basically - // we want to find the repo in case we have special SSL cert config - // for that repo. - - rc, err := c.scanReposForURL(ref, rf) - if err != nil { - // If there is no special config, return the default HTTP client and - // swallow the error. - if err == ErrNoOwnerRepo { - return u, nil - } - return u, err - } - - // If we get here, we don't need to go through the next phase of looking - // up the URL. We have it already. So we just set the parameters and return. - c.Options = append( - c.Options, - getter.WithURL(rc.URL), - getter.WithTLSClientConfig(rc.CertFile, rc.KeyFile, rc.CAFile), - ) - if rc.Username != "" && rc.Password != "" { - c.Options = append( - c.Options, - getter.WithBasicAuth(rc.Username, rc.Password), - ) - } - return u, nil - } - - // See if it's of the form: repo/path_to_chart - p := strings.SplitN(u.Path, "/", 2) - if len(p) < 2 { - return u, errors.Errorf("non-absolute URLs should be in form of repo_name/path_to_chart, got: %s", u) - } - - repoName := p[0] - chartName := p[1] - rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories) - - if err != nil { - return u, err - } - - r, err := repo.NewChartRepository(rc, c.Getters) - if err != nil { - return u, err - } - if r != nil && r.Config != nil && r.Config.Username != "" && r.Config.Password != "" { - c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password)) - } - - if r.Config.CertFile != "" || r.Config.KeyFile != "" || r.Config.CAFile != "" { - c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile)) - } - - // Next, we need to load the index, and actually look up the chart. - idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name)) - i, err := repo.LoadIndexFile(idxFile) - if err != nil { - return u, errors.Wrap(err, "no cached repo found. (try 'helm repo update')") - } - - cv, err := i.Get(chartName, version) - if err != nil { - return u, errors.Wrapf(err, "chart %q matching %s not found in %s index. (try 'helm repo update')", chartName, version, r.Config.Name) - } - - if len(cv.URLs) == 0 { - return u, errors.Errorf("chart %q has no downloadable URLs", ref) - } - - // TODO: Seems that picking first URL is not fully correct - u, err = url.Parse(cv.URLs[0]) - if err != nil { - return u, errors.Errorf("invalid chart URL format: %s", ref) - } - - // If the URL is relative (no scheme), prepend the chart repo's base URL - if !u.IsAbs() { - repoURL, err := url.Parse(rc.URL) - if err != nil { - return repoURL, err - } - q := repoURL.Query() - // We need a trailing slash for ResolveReference to work, but make sure there isn't already one - repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/" - u = repoURL.ResolveReference(u) - u.RawQuery = q.Encode() - // TODO add user-agent - if _, err := getter.NewHTTPGetter(getter.WithURL(rc.URL)); err != nil { - return repoURL, err - } - if r != nil && r.Config != nil && r.Config.Username != "" && r.Config.Password != "" { - c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password)) - } - return u, err - } - - // TODO add user-agent - return u, nil -} - -// VerifyChart takes a path to a chart archive and a keyring, and verifies the chart. -// -// It assumes that a chart archive file is accompanied by a provenance file whose -// name is the archive file name plus the ".prov" extension. -func VerifyChart(path, keyring string) (*provenance.Verification, error) { - // For now, error out if it's not a tar file. - switch fi, err := os.Stat(path); { - case err != nil: - return nil, err - case fi.IsDir(): - return nil, errors.New("unpacked charts cannot be verified") - case !isTar(path): - return nil, errors.New("chart must be a tgz file") - } - - provfile := path + ".prov" - if _, err := os.Stat(provfile); err != nil { - return nil, errors.Wrapf(err, "could not load provenance file %s", provfile) - } - - sig, err := provenance.NewFromKeyring(keyring, "") - if err != nil { - return nil, errors.Wrap(err, "failed to load keyring") - } - return sig.Verify(path, provfile) -} - -// isTar tests whether the given file is a tar file. -// -// Currently, this simply checks extension, since a subsequent function will -// untar the file and validate its binary format. -func isTar(filename string) bool { - return strings.EqualFold(filepath.Ext(filename), ".tgz") -} - -func pickChartRepositoryConfigByName(name string, cfgs []*repo.Entry) (*repo.Entry, error) { - for _, rc := range cfgs { - if rc.Name == name { - if rc.URL == "" { - return nil, errors.Errorf("no URL found for repository %s", name) - } - return rc, nil - } - } - return nil, errors.Errorf("repo %s not found", name) -} - -// scanReposForURL scans all repos to find which repo contains the given URL. -// -// This will attempt to find the given URL in all of the known repositories files. -// -// If the URL is found, this will return the repo entry that contained that URL. -// -// If all of the repos are checked, but the URL is not found, an ErrNoOwnerRepo -// error is returned. -// -// Other errors may be returned when repositories cannot be loaded or searched. -// -// Technically, the fact that a URL is not found in a repo is not a failure indication. -// Charts are not required to be included in an index before they are valid. So -// be mindful of this case. -// -// The same URL can technically exist in two or more repositories. This algorithm -// will return the first one it finds. Order is determined by the order of repositories -// in the repositories.yaml file. -func (c *ChartDownloader) scanReposForURL(u string, rf *repo.File) (*repo.Entry, error) { - // FIXME: This is far from optimal. Larger installations and index files will - // incur a performance hit for this type of scanning. - for _, rc := range rf.Repositories { - r, err := repo.NewChartRepository(rc, c.Getters) - if err != nil { - return nil, err - } - - idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name)) - i, err := repo.LoadIndexFile(idxFile) - if err != nil { - return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')") - } - - for _, entry := range i.Entries { - for _, ver := range entry { - for _, dl := range ver.URLs { - if urlutil.Equal(u, dl) { - return rc, nil - } - } - } - } - } - // This means that there is no repo file for the given URL. - return nil, ErrNoOwnerRepo -} - -func loadRepoConfig(file string) (*repo.File, error) { - r, err := repo.LoadFile(file) - if err != nil && !os.IsNotExist(errors.Cause(err)) { - return nil, err - } - return r, nil -} diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go deleted file mode 100644 index e0692c8..0000000 --- a/pkg/downloader/chart_downloader_test.go +++ /dev/null @@ -1,305 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package downloader - -import ( - "net/http" - "os" - "path/filepath" - "testing" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/repo" - "helm.sh/helm/v3/pkg/repo/repotest" -) - -const ( - repoConfig = "testdata/repositories.yaml" - repoCache = "testdata/repository" -) - -func TestResolveChartRef(t *testing.T) { - tests := []struct { - name, ref, expect, version string - fail bool - }{ - {name: "full URL", ref: "http://example.com/foo-1.2.3.tgz", expect: "http://example.com/foo-1.2.3.tgz"}, - {name: "full URL, HTTPS", ref: "https://example.com/foo-1.2.3.tgz", expect: "https://example.com/foo-1.2.3.tgz"}, - {name: "full URL, with authentication", ref: "http://username:password@example.com/foo-1.2.3.tgz", expect: "http://username:password@example.com/foo-1.2.3.tgz"}, - {name: "reference, testing repo", ref: "testing/alpine", expect: "http://example.com/alpine-1.2.3.tgz"}, - {name: "reference, version, testing repo", ref: "testing/alpine", version: "0.2.0", expect: "http://example.com/alpine-0.2.0.tgz"}, - {name: "reference, version, malformed repo", ref: "malformed/alpine", version: "1.2.3", expect: "http://dl.example.com/alpine-1.2.3.tgz"}, - {name: "reference, querystring repo", ref: "testing-querystring/alpine", expect: "http://example.com/alpine-1.2.3.tgz?key=value"}, - {name: "reference, testing-relative repo", ref: "testing-relative/foo", expect: "http://example.com/helm/charts/foo-1.2.3.tgz"}, - {name: "reference, testing-relative repo", ref: "testing-relative/bar", expect: "http://example.com/helm/bar-1.2.3.tgz"}, - {name: "reference, testing-relative-trailing-slash repo", ref: "testing-relative-trailing-slash/foo", expect: "http://example.com/helm/charts/foo-1.2.3.tgz"}, - {name: "reference, testing-relative-trailing-slash repo", ref: "testing-relative-trailing-slash/bar", expect: "http://example.com/helm/bar-1.2.3.tgz"}, - {name: "full URL, HTTPS, irrelevant version", ref: "https://example.com/foo-1.2.3.tgz", version: "0.1.0", expect: "https://example.com/foo-1.2.3.tgz", fail: true}, - {name: "full URL, file", ref: "file:///foo-1.2.3.tgz", fail: true}, - {name: "invalid", ref: "invalid-1.2.3", fail: true}, - {name: "not found", ref: "nosuchthing/invalid-1.2.3", fail: true}, - } - - c := ChartDownloader{ - Out: os.Stderr, - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - Getters: getter.All(&cli.EnvSettings{ - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - }), - } - - for _, tt := range tests { - u, err := c.ResolveChartVersion(tt.ref, tt.version) - if err != nil { - if tt.fail { - continue - } - t.Errorf("%s: failed with error %s", tt.name, err) - continue - } - if got := u.String(); got != tt.expect { - t.Errorf("%s: expected %s, got %s", tt.name, tt.expect, got) - } - } -} - -func TestResolveChartOpts(t *testing.T) { - tests := []struct { - name, ref, version string - expect []getter.Option - }{ - { - name: "repo with CA-file", - ref: "testing-ca-file/foo", - expect: []getter.Option{ - getter.WithURL("https://example.com/foo-1.2.3.tgz"), - getter.WithTLSClientConfig("cert", "key", "ca"), - }, - }, - } - - c := ChartDownloader{ - Out: os.Stderr, - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - Getters: getter.All(&cli.EnvSettings{ - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - }), - } - - // snapshot options - snapshotOpts := c.Options - - for _, tt := range tests { - // reset chart downloader options for each test case - c.Options = snapshotOpts - - expect, err := getter.NewHTTPGetter(tt.expect...) - if err != nil { - t.Errorf("%s: failed to setup http client: %s", tt.name, err) - continue - } - - u, err := c.ResolveChartVersion(tt.ref, tt.version) - if err != nil { - t.Errorf("%s: failed with error %s", tt.name, err) - continue - } - - got, err := getter.NewHTTPGetter( - append( - c.Options, - getter.WithURL(u.String()), - )..., - ) - if err != nil { - t.Errorf("%s: failed to create http client: %s", tt.name, err) - continue - } - - if *(got.(*getter.HTTPGetter)) != *(expect.(*getter.HTTPGetter)) { - t.Errorf("%s: expected %s, got %s", tt.name, expect, got) - } - } -} - -func TestVerifyChart(t *testing.T) { - v, err := VerifyChart("testdata/signtest-0.1.0.tgz", "testdata/helm-test-key.pub") - if err != nil { - t.Fatal(err) - } - // The verification is tested at length in the provenance package. Here, - // we just want a quick sanity check that the v is not empty. - if len(v.FileHash) == 0 { - t.Error("Digest missing") - } -} - -func TestIsTar(t *testing.T) { - tests := map[string]bool{ - "foo.tgz": true, - "foo/bar/baz.tgz": true, - "foo-1.2.3.4.5.tgz": true, - "foo.tar.gz": false, // for our purposes - "foo.tgz.1": false, - "footgz": false, - } - - for src, expect := range tests { - if isTar(src) != expect { - t.Errorf("%q should be %t", src, expect) - } - } -} - -func TestDownloadTo(t *testing.T) { - // Set up a fake repo with basic auth enabled - srv, err := repotest.NewTempServer("testdata/*.tgz*") - srv.Stop() - if err != nil { - t.Fatal(err) - } - srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - username, password, ok := r.BasicAuth() - if !ok || username != "username" || password != "password" { - t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) - } - })) - srv.Start() - defer srv.Stop() - if err := srv.CreateIndex(); err != nil { - t.Fatal(err) - } - - if err := srv.LinkIndices(); err != nil { - t.Fatal(err) - } - - c := ChartDownloader{ - Out: os.Stderr, - Verify: VerifyAlways, - Keyring: "testdata/helm-test-key.pub", - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - Getters: getter.All(&cli.EnvSettings{ - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - }), - Options: []getter.Option{ - getter.WithBasicAuth("username", "password"), - }, - } - cname := "/signtest-0.1.0.tgz" - dest := srv.Root() - where, v, err := c.DownloadTo(srv.URL()+cname, "", dest) - if err != nil { - t.Fatal(err) - } - - if expect := filepath.Join(dest, cname); where != expect { - t.Errorf("Expected download to %s, got %s", expect, where) - } - - if v.FileHash == "" { - t.Error("File hash was empty, but verification is required.") - } - - if _, err := os.Stat(filepath.Join(dest, cname)); err != nil { - t.Error(err) - } -} - -func TestDownloadTo_VerifyLater(t *testing.T) { - defer ensure.HelmHome(t)() - - dest := ensure.TempDir(t) - - // Set up a fake repo - srv, err := repotest.NewTempServer("testdata/*.tgz*") - if err != nil { - t.Fatal(err) - } - defer srv.Stop() - if err := srv.LinkIndices(); err != nil { - t.Fatal(err) - } - - c := ChartDownloader{ - Out: os.Stderr, - Verify: VerifyLater, - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - Getters: getter.All(&cli.EnvSettings{ - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - }), - } - cname := "/signtest-0.1.0.tgz" - where, _, err := c.DownloadTo(srv.URL()+cname, "", dest) - if err != nil { - t.Fatal(err) - } - - if expect := filepath.Join(dest, cname); where != expect { - t.Errorf("Expected download to %s, got %s", expect, where) - } - - if _, err := os.Stat(filepath.Join(dest, cname)); err != nil { - t.Fatal(err) - } - if _, err := os.Stat(filepath.Join(dest, cname+".prov")); err != nil { - t.Fatal(err) - } -} - -func TestScanReposForURL(t *testing.T) { - c := ChartDownloader{ - Out: os.Stderr, - Verify: VerifyLater, - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - Getters: getter.All(&cli.EnvSettings{ - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - }), - } - - u := "http://example.com/alpine-0.2.0.tgz" - rf, err := repo.LoadFile(repoConfig) - if err != nil { - t.Fatal(err) - } - - entry, err := c.scanReposForURL(u, rf) - if err != nil { - t.Fatal(err) - } - - if entry.Name != "testing" { - t.Errorf("Unexpected repo %q for URL %q", entry.Name, u) - } - - // A lookup failure should produce an ErrNoOwnerRepo - u = "https://no.such.repo/foo/bar-1.23.4.tgz" - if _, err = c.scanReposForURL(u, rf); err != ErrNoOwnerRepo { - t.Fatalf("expected ErrNoOwnerRepo, got %v", err) - } -} diff --git a/pkg/downloader/doc.go b/pkg/downloader/doc.go deleted file mode 100644 index 9588a7d..0000000 --- a/pkg/downloader/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package downloader provides a library for downloading charts. - -This package contains various tools for downloading charts from repository -servers, and then storing them in Helm-specific directory structures. This -library contains many functions that depend on a specific -filesystem layout. -*/ -package downloader diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go deleted file mode 100644 index e46af69..0000000 --- a/pkg/downloader/manager.go +++ /dev/null @@ -1,698 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package downloader - -import ( - "fmt" - "io" - "io/ioutil" - "net/url" - "os" - "path" - "path/filepath" - "strings" - "sync" - - "github.com/Masterminds/semver/v3" - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/internal/resolver" - "helm.sh/helm/v3/internal/third_party/dep/fs" - "helm.sh/helm/v3/internal/urlutil" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/repo" -) - -// Manager handles the lifecycle of fetching, resolving, and storing dependencies. -type Manager struct { - // Out is used to print warnings and notifications. - Out io.Writer - // ChartPath is the path to the unpacked base chart upon which this operates. - ChartPath string - // Verification indicates whether the chart should be verified. - Verify VerificationStrategy - // Debug is the global "--debug" flag - Debug bool - // Keyring is the key ring file. - Keyring string - // SkipUpdate indicates that the repository should not be updated first. - SkipUpdate bool - // Getter collection for the operation - Getters []getter.Provider - RepositoryConfig string - RepositoryCache string -} - -// Build rebuilds a local charts directory from a lockfile. -// -// If the lockfile is not present, this will run a Manager.Update() -// -// If SkipUpdate is set, this will not update the repository. -func (m *Manager) Build() error { - c, err := m.loadChartDir() - if err != nil { - return err - } - - // If a lock file is found, run a build from that. Otherwise, just do - // an update. - lock := c.Lock - if lock == nil { - return m.Update() - } - - // Check that all of the repos we're dependent on actually exist. - req := c.Metadata.Dependencies - if _, err := m.resolveRepoNames(req); err != nil { - return err - } - - if sum, err := resolver.HashReq(req, lock.Dependencies); err != nil || sum != lock.Digest { - return errors.New("Chart.lock is out of sync with Chart.yaml") - } - - // Check that all of the repos we're dependent on actually exist. - if err := m.hasAllRepos(lock.Dependencies); err != nil { - return err - } - - if !m.SkipUpdate { - // For each repo in the file, update the cached copy of that repo - if err := m.UpdateRepositories(); err != nil { - return err - } - } - - // Now we need to fetch every package here into charts/ - return m.downloadAll(lock.Dependencies) -} - -// Update updates a local charts directory. -// -// It first reads the Chart.yaml file, and then attempts to -// negotiate versions based on that. It will download the versions -// from remote chart repositories unless SkipUpdate is true. -func (m *Manager) Update() error { - c, err := m.loadChartDir() - if err != nil { - return err - } - - // If no dependencies are found, we consider this a successful - // completion. - req := c.Metadata.Dependencies - if req == nil { - return nil - } - - // Check that all of the repos we're dependent on actually exist and - // the repo index names. - repoNames, err := m.resolveRepoNames(req) - if err != nil { - return err - } - - // For each repo in the file, update the cached copy of that repo - if !m.SkipUpdate { - if err := m.UpdateRepositories(); err != nil { - return err - } - } - - // Now we need to find out which version of a chart best satisfies the - // dependencies in the Chart.yaml - lock, err := m.resolve(req, repoNames) - if err != nil { - return err - } - - // Now we need to fetch every package here into charts/ - if err := m.downloadAll(lock.Dependencies); err != nil { - return err - } - - // downloadAll might overwrite dependency version, recalculate lock digest - newDigest, err := resolver.HashReq(req, lock.Dependencies) - if err != nil { - return err - } - lock.Digest = newDigest - - // If the lock file hasn't changed, don't write a new one. - oldLock := c.Lock - if oldLock != nil && oldLock.Digest == lock.Digest { - return nil - } - - // Finally, we need to write the lockfile. - return writeLock(m.ChartPath, lock, c.Metadata.APIVersion == chart.APIVersionV1) -} - -func (m *Manager) loadChartDir() (*chart.Chart, error) { - if fi, err := os.Stat(m.ChartPath); err != nil { - return nil, errors.Wrapf(err, "could not find %s", m.ChartPath) - } else if !fi.IsDir() { - return nil, errors.New("only unpacked charts can be updated") - } - return loader.LoadDir(m.ChartPath) -} - -// resolve takes a list of dependencies and translates them into an exact version to download. -// -// This returns a lock file, which has all of the dependencies normalized to a specific version. -func (m *Manager) resolve(req []*chart.Dependency, repoNames map[string]string) (*chart.Lock, error) { - res := resolver.New(m.ChartPath, m.RepositoryCache) - return res.Resolve(req, repoNames) -} - -// downloadAll takes a list of dependencies and downloads them into charts/ -// -// It will delete versions of the chart that exist on disk and might cause -// a conflict. -func (m *Manager) downloadAll(deps []*chart.Dependency) error { - repos, err := m.loadChartRepositories() - if err != nil { - return err - } - - destPath := filepath.Join(m.ChartPath, "charts") - tmpPath := filepath.Join(m.ChartPath, "tmpcharts") - - // Create 'charts' directory if it doesn't already exist. - if fi, err := os.Stat(destPath); err != nil { - if err := os.MkdirAll(destPath, 0755); err != nil { - return err - } - } else if !fi.IsDir() { - return errors.Errorf("%q is not a directory", destPath) - } - - if err := fs.RenameWithFallback(destPath, tmpPath); err != nil { - return errors.Wrap(err, "unable to move current charts to tmp dir") - } - - if err := os.MkdirAll(destPath, 0755); err != nil { - return err - } - - fmt.Fprintf(m.Out, "Saving %d charts\n", len(deps)) - var saveError error - for _, dep := range deps { - // No repository means the chart is in charts directory - if dep.Repository == "" { - fmt.Fprintf(m.Out, "Dependency %s did not declare a repository. Assuming it exists in the charts directory\n", dep.Name) - chartPath := filepath.Join(tmpPath, dep.Name) - ch, err := loader.LoadDir(chartPath) - if err != nil { - return fmt.Errorf("Unable to load chart: %v", err) - } - - constraint, err := semver.NewConstraint(dep.Version) - if err != nil { - return fmt.Errorf("Dependency %s has an invalid version/constraint format: %s", dep.Name, err) - } - - v, err := semver.NewVersion(ch.Metadata.Version) - if err != nil { - return fmt.Errorf("Invalid version %s for dependency %s: %s", dep.Version, dep.Name, err) - } - - if !constraint.Check(v) { - saveError = fmt.Errorf("Dependency %s at version %s does not satisfy the constraint %s", dep.Name, ch.Metadata.Version, dep.Version) - break - } - continue - } - if strings.HasPrefix(dep.Repository, "file://") { - if m.Debug { - fmt.Fprintf(m.Out, "Archiving %s from repo %s\n", dep.Name, dep.Repository) - } - ver, err := tarFromLocalDir(m.ChartPath, dep.Name, dep.Repository, dep.Version) - if err != nil { - saveError = err - break - } - dep.Version = ver - continue - } - - fmt.Fprintf(m.Out, "Downloading %s from repo %s\n", dep.Name, dep.Repository) - - // Any failure to resolve/download a chart should fail: - // https://github.com/helm/helm/issues/1439 - churl, username, password, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos) - if err != nil { - saveError = errors.Wrapf(err, "could not find %s", churl) - break - } - - dl := ChartDownloader{ - Out: m.Out, - Verify: m.Verify, - Keyring: m.Keyring, - RepositoryConfig: m.RepositoryConfig, - RepositoryCache: m.RepositoryCache, - Getters: m.Getters, - Options: []getter.Option{ - getter.WithBasicAuth(username, password), - }, - } - - if _, _, err := dl.DownloadTo(churl, "", destPath); err != nil { - saveError = errors.Wrapf(err, "could not download %s", churl) - break - } - } - - if saveError == nil { - fmt.Fprintln(m.Out, "Deleting outdated charts") - for _, dep := range deps { - // Chart from local charts directory stays in place - if dep.Repository != "" { - if err := m.safeDeleteDep(dep.Name, tmpPath); err != nil { - return err - } - } - } - if err := move(tmpPath, destPath); err != nil { - return err - } - if err := os.RemoveAll(tmpPath); err != nil { - return errors.Wrapf(err, "failed to remove %v", tmpPath) - } - } else { - fmt.Fprintln(m.Out, "Save error occurred: ", saveError) - fmt.Fprintln(m.Out, "Deleting newly downloaded charts, restoring pre-update state") - for _, dep := range deps { - if err := m.safeDeleteDep(dep.Name, destPath); err != nil { - return err - } - } - if err := os.RemoveAll(destPath); err != nil { - return errors.Wrapf(err, "failed to remove %v", destPath) - } - if err := fs.RenameWithFallback(tmpPath, destPath); err != nil { - return errors.Wrap(err, "unable to move current charts to tmp dir") - } - return saveError - } - return nil -} - -// safeDeleteDep deletes any versions of the given dependency in the given directory. -// -// It does this by first matching the file name to an expected pattern, then loading -// the file to verify that it is a chart with the same name as the given name. -// -// Because it requires tar file introspection, it is more intensive than a basic delete. -// -// This will only return errors that should stop processing entirely. Other errors -// will emit log messages or be ignored. -func (m *Manager) safeDeleteDep(name, dir string) error { - files, err := filepath.Glob(filepath.Join(dir, name+"-*.tgz")) - if err != nil { - // Only for ErrBadPattern - return err - } - for _, fname := range files { - ch, err := loader.LoadFile(fname) - if err != nil { - fmt.Fprintf(m.Out, "Could not verify %s for deletion: %s (Skipping)", fname, err) - continue - } - if ch.Name() != name { - // This is not the file you are looking for. - continue - } - if err := os.Remove(fname); err != nil { - fmt.Fprintf(m.Out, "Could not delete %s: %s (Skipping)", fname, err) - continue - } - } - return nil -} - -// hasAllRepos ensures that all of the referenced deps are in the local repo cache. -func (m *Manager) hasAllRepos(deps []*chart.Dependency) error { - rf, err := loadRepoConfig(m.RepositoryConfig) - if err != nil { - return err - } - repos := rf.Repositories - - // Verify that all repositories referenced in the deps are actually known - // by Helm. - missing := []string{} -Loop: - for _, dd := range deps { - // If repo is from local path, continue - if strings.HasPrefix(dd.Repository, "file://") { - continue - } - - if dd.Repository == "" { - continue - } - for _, repo := range repos { - if urlutil.Equal(repo.URL, strings.TrimSuffix(dd.Repository, "/")) { - continue Loop - } - } - missing = append(missing, dd.Repository) - } - if len(missing) > 0 { - return errors.Errorf("no repository definition for %s. Please add the missing repos via 'helm repo add'", strings.Join(missing, ", ")) - } - return nil -} - -// resolveRepoNames returns the repo names of the referenced deps which can be used to fetch the cached index file -// and replaces aliased repository URLs into resolved URLs in dependencies. -func (m *Manager) resolveRepoNames(deps []*chart.Dependency) (map[string]string, error) { - rf, err := loadRepoConfig(m.RepositoryConfig) - if err != nil { - if os.IsNotExist(err) { - return make(map[string]string), nil - } - return nil, err - } - repos := rf.Repositories - - reposMap := make(map[string]string) - - // Verify that all repositories referenced in the deps are actually known - // by Helm. - missing := []string{} - for _, dd := range deps { - // Don't map the repository, we don't need to download chart from charts directory - if dd.Repository == "" { - continue - } - // if dep chart is from local path, verify the path is valid - if strings.HasPrefix(dd.Repository, "file://") { - if _, err := resolver.GetLocalPath(dd.Repository, m.ChartPath); err != nil { - return nil, err - } - - if m.Debug { - fmt.Fprintf(m.Out, "Repository from local path: %s\n", dd.Repository) - } - reposMap[dd.Name] = dd.Repository - continue - } - - found := false - - for _, repo := range repos { - if (strings.HasPrefix(dd.Repository, "@") && strings.TrimPrefix(dd.Repository, "@") == repo.Name) || - (strings.HasPrefix(dd.Repository, "alias:") && strings.TrimPrefix(dd.Repository, "alias:") == repo.Name) { - found = true - dd.Repository = repo.URL - reposMap[dd.Name] = repo.Name - break - } else if urlutil.Equal(repo.URL, dd.Repository) { - found = true - reposMap[dd.Name] = repo.Name - break - } - } - if !found { - repository := dd.Repository - // Add if URL - _, err := url.ParseRequestURI(repository) - if err == nil { - reposMap[repository] = repository - continue - } - missing = append(missing, repository) - } - } - if len(missing) > 0 { - errorMessage := fmt.Sprintf("no repository definition for %s. Please add them via 'helm repo add'", strings.Join(missing, ", ")) - // It is common for people to try to enter "stable" as a repository instead of the actual URL. - // For this case, let's give them a suggestion. - containsNonURL := false - for _, repo := range missing { - if !strings.Contains(repo, "//") && !strings.HasPrefix(repo, "@") && !strings.HasPrefix(repo, "alias:") { - containsNonURL = true - } - } - if containsNonURL { - errorMessage += ` -Note that repositories must be URLs or aliases. For example, to refer to the "example" -repository, use "https://charts.example.com/" or "@example" instead of -"example". Don't forget to add the repo, too ('helm repo add').` - } - return nil, errors.New(errorMessage) - } - return reposMap, nil -} - -// UpdateRepositories updates all of the local repos to the latest. -func (m *Manager) UpdateRepositories() error { - rf, err := loadRepoConfig(m.RepositoryConfig) - if err != nil { - return err - } - repos := rf.Repositories - if len(repos) > 0 { - // This prints warnings straight to out. - if err := m.parallelRepoUpdate(repos); err != nil { - return err - } - } - return nil -} - -func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { - fmt.Fprintln(m.Out, "Hang tight while we grab the latest from your chart repositories...") - var wg sync.WaitGroup - for _, c := range repos { - r, err := repo.NewChartRepository(c, m.Getters) - if err != nil { - return err - } - wg.Add(1) - go func(r *repo.ChartRepository) { - if _, err := r.DownloadIndexFile(); err != nil { - fmt.Fprintf(m.Out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", r.Config.Name, r.Config.URL, err) - } else { - fmt.Fprintf(m.Out, "...Successfully got an update from the %q chart repository\n", r.Config.Name) - } - wg.Done() - }(r) - } - wg.Wait() - fmt.Fprintln(m.Out, "Update Complete. ⎈Happy Helming!⎈") - return nil -} - -// findChartURL searches the cache of repo data for a chart that has the name and the repoURL specified. -// -// 'name' is the name of the chart. Version is an exact semver, or an empty string. If empty, the -// newest version will be returned. -// -// repoURL is the repository to search -// -// If it finds a URL that is "relative", it will prepend the repoURL. -func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, err error) { - for _, cr := range repos { - if urlutil.Equal(repoURL, cr.Config.URL) { - var entry repo.ChartVersions - entry, err = findEntryByName(name, cr) - if err != nil { - return - } - var ve *repo.ChartVersion - ve, err = findVersionedEntry(version, entry) - if err != nil { - return - } - url, err = normalizeURL(repoURL, ve.URLs[0]) - if err != nil { - return - } - username = cr.Config.Username - password = cr.Config.Password - return - } - } - url, err = repo.FindChartInRepoURL(repoURL, name, version, "", "", "", m.Getters) - if err == nil { - return - } - err = errors.Errorf("chart %s not found in %s", name, repoURL) - return -} - -// findEntryByName finds an entry in the chart repository whose name matches the given name. -// -// It returns the ChartVersions for that entry. -func findEntryByName(name string, cr *repo.ChartRepository) (repo.ChartVersions, error) { - for ename, entry := range cr.IndexFile.Entries { - if ename == name { - return entry, nil - } - } - return nil, errors.New("entry not found") -} - -// findVersionedEntry takes a ChartVersions list and returns a single chart version that satisfies the version constraints. -// -// If version is empty, the first chart found is returned. -func findVersionedEntry(version string, vers repo.ChartVersions) (*repo.ChartVersion, error) { - for _, verEntry := range vers { - if len(verEntry.URLs) == 0 { - // Not a legit entry. - continue - } - - if version == "" || versionEquals(version, verEntry.Version) { - return verEntry, nil - } - } - return nil, errors.New("no matching version") -} - -func versionEquals(v1, v2 string) bool { - sv1, err := semver.NewVersion(v1) - if err != nil { - // Fallback to string comparison. - return v1 == v2 - } - sv2, err := semver.NewVersion(v2) - if err != nil { - return false - } - return sv1.Equal(sv2) -} - -func normalizeURL(baseURL, urlOrPath string) (string, error) { - u, err := url.Parse(urlOrPath) - if err != nil { - return urlOrPath, err - } - if u.IsAbs() { - return u.String(), nil - } - u2, err := url.Parse(baseURL) - if err != nil { - return urlOrPath, errors.Wrap(err, "base URL failed to parse") - } - - u2.Path = path.Join(u2.Path, urlOrPath) - return u2.String(), nil -} - -// loadChartRepositories reads the repositories.yaml, and then builds a map of -// ChartRepositories. -// -// The key is the local name (which is only present in the repositories.yaml). -func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, error) { - indices := map[string]*repo.ChartRepository{} - - // Load repositories.yaml file - rf, err := loadRepoConfig(m.RepositoryConfig) - if err != nil { - return indices, errors.Wrapf(err, "failed to load %s", m.RepositoryConfig) - } - - for _, re := range rf.Repositories { - lname := re.Name - idxFile := filepath.Join(m.RepositoryCache, helmpath.CacheIndexFile(lname)) - index, err := repo.LoadIndexFile(idxFile) - if err != nil { - return indices, err - } - - // TODO: use constructor - cr := &repo.ChartRepository{ - Config: re, - IndexFile: index, - } - indices[lname] = cr - } - return indices, nil -} - -// writeLock writes a lockfile to disk -func writeLock(chartpath string, lock *chart.Lock, legacyLockfile bool) error { - data, err := yaml.Marshal(lock) - if err != nil { - return err - } - lockfileName := "Chart.lock" - if legacyLockfile { - lockfileName = "requirements.lock" - } - dest := filepath.Join(chartpath, lockfileName) - return ioutil.WriteFile(dest, data, 0644) -} - -// archive a dep chart from local directory and save it into charts/ -func tarFromLocalDir(chartpath, name, repo, version string) (string, error) { - destPath := filepath.Join(chartpath, "charts") - - if !strings.HasPrefix(repo, "file://") { - return "", errors.Errorf("wrong format: chart %s repository %s", name, repo) - } - - origPath, err := resolver.GetLocalPath(repo, chartpath) - if err != nil { - return "", err - } - - ch, err := loader.LoadDir(origPath) - if err != nil { - return "", err - } - - constraint, err := semver.NewConstraint(version) - if err != nil { - return "", errors.Wrapf(err, "dependency %s has an invalid version/constraint format", name) - } - - v, err := semver.NewVersion(ch.Metadata.Version) - if err != nil { - return "", err - } - - if constraint.Check(v) { - _, err = chartutil.Save(ch, destPath) - return ch.Metadata.Version, err - } - - return "", errors.Errorf("can't get a valid version for dependency %s", name) -} - -// move files from tmppath to destpath -func move(tmpPath, destPath string) error { - files, _ := ioutil.ReadDir(tmpPath) - for _, file := range files { - filename := file.Name() - tmpfile := filepath.Join(tmpPath, filename) - destfile := filepath.Join(destPath, filename) - if err := fs.RenameWithFallback(tmpfile, destfile); err != nil { - return errors.Wrap(err, "unable to move local charts to charts dir") - } - } - return nil -} diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go deleted file mode 100644 index ea235c1..0000000 --- a/pkg/downloader/manager_test.go +++ /dev/null @@ -1,362 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package downloader - -import ( - "bytes" - "path/filepath" - "reflect" - "testing" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/repo/repotest" -) - -func TestVersionEquals(t *testing.T) { - tests := []struct { - name, v1, v2 string - expect bool - }{ - {name: "semver match", v1: "1.2.3-beta.11", v2: "1.2.3-beta.11", expect: true}, - {name: "semver match, build info", v1: "1.2.3-beta.11+a", v2: "1.2.3-beta.11+b", expect: true}, - {name: "string match", v1: "abcdef123", v2: "abcdef123", expect: true}, - {name: "semver mismatch", v1: "1.2.3-beta.11", v2: "1.2.3-beta.22", expect: false}, - {name: "semver mismatch, invalid semver", v1: "1.2.3-beta.11", v2: "stinkycheese", expect: false}, - } - - for _, tt := range tests { - if versionEquals(tt.v1, tt.v2) != tt.expect { - t.Errorf("%s: failed comparison of %q and %q (expect equal: %t)", tt.name, tt.v1, tt.v2, tt.expect) - } - } -} - -func TestNormalizeURL(t *testing.T) { - tests := []struct { - name, base, path, expect string - }{ - {name: "basic URL", base: "https://example.com", path: "http://helm.sh/foo", expect: "http://helm.sh/foo"}, - {name: "relative path", base: "https://helm.sh/charts", path: "foo", expect: "https://helm.sh/charts/foo"}, - } - - for _, tt := range tests { - got, err := normalizeURL(tt.base, tt.path) - if err != nil { - t.Errorf("%s: error %s", tt.name, err) - continue - } else if got != tt.expect { - t.Errorf("%s: expected %q, got %q", tt.name, tt.expect, got) - } - } -} - -func TestFindChartURL(t *testing.T) { - var b bytes.Buffer - m := &Manager{ - Out: &b, - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - } - repos, err := m.loadChartRepositories() - if err != nil { - t.Fatal(err) - } - - name := "alpine" - version := "0.1.0" - repoURL := "http://example.com/charts" - - churl, username, password, err := m.findChartURL(name, version, repoURL, repos) - if err != nil { - t.Fatal(err) - } - if churl != "https://kubernetes-charts.storage.googleapis.com/alpine-0.1.0.tgz" { - t.Errorf("Unexpected URL %q", churl) - } - if username != "" { - t.Errorf("Unexpected username %q", username) - } - if password != "" { - t.Errorf("Unexpected password %q", password) - } -} - -func TestGetRepoNames(t *testing.T) { - b := bytes.NewBuffer(nil) - m := &Manager{ - Out: b, - RepositoryConfig: repoConfig, - RepositoryCache: repoCache, - } - tests := []struct { - name string - req []*chart.Dependency - expect map[string]string - err bool - }{ - { - name: "no repo definition, but references a url", - req: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com/test"}, - }, - expect: map[string]string{"http://example.com/test": "http://example.com/test"}, - }, - { - name: "no repo definition failure -- stable repo", - req: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "stable"}, - }, - err: true, - }, - { - name: "no repo definition failure", - req: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "http://example.com"}, - }, - expect: map[string]string{"oedipus-rex": "testing"}, - }, - { - name: "repo from local path", - req: []*chart.Dependency{ - {Name: "local-dep", Repository: "file://./testdata/signtest"}, - }, - expect: map[string]string{"local-dep": "file://./testdata/signtest"}, - }, - { - name: "repo alias (alias:)", - req: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "alias:testing"}, - }, - expect: map[string]string{"oedipus-rex": "testing"}, - }, - { - name: "repo alias (@)", - req: []*chart.Dependency{ - {Name: "oedipus-rex", Repository: "@testing"}, - }, - expect: map[string]string{"oedipus-rex": "testing"}, - }, - { - name: "repo from local chart under charts path", - req: []*chart.Dependency{ - {Name: "local-subchart", Repository: ""}, - }, - expect: map[string]string{}, - }, - } - - for _, tt := range tests { - l, err := m.resolveRepoNames(tt.req) - if err != nil { - if tt.err { - continue - } - t.Fatal(err) - } - - if tt.err { - t.Fatalf("Expected error in test %q", tt.name) - } - - // m1 and m2 are the maps we want to compare - eq := reflect.DeepEqual(l, tt.expect) - if !eq { - t.Errorf("%s: expected map %v, got %v", tt.name, l, tt.name) - } - } -} - -func TestUpdateBeforeBuild(t *testing.T) { - // Set up a fake repo - srv, err := repotest.NewTempServer("testdata/*.tgz*") - if err != nil { - t.Fatal(err) - } - defer srv.Stop() - if err := srv.LinkIndices(); err != nil { - t.Fatal(err) - } - dir := func(p ...string) string { - return filepath.Join(append([]string{srv.Root()}, p...)...) - } - - // Save dep - d := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "dep-chart", - Version: "0.1.0", - APIVersion: "v1", - }, - } - if err := chartutil.SaveDir(d, dir()); err != nil { - t.Fatal(err) - } - // Save a chart - c := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "with-dependency", - Version: "0.1.0", - APIVersion: "v2", - Dependencies: []*chart.Dependency{{ - Name: d.Metadata.Name, - Version: ">=0.1.0", - Repository: "file://../dep-chart", - }}, - }, - } - if err := chartutil.SaveDir(c, dir()); err != nil { - t.Fatal(err) - } - - // Set-up a manager - b := bytes.NewBuffer(nil) - g := getter.Providers{getter.Provider{ - Schemes: []string{"http", "https"}, - New: getter.NewHTTPGetter, - }} - m := &Manager{ - ChartPath: dir(c.Metadata.Name), - Out: b, - Getters: g, - RepositoryConfig: dir("repositories.yaml"), - RepositoryCache: dir(), - } - - // Update before Build. see issue: https://github.com/helm/helm/issues/7101 - err = m.Update() - if err != nil { - t.Fatal(err) - } - - err = m.Build() - if err != nil { - t.Fatal(err) - } -} - -// This function is the skeleton test code of failing tests for #6416 and #6871 and bugs due to #5874. -// -// This function is used by below tests that ensures success of build operation -// with optional fields, alias, condition, tags, and even with ranged version. -// Parent chart includes local-subchart 0.1.0 subchart from a fake repository, by default. -// If each of these main fields (name, version, repository) is not supplied by dep param, default value will be used. -func checkBuildWithOptionalFields(t *testing.T, chartName string, dep chart.Dependency) { - // Set up a fake repo - srv, err := repotest.NewTempServer("testdata/*.tgz*") - if err != nil { - t.Fatal(err) - } - defer srv.Stop() - if err := srv.LinkIndices(); err != nil { - t.Fatal(err) - } - dir := func(p ...string) string { - return filepath.Join(append([]string{srv.Root()}, p...)...) - } - - // Set main fields if not exist - if dep.Name == "" { - dep.Name = "local-subchart" - } - if dep.Version == "" { - dep.Version = "0.1.0" - } - if dep.Repository == "" { - dep.Repository = srv.URL() - } - - // Save a chart - c := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: chartName, - Version: "0.1.0", - APIVersion: "v2", - Dependencies: []*chart.Dependency{&dep}, - }, - } - if err := chartutil.SaveDir(c, dir()); err != nil { - t.Fatal(err) - } - - // Set-up a manager - b := bytes.NewBuffer(nil) - g := getter.Providers{getter.Provider{ - Schemes: []string{"http", "https"}, - New: getter.NewHTTPGetter, - }} - m := &Manager{ - ChartPath: dir(chartName), - Out: b, - Getters: g, - RepositoryConfig: dir("repositories.yaml"), - RepositoryCache: dir(), - } - - // First build will update dependencies and create Chart.lock file. - err = m.Build() - if err != nil { - t.Fatal(err) - } - - // Second build should be passed. See PR #6655. - err = m.Build() - if err != nil { - t.Fatal(err) - } -} - -func TestBuild_WithoutOptionalFields(t *testing.T) { - // Dependency has main fields only (name/version/repository) - checkBuildWithOptionalFields(t, "without-optional-fields", chart.Dependency{}) -} - -func TestBuild_WithSemVerRange(t *testing.T) { - // Dependency version is the form of SemVer range - checkBuildWithOptionalFields(t, "with-semver-range", chart.Dependency{ - Version: ">=0.1.0", - }) -} - -func TestBuild_WithAlias(t *testing.T) { - // Dependency has an alias - checkBuildWithOptionalFields(t, "with-alias", chart.Dependency{ - Alias: "local-subchart-alias", - }) -} - -func TestBuild_WithCondition(t *testing.T) { - // Dependency has a condition - checkBuildWithOptionalFields(t, "with-condition", chart.Dependency{ - Condition: "some.condition", - }) -} - -func TestBuild_WithTags(t *testing.T) { - // Dependency has several tags - checkBuildWithOptionalFields(t, "with-tags", chart.Dependency{ - Tags: []string{"tag1", "tag2"}, - }) -} - -// Failing test for #6871 -func TestBuild_WithRepositoryAlias(t *testing.T) { - // Dependency repository is aliased in Chart.yaml - checkBuildWithOptionalFields(t, "with-repository-alias", chart.Dependency{ - Repository: "@test", - }) -} diff --git a/pkg/downloader/testdata/helm-test-key.pub b/pkg/downloader/testdata/helm-test-key.pub deleted file mode 100644 index 38714f2..0000000 Binary files a/pkg/downloader/testdata/helm-test-key.pub and /dev/null differ diff --git a/pkg/downloader/testdata/helm-test-key.secret b/pkg/downloader/testdata/helm-test-key.secret deleted file mode 100644 index a966aef..0000000 Binary files a/pkg/downloader/testdata/helm-test-key.secret and /dev/null differ diff --git a/pkg/downloader/testdata/local-subchart-0.1.0.tgz b/pkg/downloader/testdata/local-subchart-0.1.0.tgz deleted file mode 100644 index 4853121..0000000 Binary files a/pkg/downloader/testdata/local-subchart-0.1.0.tgz and /dev/null differ diff --git a/pkg/downloader/testdata/local-subchart/Chart.yaml b/pkg/downloader/testdata/local-subchart/Chart.yaml deleted file mode 100644 index 1e17203..0000000 --- a/pkg/downloader/testdata/local-subchart/Chart.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: A Helm chart for Kubernetes -name: local-subchart -version: 0.1.0 diff --git a/pkg/downloader/testdata/repositories.yaml b/pkg/downloader/testdata/repositories.yaml deleted file mode 100644 index 4308652..0000000 --- a/pkg/downloader/testdata/repositories.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v1 -repositories: - - name: testing - url: "http://example.com" - - name: testing-https - url: "https://example.com" - - name: testing-basicauth - url: "http://username:password@example.com" - - name: kubernetes-charts - url: "http://example.com/charts" - - name: malformed - url: "http://dl.example.com" - - name: testing-querystring - url: "http://example.com?key=value" - - name: testing-relative - url: "http://example.com/helm" - - name: testing-relative-trailing-slash - url: "http://example.com/helm/" - - name: testing-ca-file - url: "https://example.com" - certFile: "cert" - keyFile: "key" - caFile: "ca" diff --git a/pkg/downloader/testdata/repository/kubernetes-charts-index.yaml b/pkg/downloader/testdata/repository/kubernetes-charts-index.yaml deleted file mode 100644 index 9a46409..0000000 --- a/pkg/downloader/testdata/repository/kubernetes-charts-index.yaml +++ /dev/null @@ -1,46 +0,0 @@ -apiVersion: v1 -entries: - alpine: - - name: alpine - urls: - - https://kubernetes-charts.storage.googleapis.com/alpine-0.1.0.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.1.0 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - - name: alpine - urls: - - https://kubernetes-charts.storage.googleapis.com/alpine-0.2.0.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.2.0 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - mariadb: - - name: mariadb - urls: - - https://kubernetes-charts.storage.googleapis.com/mariadb-0.3.0.tgz - checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56 - home: https://mariadb.org - sources: - - https://github.com/bitnami/bitnami-docker-mariadb - version: 0.3.0 - description: Chart for MariaDB - keywords: - - mariadb - - mysql - - database - - sql - maintainers: - - name: Bitnami - email: containers@bitnami.com - icon: "" diff --git a/pkg/downloader/testdata/repository/malformed-index.yaml b/pkg/downloader/testdata/repository/malformed-index.yaml deleted file mode 100644 index 887e129..0000000 --- a/pkg/downloader/testdata/repository/malformed-index.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -entries: - alpine: - - name: alpine - urls: - - alpine-1.2.3.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 1.2.3 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" diff --git a/pkg/downloader/testdata/repository/testing-basicauth-index.yaml b/pkg/downloader/testdata/repository/testing-basicauth-index.yaml deleted file mode 100644 index da3ed51..0000000 --- a/pkg/downloader/testdata/repository/testing-basicauth-index.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -entries: - foo: - - name: foo - description: Foo Chart - home: https://helm.sh/helm - keywords: [] - maintainers: [] - sources: - - https://github.com/helm/charts - urls: - - http://username:password@example.com/foo-1.2.3.tgz - version: 1.2.3 - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d diff --git a/pkg/downloader/testdata/repository/testing-ca-file-index.yaml b/pkg/downloader/testdata/repository/testing-ca-file-index.yaml deleted file mode 100644 index 17cdde1..0000000 --- a/pkg/downloader/testdata/repository/testing-ca-file-index.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -entries: - foo: - - name: foo - description: Foo Chart - home: https://helm.sh/helm - keywords: [] - maintainers: [] - sources: - - https://github.com/helm/charts - urls: - - https://example.com/foo-1.2.3.tgz - version: 1.2.3 - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d diff --git a/pkg/downloader/testdata/repository/testing-https-index.yaml b/pkg/downloader/testdata/repository/testing-https-index.yaml deleted file mode 100644 index 17cdde1..0000000 --- a/pkg/downloader/testdata/repository/testing-https-index.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -entries: - foo: - - name: foo - description: Foo Chart - home: https://helm.sh/helm - keywords: [] - maintainers: [] - sources: - - https://github.com/helm/charts - urls: - - https://example.com/foo-1.2.3.tgz - version: 1.2.3 - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d diff --git a/pkg/downloader/testdata/repository/testing-index.yaml b/pkg/downloader/testdata/repository/testing-index.yaml deleted file mode 100644 index 16abc73..0000000 --- a/pkg/downloader/testdata/repository/testing-index.yaml +++ /dev/null @@ -1,40 +0,0 @@ -apiVersion: v1 -entries: - alpine: - - name: alpine - urls: - - http://example.com/alpine-1.2.3.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 1.2.3 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - - name: alpine - urls: - - http://example.com/alpine-0.2.0.tgz - - https://kubernetes-charts.storage.googleapis.com/alpine-0.2.0.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 0.2.0 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" - foo: - - name: foo - description: Foo Chart - home: https://helm.sh/helm - keywords: [] - maintainers: [] - sources: - - https://github.com/helm/charts - urls: - - http://example.com/foo-1.2.3.tgz - version: 1.2.3 - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d diff --git a/pkg/downloader/testdata/repository/testing-querystring-index.yaml b/pkg/downloader/testdata/repository/testing-querystring-index.yaml deleted file mode 100644 index 887e129..0000000 --- a/pkg/downloader/testdata/repository/testing-querystring-index.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -entries: - alpine: - - name: alpine - urls: - - alpine-1.2.3.tgz - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - home: https://helm.sh/helm - sources: - - https://github.com/helm/helm - version: 1.2.3 - description: Deploy a basic Alpine Linux pod - keywords: [] - maintainers: [] - icon: "" diff --git a/pkg/downloader/testdata/repository/testing-relative-index.yaml b/pkg/downloader/testdata/repository/testing-relative-index.yaml deleted file mode 100644 index 62197b6..0000000 --- a/pkg/downloader/testdata/repository/testing-relative-index.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -entries: - foo: - - name: foo - description: Foo Chart With Relative Path - home: https://helm.sh/helm - keywords: [] - maintainers: [] - sources: - - https://github.com/helm/charts - urls: - - charts/foo-1.2.3.tgz - version: 1.2.3 - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - bar: - - name: bar - description: Bar Chart With Relative Path - home: https://helm.sh/helm - keywords: [] - maintainers: [] - sources: - - https://github.com/helm/charts - urls: - - bar-1.2.3.tgz - version: 1.2.3 - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d diff --git a/pkg/downloader/testdata/repository/testing-relative-trailing-slash-index.yaml b/pkg/downloader/testdata/repository/testing-relative-trailing-slash-index.yaml deleted file mode 100644 index 62197b6..0000000 --- a/pkg/downloader/testdata/repository/testing-relative-trailing-slash-index.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: v1 -entries: - foo: - - name: foo - description: Foo Chart With Relative Path - home: https://helm.sh/helm - keywords: [] - maintainers: [] - sources: - - https://github.com/helm/charts - urls: - - charts/foo-1.2.3.tgz - version: 1.2.3 - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d - bar: - - name: bar - description: Bar Chart With Relative Path - home: https://helm.sh/helm - keywords: [] - maintainers: [] - sources: - - https://github.com/helm/charts - urls: - - bar-1.2.3.tgz - version: 1.2.3 - checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d diff --git a/pkg/downloader/testdata/signtest-0.1.0.tgz b/pkg/downloader/testdata/signtest-0.1.0.tgz deleted file mode 100644 index c74e5b0..0000000 Binary files a/pkg/downloader/testdata/signtest-0.1.0.tgz and /dev/null differ diff --git a/pkg/downloader/testdata/signtest-0.1.0.tgz.prov b/pkg/downloader/testdata/signtest-0.1.0.tgz.prov deleted file mode 100644 index d325bb2..0000000 --- a/pkg/downloader/testdata/signtest-0.1.0.tgz.prov +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA512 - -apiVersion: v1 -description: A Helm chart for Kubernetes -name: signtest -version: 0.1.0 - -... -files: - signtest-0.1.0.tgz: sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55 ------BEGIN PGP SIGNATURE----- - -wsBcBAEBCgAQBQJcoosfCRCEO7+YH8GHYgAA220IALAs8T8NPgkcLvHu+5109cAN -BOCNPSZDNsqLZW/2Dc9cKoBG7Jen4Qad+i5l9351kqn3D9Gm6eRfAWcjfggRobV/ -9daZ19h0nl4O1muQNAkjvdgZt8MOP3+PB3I3/Tu2QCYjI579SLUmuXlcZR5BCFPR -PJy+e3QpV2PcdeU2KZLG4tjtlrq+3QC9ZHHEJLs+BVN9d46Dwo6CxJdHJrrrAkTw -M8MhA92vbiTTPRSCZI9x5qDAwJYhoq0oxLflpuL2tIlo3qVoCsaTSURwMESEHO32 -XwYG7BaVDMELWhAorBAGBGBwWFbJ1677qQ2gd9CN0COiVhekWlFRcnn60800r84= -=k9Y9 ------END PGP SIGNATURE----- \ No newline at end of file diff --git a/pkg/downloader/testdata/signtest/.helmignore b/pkg/downloader/testdata/signtest/.helmignore deleted file mode 100644 index 435b756..0000000 --- a/pkg/downloader/testdata/signtest/.helmignore +++ /dev/null @@ -1,5 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -.git diff --git a/pkg/downloader/testdata/signtest/Chart.yaml b/pkg/downloader/testdata/signtest/Chart.yaml deleted file mode 100644 index f1f7372..0000000 --- a/pkg/downloader/testdata/signtest/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: signtest -version: 0.1.0 diff --git a/pkg/downloader/testdata/signtest/alpine/Chart.yaml b/pkg/downloader/testdata/signtest/alpine/Chart.yaml deleted file mode 100644 index eec2612..0000000 --- a/pkg/downloader/testdata/signtest/alpine/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -description: Deploy a basic Alpine Linux pod -home: https://helm.sh/helm -name: alpine -sources: -- https://github.com/helm/helm -version: 0.1.0 diff --git a/pkg/downloader/testdata/signtest/alpine/README.md b/pkg/downloader/testdata/signtest/alpine/README.md deleted file mode 100644 index 28bebae..0000000 --- a/pkg/downloader/testdata/signtest/alpine/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This example was generated using the command `helm create alpine`. - -The `templates/` directory contains a very simple pod resource with a -couple of parameters. - -The `values.yaml` file contains the default values for the -`alpine-pod.yaml` template. - -You can install this example using `helm install ./alpine`. diff --git a/pkg/downloader/testdata/signtest/alpine/templates/alpine-pod.yaml b/pkg/downloader/testdata/signtest/alpine/templates/alpine-pod.yaml deleted file mode 100644 index 5bbae10..0000000 --- a/pkg/downloader/testdata/signtest/alpine/templates/alpine-pod.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: {{.Release.Name}}-{{.Chart.Name}} - labels: - app.kubernetes.io/managed-by: {{.Release.Service}} - chartName: {{.Chart.Name}} - chartVersion: {{.Chart.Version | quote}} -spec: - restartPolicy: {{default "Never" .restart_policy}} - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/downloader/testdata/signtest/alpine/values.yaml b/pkg/downloader/testdata/signtest/alpine/values.yaml deleted file mode 100644 index bb6c06a..0000000 --- a/pkg/downloader/testdata/signtest/alpine/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name: my-alpine diff --git a/pkg/downloader/testdata/signtest/templates/pod.yaml b/pkg/downloader/testdata/signtest/templates/pod.yaml deleted file mode 100644 index 9b00cca..0000000 --- a/pkg/downloader/testdata/signtest/templates/pod.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: signtest -spec: - restartPolicy: Never - containers: - - name: waiter - image: "alpine:3.3" - command: ["/bin/sleep","9000"] diff --git a/pkg/downloader/testdata/signtest/values.yaml b/pkg/downloader/testdata/signtest/values.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/pkg/engine/doc.go b/pkg/engine/doc.go deleted file mode 100644 index a68b6f7..0000000 --- a/pkg/engine/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package engine implements the Go template engine as a Tiller Engine. - -Tiller provides a simple interface for taking a Chart and rendering its templates. -The 'engine' package implements this interface using Go's built-in 'text/template' -package. -*/ -package engine // import "helm.sh/helm/v3/pkg/engine" diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go deleted file mode 100644 index 4905610..0000000 --- a/pkg/engine/engine.go +++ /dev/null @@ -1,376 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "fmt" - "log" - "path" - "path/filepath" - "regexp" - "sort" - "strings" - "text/template" - - "github.com/pkg/errors" - "k8s.io/client-go/rest" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" -) - -// Engine is an implementation of 'cmd/tiller/environment'.Engine that uses Go templates. -type Engine struct { - // If strict is enabled, template rendering will fail if a template references - // a value that was not passed in. - Strict bool - // In LintMode, some 'required' template values may be missing, so don't fail - LintMode bool - // the rest config to connect to te kubernetes api - config *rest.Config -} - -// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates. -// -// Render can be called repeatedly on the same engine. -// -// This will look in the chart's 'templates' data (e.g. the 'templates/' directory) -// and attempt to render the templates there using the values passed in. -// -// Values are scoped to their templates. A dependency template will not have -// access to the values set for its parent. If chart "foo" includes chart "bar", -// "bar" will not have access to the values for "foo". -// -// Values should be prepared with something like `chartutils.ReadValues`. -// -// Values are passed through the templates according to scope. If the top layer -// chart includes the chart foo, which includes the chart bar, the values map -// will be examined for a table called "foo". If "foo" is found in vals, -// that section of the values will be passed into the "foo" chart. And if that -// section contains a value named "bar", that value will be passed on to the -// bar chart during render time. -func (e Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { - tmap := allTemplates(chrt, values) - return e.render(tmap) -} - -// Render takes a chart, optional values, and value overrides, and attempts to -// render the Go templates using the default options. -func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { - return new(Engine).Render(chrt, values) -} - -// RenderWithClient takes a chart, optional values, and value overrides, and attempts to -// render the Go templates using the default options. This engine is client aware and so can have template -// functions that interact with the client -func RenderWithClient(chrt *chart.Chart, values chartutil.Values, config *rest.Config) (map[string]string, error) { - return Engine{ - config: config, - }.Render(chrt, values) -} - -// renderable is an object that can be rendered. -type renderable struct { - // tpl is the current template. - tpl string - // vals are the values to be supplied to the template. - vals chartutil.Values - // namespace prefix to the templates of the current chart - basePath string -} - -const warnStartDelim = "HELM_ERR_START" -const warnEndDelim = "HELM_ERR_END" -const recursionMaxNums = 1000 - -var warnRegex = regexp.MustCompile(warnStartDelim + `(.*)` + warnEndDelim) - -func warnWrap(warn string) string { - return warnStartDelim + warn + warnEndDelim -} - -// initFunMap creates the Engine's FuncMap and adds context-specific functions. -func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) { - funcMap := funcMap() - includedNames := make(map[string]int) - - // Add the 'include' function here so we can close over t. - funcMap["include"] = func(name string, data interface{}) (string, error) { - var buf strings.Builder - if v, ok := includedNames[name]; ok { - if v > recursionMaxNums { - return "", errors.Wrapf(fmt.Errorf("unable to execute template"), "rendering template has a nested reference name: %s", name) - } - includedNames[name]++ - } else { - includedNames[name] = 1 - } - err := t.ExecuteTemplate(&buf, name, data) - return buf.String(), err - } - - // Add the 'tpl' function here - funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) { - basePath, err := vals.PathValue("Template.BasePath") - if err != nil { - return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl) - } - - templateName, err := vals.PathValue("Template.Name") - if err != nil { - return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl) - } - - templates := map[string]renderable{ - templateName.(string): { - tpl: tpl, - vals: vals, - basePath: basePath.(string), - }, - } - - result, err := e.renderWithReferences(templates, referenceTpls) - if err != nil { - return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl) - } - return result[templateName.(string)], nil - } - - // Add the `required` function here so we can use lintMode - funcMap["required"] = func(warn string, val interface{}) (interface{}, error) { - if val == nil { - if e.LintMode { - // Don't fail on missing required values when linting - log.Printf("[INFO] Missing required value: %s", warn) - return "", nil - } - return val, errors.Errorf(warnWrap(warn)) - } else if _, ok := val.(string); ok { - if val == "" { - if e.LintMode { - // Don't fail on missing required values when linting - log.Printf("[INFO] Missing required value: %s", warn) - return "", nil - } - return val, errors.Errorf(warnWrap(warn)) - } - } - return val, nil - } - if e.config != nil { - funcMap["lookup"] = NewLookupFunction(e.config) - } - - t.Funcs(funcMap) -} - -// render takes a map of templates/values and renders them. -func (e Engine) render(tpls map[string]renderable) (map[string]string, error) { - return e.renderWithReferences(tpls, tpls) -} - -// renderWithReferences takes a map of templates/values to render, and a map of -// templates which can be referenced within them. -func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) (rendered map[string]string, err error) { - // Basically, what we do here is start with an empty parent template and then - // build up a list of templates -- one for each file. Once all of the templates - // have been parsed, we loop through again and execute every template. - // - // The idea with this process is to make it possible for more complex templates - // to share common blocks, but to make the entire thing feel like a file-based - // template engine. - defer func() { - if r := recover(); r != nil { - err = errors.Errorf("rendering template failed: %v", r) - } - }() - t := template.New("gotpl") - if e.Strict { - t.Option("missingkey=error") - } else { - // Not that zero will attempt to add default values for types it knows, - // but will still emit for others. We mitigate that later. - t.Option("missingkey=zero") - } - - e.initFunMap(t, referenceTpls) - - // We want to parse the templates in a predictable order. The order favors - // higher-level (in file system) templates over deeply nested templates. - keys := sortTemplates(tpls) - - for _, filename := range keys { - r := tpls[filename] - if _, err := t.New(filename).Parse(r.tpl); err != nil { - return map[string]string{}, cleanupParseError(filename, err) - } - } - - // Adding the reference templates to the template context - // so they can be referenced in the tpl function - for filename, r := range referenceTpls { - if t.Lookup(filename) == nil { - if _, err := t.New(filename).Parse(r.tpl); err != nil { - return map[string]string{}, cleanupParseError(filename, err) - } - } - } - - rendered = make(map[string]string, len(keys)) - for _, filename := range keys { - // Don't render partials. We don't care out the direct output of partials. - // They are only included from other templates. - if strings.HasPrefix(path.Base(filename), "_") { - continue - } - // At render time, add information about the template that is being rendered. - vals := tpls[filename].vals - vals["Template"] = chartutil.Values{"Name": filename, "BasePath": tpls[filename].basePath} - var buf strings.Builder - if err := t.ExecuteTemplate(&buf, filename, vals); err != nil { - return map[string]string{}, cleanupExecError(filename, err) - } - - // Work around the issue where Go will emit "" even if Options(missing=zero) - // is set. Since missing=error will never get here, we do not need to handle - // the Strict case. - rendered[filename] = strings.ReplaceAll(buf.String(), "", "") - } - - return rendered, nil -} - -func cleanupParseError(filename string, err error) error { - tokens := strings.Split(err.Error(), ": ") - if len(tokens) == 1 { - // This might happen if a non-templating error occurs - return fmt.Errorf("parse error in (%s): %s", filename, err) - } - // The first token is "template" - // The second token is either "filename:lineno" or "filename:lineNo:columnNo" - location := tokens[1] - // The remaining tokens make up a stacktrace-like chain, ending with the relevant error - errMsg := tokens[len(tokens)-1] - return fmt.Errorf("parse error at (%s): %s", string(location), errMsg) -} - -func cleanupExecError(filename string, err error) error { - if _, isExecError := err.(template.ExecError); !isExecError { - return err - } - - tokens := strings.SplitN(err.Error(), ": ", 3) - if len(tokens) != 3 { - // This might happen if a non-templating error occurs - return fmt.Errorf("execution error in (%s): %s", filename, err) - } - - // The first token is "template" - // The second token is either "filename:lineno" or "filename:lineNo:columnNo" - location := tokens[1] - - parts := warnRegex.FindStringSubmatch(tokens[2]) - if len(parts) >= 2 { - return fmt.Errorf("execution error at (%s): %s", string(location), parts[1]) - } - - return err -} - -func sortTemplates(tpls map[string]renderable) []string { - keys := make([]string, len(tpls)) - i := 0 - for key := range tpls { - keys[i] = key - i++ - } - sort.Sort(sort.Reverse(byPathLen(keys))) - return keys -} - -type byPathLen []string - -func (p byPathLen) Len() int { return len(p) } -func (p byPathLen) Swap(i, j int) { p[j], p[i] = p[i], p[j] } -func (p byPathLen) Less(i, j int) bool { - a, b := p[i], p[j] - ca, cb := strings.Count(a, "/"), strings.Count(b, "/") - if ca == cb { - return strings.Compare(a, b) == -1 - } - return ca < cb -} - -// allTemplates returns all templates for a chart and its dependencies. -// -// As it goes, it also prepares the values in a scope-sensitive manner. -func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable { - templates := make(map[string]renderable) - recAllTpls(c, templates, vals) - return templates -} - -// recAllTpls recurses through the templates in a chart. -// -// As it recurses, it also sets the values to be appropriate for the template -// scope. -func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.Values) { - next := map[string]interface{}{ - "Chart": c.Metadata, - "Files": newFiles(c.Files), - "Release": vals["Release"], - "Capabilities": vals["Capabilities"], - "Values": make(chartutil.Values), - } - - // If there is a {{.Values.ThisChart}} in the parent metadata, - // copy that into the {{.Values}} for this template. - if c.IsRoot() { - next["Values"] = vals["Values"] - } else if vs, err := vals.Table("Values." + c.Name()); err == nil { - next["Values"] = vs - } - - for _, child := range c.Dependencies() { - recAllTpls(child, templates, next) - } - - newParentID := c.ChartFullPath() - for _, t := range c.Templates { - if !isTemplateValid(c, t.Name) { - continue - } - templates[path.Join(newParentID, t.Name)] = renderable{ - tpl: string(t.Data), - vals: next, - basePath: path.Join(newParentID, "templates"), - } - } -} - -// isTemplateValid returns true if the template is valid for the chart type -func isTemplateValid(ch *chart.Chart, templateName string) bool { - if isLibraryChart(ch) { - return strings.HasPrefix(filepath.Base(templateName), "_") - } - return true -} - -// isLibraryChart returns true if the chart is a library chart -func isLibraryChart(c *chart.Chart) bool { - return strings.EqualFold(c.Metadata.Type, "library") -} diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go deleted file mode 100644 index f3609fc..0000000 --- a/pkg/engine/engine_test.go +++ /dev/null @@ -1,645 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "fmt" - "strings" - "sync" - "testing" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" -) - -func TestSortTemplates(t *testing.T) { - tpls := map[string]renderable{ - "/mychart/templates/foo.tpl": {}, - "/mychart/templates/charts/foo/charts/bar/templates/foo.tpl": {}, - "/mychart/templates/bar.tpl": {}, - "/mychart/templates/charts/foo/templates/bar.tpl": {}, - "/mychart/templates/_foo.tpl": {}, - "/mychart/templates/charts/foo/templates/foo.tpl": {}, - "/mychart/templates/charts/bar/templates/foo.tpl": {}, - } - got := sortTemplates(tpls) - if len(got) != len(tpls) { - t.Fatal("Sorted results are missing templates") - } - - expect := []string{ - "/mychart/templates/charts/foo/charts/bar/templates/foo.tpl", - "/mychart/templates/charts/foo/templates/foo.tpl", - "/mychart/templates/charts/foo/templates/bar.tpl", - "/mychart/templates/charts/bar/templates/foo.tpl", - "/mychart/templates/foo.tpl", - "/mychart/templates/bar.tpl", - "/mychart/templates/_foo.tpl", - } - for i, e := range expect { - if got[i] != e { - t.Fatalf("\n\tExp:\n%s\n\tGot:\n%s", - strings.Join(expect, "\n"), - strings.Join(got, "\n"), - ) - } - } -} - -func TestFuncMap(t *testing.T) { - fns := funcMap() - forbidden := []string{"env", "expandenv"} - for _, f := range forbidden { - if _, ok := fns[f]; ok { - t.Errorf("Forbidden function %s exists in FuncMap.", f) - } - } - - // Test for Engine-specific template functions. - expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson"} - for _, f := range expect { - if _, ok := fns[f]; !ok { - t.Errorf("Expected add-on function %q", f) - } - } -} - -func TestRender(t *testing.T) { - c := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "moby", - Version: "1.2.3", - }, - Templates: []*chart.File{ - {Name: "templates/test1", Data: []byte("{{.Values.outer | title }} {{.Values.inner | title}}")}, - {Name: "templates/test2", Data: []byte("{{.Values.global.callme | lower }}")}, - {Name: "templates/test3", Data: []byte("{{.noValue}}")}, - {Name: "templates/test4", Data: []byte("{{toJson .Values}}")}, - }, - Values: map[string]interface{}{"outer": "DEFAULT", "inner": "DEFAULT"}, - } - - vals := map[string]interface{}{ - "Values": map[string]interface{}{ - "outer": "spouter", - "inner": "inn", - "global": map[string]interface{}{ - "callme": "Ishmael", - }, - }, - } - - v, err := chartutil.CoalesceValues(c, vals) - if err != nil { - t.Fatalf("Failed to coalesce values: %s", err) - } - out, err := Render(c, v) - if err != nil { - t.Errorf("Failed to render templates: %s", err) - } - - expect := map[string]string{ - "moby/templates/test1": "Spouter Inn", - "moby/templates/test2": "ishmael", - "moby/templates/test3": "", - "moby/templates/test4": `{"global":{"callme":"Ishmael"},"inner":"inn","outer":"spouter"}`, - } - - for name, data := range expect { - if out[name] != data { - t.Errorf("Expected %q, got %q", data, out[name]) - } - } -} - -func TestRenderInternals(t *testing.T) { - // Test the internals of the rendering tool. - - vals := chartutil.Values{"Name": "one", "Value": "two"} - tpls := map[string]renderable{ - "one": {tpl: `Hello {{title .Name}}`, vals: vals}, - "two": {tpl: `Goodbye {{upper .Value}}`, vals: vals}, - // Test whether a template can reliably reference another template - // without regard for ordering. - "three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals}, - } - - out, err := new(Engine).render(tpls) - if err != nil { - t.Fatalf("Failed template rendering: %s", err) - } - - if len(out) != 3 { - t.Fatalf("Expected 3 templates, got %d", len(out)) - } - - if out["one"] != "Hello One" { - t.Errorf("Expected 'Hello One', got %q", out["one"]) - } - - if out["two"] != "Goodbye TWO" { - t.Errorf("Expected 'Goodbye TWO'. got %q", out["two"]) - } - - if out["three"] != "Goodbye THREE" { - t.Errorf("Expected 'Goodbye THREE'. got %q", out["two"]) - } -} - -func TestParallelRenderInternals(t *testing.T) { - // Make sure that we can use one Engine to run parallel template renders. - e := new(Engine) - var wg sync.WaitGroup - for i := 0; i < 20; i++ { - wg.Add(1) - go func(i int) { - tt := fmt.Sprintf("expect-%d", i) - tpls := map[string]renderable{ - "t": { - tpl: `{{.val}}`, - vals: map[string]interface{}{"val": tt}, - }, - } - out, err := e.render(tpls) - if err != nil { - t.Errorf("Failed to render %s: %s", tt, err) - } - if out["t"] != tt { - t.Errorf("Expected %q, got %q", tt, out["t"]) - } - wg.Done() - }(i) - } - wg.Wait() -} - -func TestParseErrors(t *testing.T) { - vals := chartutil.Values{"Values": map[string]interface{}{}} - - tplsUndefinedFunction := map[string]renderable{ - "undefined_function": {tpl: `{{foo}}`, vals: vals}, - } - _, err := new(Engine).render(tplsUndefinedFunction) - if err == nil { - t.Fatalf("Expected failures while rendering: %s", err) - } - expected := `parse error at (undefined_function:1): function "foo" not defined` - if err.Error() != expected { - t.Errorf("Expected '%s', got %q", expected, err.Error()) - } -} - -func TestExecErrors(t *testing.T) { - vals := chartutil.Values{"Values": map[string]interface{}{}} - - tplsMissingRequired := map[string]renderable{ - "missing_required": {tpl: `{{required "foo is required" .Values.foo}}`, vals: vals}, - } - _, err := new(Engine).render(tplsMissingRequired) - if err == nil { - t.Fatalf("Expected failures while rendering: %s", err) - } - expected := `execution error at (missing_required:1:2): foo is required` - if err.Error() != expected { - t.Errorf("Expected '%s', got %q", expected, err.Error()) - } - - tplsMissingRequired = map[string]renderable{ - "missing_required_with_colons": {tpl: `{{required ":this: message: has many: colons:" .Values.foo}}`, vals: vals}, - } - _, err = new(Engine).render(tplsMissingRequired) - if err == nil { - t.Fatalf("Expected failures while rendering: %s", err) - } - expected = `execution error at (missing_required_with_colons:1:2): :this: message: has many: colons:` - if err.Error() != expected { - t.Errorf("Expected '%s', got %q", expected, err.Error()) - } - - issue6044tpl := `{{ $someEmptyValue := "" }} -{{ $myvar := "abc" }} -{{- required (printf "%s: something is missing" $myvar) $someEmptyValue | repeat 0 }}` - tplsMissingRequired = map[string]renderable{ - "issue6044": {tpl: issue6044tpl, vals: vals}, - } - _, err = new(Engine).render(tplsMissingRequired) - if err == nil { - t.Fatalf("Expected failures while rendering: %s", err) - } - expected = `execution error at (issue6044:3:4): abc: something is missing` - if err.Error() != expected { - t.Errorf("Expected '%s', got %q", expected, err.Error()) - } -} - -func TestAllTemplates(t *testing.T) { - ch1 := &chart.Chart{ - Metadata: &chart.Metadata{Name: "ch1"}, - Templates: []*chart.File{ - {Name: "templates/foo", Data: []byte("foo")}, - {Name: "templates/bar", Data: []byte("bar")}, - }, - } - dep1 := &chart.Chart{ - Metadata: &chart.Metadata{Name: "laboratory mice"}, - Templates: []*chart.File{ - {Name: "templates/pinky", Data: []byte("pinky")}, - {Name: "templates/brain", Data: []byte("brain")}, - }, - } - ch1.AddDependency(dep1) - - dep2 := &chart.Chart{ - Metadata: &chart.Metadata{Name: "same thing we do every night"}, - Templates: []*chart.File{ - {Name: "templates/innermost", Data: []byte("innermost")}, - }, - } - dep1.AddDependency(dep2) - - tpls := allTemplates(ch1, chartutil.Values{}) - if len(tpls) != 5 { - t.Errorf("Expected 5 charts, got %d", len(tpls)) - } -} - -func TestRenderDependency(t *testing.T) { - deptpl := `{{define "myblock"}}World{{end}}` - toptpl := `Hello {{template "myblock"}}` - ch := &chart.Chart{ - Metadata: &chart.Metadata{Name: "outerchart"}, - Templates: []*chart.File{ - {Name: "templates/outer", Data: []byte(toptpl)}, - }, - } - ch.AddDependency(&chart.Chart{ - Metadata: &chart.Metadata{Name: "innerchart"}, - Templates: []*chart.File{ - {Name: "templates/inner", Data: []byte(deptpl)}, - }, - }) - - out, err := Render(ch, map[string]interface{}{}) - if err != nil { - t.Fatalf("failed to render chart: %s", err) - } - - if len(out) != 2 { - t.Errorf("Expected 2, got %d", len(out)) - } - - expect := "Hello World" - if out["outerchart/templates/outer"] != expect { - t.Errorf("Expected %q, got %q", expect, out["outer"]) - } - -} - -func TestRenderNestedValues(t *testing.T) { - innerpath := "templates/inner.tpl" - outerpath := "templates/outer.tpl" - // Ensure namespacing rules are working. - deepestpath := "templates/inner.tpl" - checkrelease := "templates/release.tpl" - - deepest := &chart.Chart{ - Metadata: &chart.Metadata{Name: "deepest"}, - Templates: []*chart.File{ - {Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)}, - {Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)}, - }, - Values: map[string]interface{}{"what": "milkshake"}, - } - - inner := &chart.Chart{ - Metadata: &chart.Metadata{Name: "herrick"}, - Templates: []*chart.File{ - {Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)}, - }, - Values: map[string]interface{}{"who": "Robert"}, - } - inner.AddDependency(deepest) - - outer := &chart.Chart{ - Metadata: &chart.Metadata{Name: "top"}, - Templates: []*chart.File{ - {Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)}, - }, - Values: map[string]interface{}{ - "what": "stinkweed", - "who": "me", - "herrick": map[string]interface{}{ - "who": "time", - }, - }, - } - outer.AddDependency(inner) - - injValues := map[string]interface{}{ - "what": "rosebuds", - "herrick": map[string]interface{}{ - "deepest": map[string]interface{}{ - "what": "flower", - }, - }, - "global": map[string]interface{}{ - "when": "to-day", - }, - } - - tmp, err := chartutil.CoalesceValues(outer, injValues) - if err != nil { - t.Fatalf("Failed to coalesce values: %s", err) - } - - inject := chartutil.Values{ - "Values": tmp, - "Chart": outer.Metadata, - "Release": chartutil.Values{ - "Name": "dyin", - }, - } - - t.Logf("Calculated values: %v", inject) - - out, err := Render(outer, inject) - if err != nil { - t.Fatalf("failed to render templates: %s", err) - } - - fullouterpath := "top/" + outerpath - if out[fullouterpath] != "Gather ye rosebuds while ye may" { - t.Errorf("Unexpected outer: %q", out[fullouterpath]) - } - - fullinnerpath := "top/charts/herrick/" + innerpath - if out[fullinnerpath] != "Old time is still a-flyin'" { - t.Errorf("Unexpected inner: %q", out[fullinnerpath]) - } - - fulldeepestpath := "top/charts/herrick/charts/deepest/" + deepestpath - if out[fulldeepestpath] != "And this same flower that smiles to-day" { - t.Errorf("Unexpected deepest: %q", out[fulldeepestpath]) - } - - fullcheckrelease := "top/charts/herrick/charts/deepest/" + checkrelease - if out[fullcheckrelease] != "Tomorrow will be dyin" { - t.Errorf("Unexpected release: %q", out[fullcheckrelease]) - } -} - -func TestRenderBuiltinValues(t *testing.T) { - inner := &chart.Chart{ - Metadata: &chart.Metadata{Name: "Latium"}, - Templates: []*chart.File{ - {Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, - {Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)}, - }, - Files: []*chart.File{ - {Name: "author", Data: []byte("Virgil")}, - {Name: "book/title.txt", Data: []byte("Aeneid")}, - }, - } - - outer := &chart.Chart{ - Metadata: &chart.Metadata{Name: "Troy"}, - Templates: []*chart.File{ - {Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, - }, - } - outer.AddDependency(inner) - - inject := chartutil.Values{ - "Values": "", - "Chart": outer.Metadata, - "Release": chartutil.Values{ - "Name": "Aeneid", - }, - } - - t.Logf("Calculated values: %v", outer) - - out, err := Render(outer, inject) - if err != nil { - t.Fatalf("failed to render templates: %s", err) - } - - expects := map[string]string{ - "Troy/charts/Latium/templates/Lavinia": "Troy/charts/Latium/templates/LaviniaLatiumAeneid", - "Troy/templates/Aeneas": "Troy/templates/AeneasTroyAeneid", - "Troy/charts/Latium/templates/From": "Virgil Aeneid", - } - for file, expect := range expects { - if out[file] != expect { - t.Errorf("Expected %q, got %q", expect, out[file]) - } - } - -} - -func TestAlterFuncMap_include(t *testing.T) { - c := &chart.Chart{ - Metadata: &chart.Metadata{Name: "conrad"}, - Templates: []*chart.File{ - {Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)}, - {Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)}, - }, - } - - // Check nested reference in include FuncMap - d := &chart.Chart{ - Metadata: &chart.Metadata{Name: "nested"}, - Templates: []*chart.File{ - {Name: "templates/quote", Data: []byte(`{{include "nested/templates/quote" . | indent 2}} dead.`)}, - {Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)}, - }, - } - - v := chartutil.Values{ - "Values": "", - "Chart": c.Metadata, - "Release": chartutil.Values{ - "Name": "Mistah Kurtz", - }, - } - - out, err := Render(c, v) - if err != nil { - t.Fatal(err) - } - - expect := " Mistah Kurtz - he dead." - if got := out["conrad/templates/quote"]; got != expect { - t.Errorf("Expected %q, got %q (%v)", expect, got, out) - } - - _, err = Render(d, v) - expectErrName := "nested/templates/quote" - if err == nil { - t.Errorf("Expected err of nested reference name: %v", expectErrName) - } -} - -func TestAlterFuncMap_require(t *testing.T) { - c := &chart.Chart{ - Metadata: &chart.Metadata{Name: "conan"}, - Templates: []*chart.File{ - {Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)}, - {Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)}, - }, - } - - v := chartutil.Values{ - "Values": chartutil.Values{ - "who": "us", - "bases": 2, - }, - "Chart": c.Metadata, - "Release": chartutil.Values{ - "Name": "That 90s meme", - }, - } - - out, err := Render(c, v) - if err != nil { - t.Fatal(err) - } - - expectStr := "All your base are belong to us" - if gotStr := out["conan/templates/quote"]; gotStr != expectStr { - t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out) - } - expectNum := "All 2 of them!" - if gotNum := out["conan/templates/bases"]; gotNum != expectNum { - t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out) - } - - // test required without passing in needed values with lint mode on - // verifies lint replaces required with an empty string (should not fail) - lintValues := chartutil.Values{ - "Values": chartutil.Values{ - "who": "us", - }, - "Chart": c.Metadata, - "Release": chartutil.Values{ - "Name": "That 90s meme", - }, - } - var e Engine - e.LintMode = true - out, err = e.Render(c, lintValues) - if err != nil { - t.Fatal(err) - } - - expectStr = "All your base are belong to us" - if gotStr := out["conan/templates/quote"]; gotStr != expectStr { - t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out) - } - expectNum = "All of them!" - if gotNum := out["conan/templates/bases"]; gotNum != expectNum { - t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out) - } -} - -func TestAlterFuncMap_tpl(t *testing.T) { - c := &chart.Chart{ - Metadata: &chart.Metadata{Name: "TplFunction"}, - Templates: []*chart.File{ - {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)}, - }, - } - - v := chartutil.Values{ - "Values": chartutil.Values{ - "value": "myvalue", - }, - "Chart": c.Metadata, - "Release": chartutil.Values{ - "Name": "TestRelease", - }, - } - - out, err := Render(c, v) - if err != nil { - t.Fatal(err) - } - - expect := "Evaluate tpl Value: myvalue" - if got := out["TplFunction/templates/base"]; got != expect { - t.Errorf("Expected %q, got %q (%v)", expect, got, out) - } -} - -func TestAlterFuncMap_tplfunc(t *testing.T) { - c := &chart.Chart{ - Metadata: &chart.Metadata{Name: "TplFunction"}, - Templates: []*chart.File{ - {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)}, - }, - } - - v := chartutil.Values{ - "Values": chartutil.Values{ - "value": "myvalue", - }, - "Chart": c.Metadata, - "Release": chartutil.Values{ - "Name": "TestRelease", - }, - } - - out, err := Render(c, v) - if err != nil { - t.Fatal(err) - } - - expect := "Evaluate tpl Value: \"myvalue\"" - if got := out["TplFunction/templates/base"]; got != expect { - t.Errorf("Expected %q, got %q (%v)", expect, got, out) - } -} - -func TestAlterFuncMap_tplinclude(t *testing.T) { - c := &chart.Chart{ - Metadata: &chart.Metadata{Name: "TplFunction"}, - Templates: []*chart.File{ - {Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` . | quote }}" .}}`)}, - {Name: "templates/_partial", Data: []byte(`{{.Template.Name}}`)}, - }, - } - v := chartutil.Values{ - "Values": chartutil.Values{ - "value": "myvalue", - }, - "Chart": c.Metadata, - "Release": chartutil.Values{ - "Name": "TestRelease", - }, - } - - out, err := Render(c, v) - if err != nil { - t.Fatal(err) - } - - expect := "\"TplFunction/templates/base\"" - if got := out["TplFunction/templates/base"]; got != expect { - t.Errorf("Expected %q, got %q (%v)", expect, got, out) - } - -} diff --git a/pkg/engine/files.go b/pkg/engine/files.go deleted file mode 100644 index 3a6659d..0000000 --- a/pkg/engine/files.go +++ /dev/null @@ -1,160 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "encoding/base64" - "path" - "strings" - - "github.com/gobwas/glob" - - "helm.sh/helm/v3/pkg/chart" -) - -// files is a map of files in a chart that can be accessed from a template. -type files map[string][]byte - -// NewFiles creates a new files from chart files. -// Given an []*any.Any (the format for files in a chart.Chart), extract a map of files. -func newFiles(from []*chart.File) files { - files := make(map[string][]byte) - for _, f := range from { - files[f.Name] = f.Data - } - return files -} - -// GetBytes gets a file by path. -// -// The returned data is raw. In a template context, this is identical to calling -// {{index .Files $path}}. -// -// This is intended to be accessed from within a template, so a missed key returns -// an empty []byte. -func (f files) GetBytes(name string) []byte { - if v, ok := f[name]; ok { - return v - } - return []byte{} -} - -// Get returns a string representation of the given file. -// -// Fetch the contents of a file as a string. It is designed to be called in a -// template. -// -// {{.Files.Get "foo"}} -func (f files) Get(name string) string { - return string(f.GetBytes(name)) -} - -// Glob takes a glob pattern and returns another files object only containing -// matched files. -// -// This is designed to be called from a template. -// -// {{ range $name, $content := .Files.Glob("foo/**") }} -// {{ $name }}: | -// {{ .Files.Get($name) | indent 4 }}{{ end }} -func (f files) Glob(pattern string) files { - g, err := glob.Compile(pattern, '/') - if err != nil { - g, _ = glob.Compile("**") - } - - nf := newFiles(nil) - for name, contents := range f { - if g.Match(name) { - nf[name] = contents - } - } - - return nf -} - -// AsConfig turns a Files group and flattens it to a YAML map suitable for -// including in the 'data' section of a Kubernetes ConfigMap definition. -// Duplicate keys will be overwritten, so be aware that your file names -// (regardless of path) should be unique. -// -// This is designed to be called from a template, and will return empty string -// (via toYAML function) if it cannot be serialized to YAML, or if the Files -// object is nil. -// -// The output will not be indented, so you will want to pipe this to the -// 'indent' template function. -// -// data: -// {{ .Files.Glob("config/**").AsConfig() | indent 4 }} -func (f files) AsConfig() string { - if f == nil { - return "" - } - - m := make(map[string]string) - - // Explicitly convert to strings, and file names - for k, v := range f { - m[path.Base(k)] = string(v) - } - - return toYAML(m) -} - -// AsSecrets returns the base64-encoded value of a Files object suitable for -// including in the 'data' section of a Kubernetes Secret definition. -// Duplicate keys will be overwritten, so be aware that your file names -// (regardless of path) should be unique. -// -// This is designed to be called from a template, and will return empty string -// (via toYAML function) if it cannot be serialized to YAML, or if the Files -// object is nil. -// -// The output will not be indented, so you will want to pipe this to the -// 'indent' template function. -// -// data: -// {{ .Files.Glob("secrets/*").AsSecrets() }} -func (f files) AsSecrets() string { - if f == nil { - return "" - } - - m := make(map[string]string) - - for k, v := range f { - m[path.Base(k)] = base64.StdEncoding.EncodeToString(v) - } - - return toYAML(m) -} - -// Lines returns each line of a named file (split by "\n") as a slice, so it can -// be ranged over in your templates. -// -// This is designed to be called from a template. -// -// {{ range .Files.Lines "foo/bar.html" }} -// {{ . }}{{ end }} -func (f files) Lines(path string) []string { - if f == nil || f[path] == nil { - return []string{} - } - - return strings.Split(string(f[path]), "\n") -} diff --git a/pkg/engine/files_test.go b/pkg/engine/files_test.go deleted file mode 100644 index 4b37724..0000000 --- a/pkg/engine/files_test.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -var cases = []struct { - path, data string -}{ - {"ship/captain.txt", "The Captain"}, - {"ship/stowaway.txt", "Legatt"}, - {"story/name.txt", "The Secret Sharer"}, - {"story/author.txt", "Joseph Conrad"}, - {"multiline/test.txt", "bar\nfoo"}, -} - -func getTestFiles() files { - a := make(files, len(cases)) - for _, c := range cases { - a[c.path] = []byte(c.data) - } - return a -} - -func TestNewFiles(t *testing.T) { - files := getTestFiles() - if len(files) != len(cases) { - t.Errorf("Expected len() = %d, got %d", len(cases), len(files)) - } - - for i, f := range cases { - if got := string(files.GetBytes(f.path)); got != f.data { - t.Errorf("%d: expected %q, got %q", i, f.data, got) - } - if got := files.Get(f.path); got != f.data { - t.Errorf("%d: expected %q, got %q", i, f.data, got) - } - } -} - -func TestFileGlob(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - - matched := f.Glob("story/**") - - as.Len(matched, 2, "Should be two files in glob story/**") - as.Equal("Joseph Conrad", matched.Get("story/author.txt")) -} - -func TestToConfig(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - out := f.Glob("**/captain.txt").AsConfig() - as.Equal("captain.txt: The Captain", out) - - out = f.Glob("ship/**").AsConfig() - as.Equal("captain.txt: The Captain\nstowaway.txt: Legatt", out) -} - -func TestToSecret(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - - out := f.Glob("ship/**").AsSecrets() - as.Equal("captain.txt: VGhlIENhcHRhaW4=\nstowaway.txt: TGVnYXR0", out) -} - -func TestLines(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - - out := f.Lines("multiline/test.txt") - as.Len(out, 2) - - as.Equal("bar", out[0]) -} diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go deleted file mode 100644 index dac105e..0000000 --- a/pkg/engine/funcs.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "bytes" - "encoding/json" - "strings" - "text/template" - - "github.com/BurntSushi/toml" - "github.com/Masterminds/sprig/v3" - "sigs.k8s.io/yaml" -) - -// funcMap returns a mapping of all of the functions that Engine has. -// -// Because some functions are late-bound (e.g. contain context-sensitive -// data), the functions may not all perform identically outside of an Engine -// as they will inside of an Engine. -// -// Known late-bound functions: -// -// - "include" -// - "tpl" -// -// These are late-bound in Engine.Render(). The -// version included in the FuncMap is a placeholder. -// -func funcMap() template.FuncMap { - f := sprig.TxtFuncMap() - delete(f, "env") - delete(f, "expandenv") - - // Add some extra functionality - extra := template.FuncMap{ - "toToml": toTOML, - "toYaml": toYAML, - "fromYaml": fromYAML, - "toJson": toJSON, - "fromJson": fromJSON, - - // This is a placeholder for the "include" function, which is - // late-bound to a template. By declaring it here, we preserve the - // integrity of the linter. - "include": func(string, interface{}) string { return "not implemented" }, - "tpl": func(string, interface{}) interface{} { return "not implemented" }, - "required": func(string, interface{}) (interface{}, error) { return "not implemented", nil }, - } - - for k, v := range extra { - f[k] = v - } - - return f -} - -// toYAML takes an interface, marshals it to yaml, and returns a string. It will -// always return a string, even on marshal error (empty string). -// -// This is designed to be called from a template. -func toYAML(v interface{}) string { - data, err := yaml.Marshal(v) - if err != nil { - // Swallow errors inside of a template. - return "" - } - return strings.TrimSuffix(string(data), "\n") -} - -// fromYAML converts a YAML document into a map[string]interface{}. -// -// This is not a general-purpose YAML parser, and will not parse all valid -// YAML documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string into -// m["Error"] in the returned map. -func fromYAML(str string) map[string]interface{} { - m := map[string]interface{}{} - - if err := yaml.Unmarshal([]byte(str), &m); err != nil { - m["Error"] = err.Error() - } - return m -} - -// toTOML takes an interface, marshals it to toml, and returns a string. It will -// always return a string, even on marshal error (empty string). -// -// This is designed to be called from a template. -func toTOML(v interface{}) string { - b := bytes.NewBuffer(nil) - e := toml.NewEncoder(b) - err := e.Encode(v) - if err != nil { - return err.Error() - } - return b.String() -} - -// toJSON takes an interface, marshals it to json, and returns a string. It will -// always return a string, even on marshal error (empty string). -// -// This is designed to be called from a template. -func toJSON(v interface{}) string { - data, err := json.Marshal(v) - if err != nil { - // Swallow errors inside of a template. - return "" - } - return string(data) -} - -// fromJSON converts a JSON document into a map[string]interface{}. -// -// This is not a general-purpose JSON parser, and will not parse all valid -// JSON documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string into -// m["Error"] in the returned map. -func fromJSON(str string) map[string]interface{} { - m := make(map[string]interface{}) - - if err := json.Unmarshal([]byte(str), &m); err != nil { - m["Error"] = err.Error() - } - return m -} diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go deleted file mode 100644 index a94ff25..0000000 --- a/pkg/engine/funcs_test.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "strings" - "testing" - "text/template" - - "github.com/stretchr/testify/assert" -) - -func TestFuncs(t *testing.T) { - //TODO write tests for failure cases - tests := []struct { - tpl, expect string - vars interface{} - }{{ - tpl: `{{ toYaml . }}`, - expect: `foo: bar`, - vars: map[string]interface{}{"foo": "bar"}, - }, { - tpl: `{{ toToml . }}`, - expect: "foo = \"bar\"\n", - vars: map[string]interface{}{"foo": "bar"}, - }, { - tpl: `{{ toJson . }}`, - expect: `{"foo":"bar"}`, - vars: map[string]interface{}{"foo": "bar"}, - }, { - tpl: `{{ fromYaml . }}`, - expect: "map[hello:world]", - vars: `hello: world`, - }, { - // Regression for https://github.com/helm/helm/issues/2271 - tpl: `{{ toToml . }}`, - expect: "[mast]\n sail = \"white\"\n", - vars: map[string]map[string]string{"mast": {"sail": "white"}}, - }, { - tpl: `{{ fromYaml . }}`, - expect: "map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]", - vars: "- one\n- two\n", - }, { - tpl: `{{ fromJson .}}`, - expect: `map[hello:world]`, - vars: `{"hello":"world"}`, - }, { - tpl: `{{ fromJson . }}`, - expect: `map[Error:json: cannot unmarshal array into Go value of type map[string]interface {}]`, - vars: `["one", "two"]`, - }, { - tpl: `{{ merge .dict (fromYaml .yaml) }}`, - expect: `map[a:map[b:c]]`, - vars: map[string]interface{}{"dict": map[string]interface{}{"a": map[string]interface{}{"b": "c"}}, "yaml": `{"a":{"b":"d"}}`}, - }, { - tpl: `{{ merge (fromYaml .yaml) .dict }}`, - expect: `map[a:map[b:d]]`, - vars: map[string]interface{}{"dict": map[string]interface{}{"a": map[string]interface{}{"b": "c"}}, "yaml": `{"a":{"b":"d"}}`}, - }, { - tpl: `{{ fromYaml . }}`, - expect: `map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]`, - vars: `["one", "two"]`, - }} - - for _, tt := range tests { - var b strings.Builder - err := template.Must(template.New("test").Funcs(funcMap()).Parse(tt.tpl)).Execute(&b, tt.vars) - assert.NoError(t, err) - assert.Equal(t, tt.expect, b.String(), tt.tpl) - } -} diff --git a/pkg/engine/lookup_func.go b/pkg/engine/lookup_func.go deleted file mode 100644 index 5dde294..0000000 --- a/pkg/engine/lookup_func.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "log" - "strings" - - "github.com/pkg/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" -) - -type lookupFunc = func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) - -// NewLookupFunction returns a function for looking up objects in the cluster. If the resource does not exist, no error -// is raised. -func NewLookupFunction(config *rest.Config) lookupFunc { - return func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) { - var client dynamic.ResourceInterface - c, namespaced, err := getDynamicClientOnKind(apiversion, resource, config) - if err != nil { - return map[string]interface{}{}, err - } - if namespaced && namespace != "" { - client = c.Namespace(namespace) - } else { - client = c - } - if name != "" { - // this will return a single object - obj, err := client.Get(name, metav1.GetOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - // Just return an empty interface when the object was not found. - // That way, users can use `if not (lookup ...)` in their templates. - return map[string]interface{}{}, nil - } - return map[string]interface{}{}, err - } - return obj.UnstructuredContent(), nil - } - //this will return a list - obj, err := client.List(metav1.ListOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - // Just return an empty interface when the object was not found. - // That way, users can use `if not (lookup ...)` in their templates. - return map[string]interface{}{}, nil - } - return map[string]interface{}{}, err - } - return obj.UnstructuredContent(), nil - } -} - -// getDynamicClientOnUnstructured returns a dynamic client on an Unstructured type. This client can be further namespaced. -func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config) (dynamic.NamespaceableResourceInterface, bool, error) { - gvk := schema.FromAPIVersionAndKind(apiversion, kind) - apiRes, err := getAPIReourceForGVK(gvk, config) - if err != nil { - log.Printf("[ERROR] unable to get apiresource from unstructured: %s , error %s", gvk.String(), err) - return nil, false, errors.Wrapf(err, "unable to get apiresource from unstructured: %s", gvk.String()) - } - gvr := schema.GroupVersionResource{ - Group: apiRes.Group, - Version: apiRes.Version, - Resource: apiRes.Name, - } - intf, err := dynamic.NewForConfig(config) - if err != nil { - log.Printf("[ERROR] unable to get dynamic client %s", err) - return nil, false, err - } - res := intf.Resource(gvr) - return res, apiRes.Namespaced, nil -} - -func getAPIReourceForGVK(gvk schema.GroupVersionKind, config *rest.Config) (metav1.APIResource, error) { - res := metav1.APIResource{} - discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) - if err != nil { - log.Printf("[ERROR] unable to create discovery client %s", err) - return res, err - } - resList, err := discoveryClient.ServerResourcesForGroupVersion(gvk.GroupVersion().String()) - if err != nil { - log.Printf("[ERROR] unable to retrieve resource list for: %s , error: %s", gvk.GroupVersion().String(), err) - return res, err - } - for _, resource := range resList.APIResources { - //if a resource contains a "/" it's referencing a subresource. we don't support suberesource for now. - if resource.Kind == gvk.Kind && !strings.Contains(resource.Name, "/") { - res = resource - res.Group = gvk.Group - res.Version = gvk.Version - break - } - } - return res, nil -} diff --git a/pkg/gates/doc.go b/pkg/gates/doc.go deleted file mode 100644 index 762fdb8..0000000 --- a/pkg/gates/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package gates provides a general tool for working with experimental feature gates. - -This provides convenience methods where the user can determine if certain experimental features are enabled. -*/ -package gates diff --git a/pkg/gates/gates.go b/pkg/gates/gates.go deleted file mode 100644 index 6955921..0000000 --- a/pkg/gates/gates.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package gates - -import ( - "fmt" - "os" -) - -// Gate is the name of the feature gate. -type Gate string - -// String returns the string representation of this feature gate. -func (g Gate) String() string { - return string(g) -} - -// IsEnabled determines whether a certain feature gate is enabled. -func (g Gate) IsEnabled() bool { - return os.Getenv(string(g)) != "" -} - -func (g Gate) Error() error { - return fmt.Errorf("this feature has been marked as experimental and is not enabled by default. Please set %s=1 in your environment to use this feature", g.String()) -} diff --git a/pkg/gates/gates_test.go b/pkg/gates/gates_test.go deleted file mode 100644 index a71d490..0000000 --- a/pkg/gates/gates_test.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package gates - -import ( - "os" - "testing" -) - -const name string = "HELM_EXPERIMENTAL_FEATURE" - -func TestIsEnabled(t *testing.T) { - os.Unsetenv(name) - g := Gate(name) - - if g.IsEnabled() { - t.Errorf("feature gate shows as available, but the environment variable %s was not set", name) - } - - os.Setenv(name, "1") - - if !g.IsEnabled() { - t.Errorf("feature gate shows as disabled, but the environment variable %s was set", name) - } -} - -func TestError(t *testing.T) { - os.Unsetenv(name) - g := Gate(name) - - if g.Error().Error() != "this feature has been marked as experimental and is not enabled by default. Please set HELM_EXPERIMENTAL_FEATURE=1 in your environment to use this feature" { - t.Errorf("incorrect error message. Received %s", g.Error().Error()) - } -} diff --git a/pkg/getter/doc.go b/pkg/getter/doc.go deleted file mode 100644 index c53ef1a..0000000 --- a/pkg/getter/doc.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package getter provides a generalize tool for fetching data by scheme. - -This provides a method by which the plugin system can load arbitrary protocol -handlers based upon a URL scheme. -*/ -package getter diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go deleted file mode 100644 index 68638c2..0000000 --- a/pkg/getter/getter.go +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package getter - -import ( - "bytes" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/cli" -) - -// options are generic parameters to be provided to the getter during instantiation. -// -// Getters may or may not ignore these parameters as they are passed in. -type options struct { - url string - certFile string - keyFile string - caFile string - username string - password string - userAgent string -} - -// Option allows specifying various settings configurable by the user for overriding the defaults -// used when performing Get operations with the Getter. -type Option func(*options) - -// WithURL informs the getter the server name that will be used when fetching objects. Used in conjunction with -// WithTLSClientConfig to set the TLSClientConfig's server name. -func WithURL(url string) Option { - return func(opts *options) { - opts.url = url - } -} - -// WithBasicAuth sets the request's Authorization header to use the provided credentials -func WithBasicAuth(username, password string) Option { - return func(opts *options) { - opts.username = username - opts.password = password - } -} - -// WithUserAgent sets the request's User-Agent header to use the provided agent name. -func WithUserAgent(userAgent string) Option { - return func(opts *options) { - opts.userAgent = userAgent - } -} - -// WithTLSClientConfig sets the client auth with the provided credentials. -func WithTLSClientConfig(certFile, keyFile, caFile string) Option { - return func(opts *options) { - opts.certFile = certFile - opts.keyFile = keyFile - opts.caFile = caFile - } -} - -// Getter is an interface to support GET to the specified URL. -type Getter interface { - // Get file content by url string - Get(url string, options ...Option) (*bytes.Buffer, error) -} - -// Constructor is the function for every getter which creates a specific instance -// according to the configuration -type Constructor func(options ...Option) (Getter, error) - -// Provider represents any getter and the schemes that it supports. -// -// For example, an HTTP provider may provide one getter that handles both -// 'http' and 'https' schemes. -type Provider struct { - Schemes []string - New Constructor -} - -// Provides returns true if the given scheme is supported by this Provider. -func (p Provider) Provides(scheme string) bool { - for _, i := range p.Schemes { - if i == scheme { - return true - } - } - return false -} - -// Providers is a collection of Provider objects. -type Providers []Provider - -// ByScheme returns a Provider that handles the given scheme. -// -// If no provider handles this scheme, this will return an error. -func (p Providers) ByScheme(scheme string) (Getter, error) { - for _, pp := range p { - if pp.Provides(scheme) { - return pp.New() - } - } - return nil, errors.Errorf("scheme %q not supported", scheme) -} - -var httpProvider = Provider{ - Schemes: []string{"http", "https"}, - New: NewHTTPGetter, -} - -// All finds all of the registered getters as a list of Provider instances. -// Currently, the built-in getters and the discovered plugins with downloader -// notations are collected. -func All(settings *cli.EnvSettings) Providers { - result := Providers{httpProvider} - pluginDownloaders, _ := collectPlugins(settings) - result = append(result, pluginDownloaders...) - return result -} diff --git a/pkg/getter/getter_test.go b/pkg/getter/getter_test.go deleted file mode 100644 index 60eb473..0000000 --- a/pkg/getter/getter_test.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package getter - -import ( - "testing" - - "helm.sh/helm/v3/pkg/cli" -) - -const pluginDir = "testdata/plugins" - -func TestProvider(t *testing.T) { - p := Provider{ - []string{"one", "three"}, - func(_ ...Option) (Getter, error) { return nil, nil }, - } - - if !p.Provides("three") { - t.Error("Expected provider to provide three") - } -} - -func TestProviders(t *testing.T) { - ps := Providers{ - {[]string{"one", "three"}, func(_ ...Option) (Getter, error) { return nil, nil }}, - {[]string{"two", "four"}, func(_ ...Option) (Getter, error) { return nil, nil }}, - } - - if _, err := ps.ByScheme("one"); err != nil { - t.Error(err) - } - if _, err := ps.ByScheme("four"); err != nil { - t.Error(err) - } - - if _, err := ps.ByScheme("five"); err == nil { - t.Error("Did not expect handler for five") - } -} - -func TestAll(t *testing.T) { - all := All(&cli.EnvSettings{ - PluginsDirectory: pluginDir, - }) - if len(all) != 3 { - t.Errorf("expected 3 providers (default plus two plugins), got %d", len(all)) - } - - if _, err := all.ByScheme("test2"); err != nil { - t.Error(err) - } -} - -func TestByScheme(t *testing.T) { - g := All(&cli.EnvSettings{ - PluginsDirectory: pluginDir, - }) - if _, err := g.ByScheme("test"); err != nil { - t.Error(err) - } - if _, err := g.ByScheme("https"); err != nil { - t.Error(err) - } -} diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go deleted file mode 100644 index 5b476ff..0000000 --- a/pkg/getter/httpgetter.go +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package getter - -import ( - "bytes" - "io" - "net/http" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/internal/tlsutil" - "helm.sh/helm/v3/internal/urlutil" - "helm.sh/helm/v3/internal/version" -) - -// HTTPGetter is the default HTTP(/S) backend handler -type HTTPGetter struct { - opts options -} - -//Get performs a Get from repo.Getter and returns the body. -func (g *HTTPGetter) Get(href string, options ...Option) (*bytes.Buffer, error) { - for _, opt := range options { - opt(&g.opts) - } - return g.get(href) -} - -func (g *HTTPGetter) get(href string) (*bytes.Buffer, error) { - buf := bytes.NewBuffer(nil) - - // Set a helm specific user agent so that a repo server and metrics can - // separate helm calls from other tools interacting with repos. - req, err := http.NewRequest("GET", href, nil) - if err != nil { - return buf, err - } - - req.Header.Set("User-Agent", version.GetUserAgent()) - if g.opts.userAgent != "" { - req.Header.Set("User-Agent", g.opts.userAgent) - } - - if g.opts.username != "" && g.opts.password != "" { - req.SetBasicAuth(g.opts.username, g.opts.password) - } - - client, err := g.httpClient() - if err != nil { - return nil, err - } - - resp, err := client.Do(req) - if err != nil { - return buf, err - } - if resp.StatusCode != 200 { - return buf, errors.Errorf("failed to fetch %s : %s", href, resp.Status) - } - - _, err = io.Copy(buf, resp.Body) - resp.Body.Close() - return buf, err -} - -// NewHTTPGetter constructs a valid http/https client as a Getter -func NewHTTPGetter(options ...Option) (Getter, error) { - var client HTTPGetter - - for _, opt := range options { - opt(&client.opts) - } - - return &client, nil -} - -func (g *HTTPGetter) httpClient() (*http.Client, error) { - if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" { - tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile) - if err != nil { - return nil, errors.Wrap(err, "can't create TLS config for client") - } - tlsConf.BuildNameToCertificate() - - sni, err := urlutil.ExtractHostname(g.opts.url) - if err != nil { - return nil, err - } - tlsConf.ServerName = sni - - client := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: tlsConf, - Proxy: http.ProxyFromEnvironment, - }, - } - - return client, nil - } - return http.DefaultClient, nil -} diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go deleted file mode 100644 index b200855..0000000 --- a/pkg/getter/httpgetter_test.go +++ /dev/null @@ -1,193 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package getter - -import ( - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "path/filepath" - "strings" - "testing" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/internal/tlsutil" - "helm.sh/helm/v3/internal/version" - "helm.sh/helm/v3/pkg/cli" -) - -func TestHTTPGetter(t *testing.T) { - g, err := NewHTTPGetter(WithURL("http://example.com")) - if err != nil { - t.Fatal(err) - } - - if _, ok := g.(*HTTPGetter); !ok { - t.Fatal("Expected NewHTTPGetter to produce an *HTTPGetter") - } - - cd := "../../testdata" - join := filepath.Join - ca, pub, priv := join(cd, "rootca.crt"), join(cd, "crt.pem"), join(cd, "key.pem") - - // Test with options - g, err = NewHTTPGetter( - WithBasicAuth("I", "Am"), - WithUserAgent("Groot"), - WithTLSClientConfig(pub, priv, ca), - ) - if err != nil { - t.Fatal(err) - } - - hg, ok := g.(*HTTPGetter) - if !ok { - t.Fatal("expected NewHTTPGetter to produce an *HTTPGetter") - } - - if hg.opts.username != "I" { - t.Errorf("Expected NewHTTPGetter to contain %q as the username, got %q", "I", hg.opts.username) - } - - if hg.opts.password != "Am" { - t.Errorf("Expected NewHTTPGetter to contain %q as the password, got %q", "Am", hg.opts.password) - } - - if hg.opts.userAgent != "Groot" { - t.Errorf("Expected NewHTTPGetter to contain %q as the user agent, got %q", "Groot", hg.opts.userAgent) - } - - if hg.opts.certFile != pub { - t.Errorf("Expected NewHTTPGetter to contain %q as the public key file, got %q", pub, hg.opts.certFile) - } - - if hg.opts.keyFile != priv { - t.Errorf("Expected NewHTTPGetter to contain %q as the private key file, got %q", priv, hg.opts.keyFile) - } - - if hg.opts.caFile != ca { - t.Errorf("Expected NewHTTPGetter to contain %q as the CA file, got %q", ca, hg.opts.caFile) - } -} - -func TestDownload(t *testing.T) { - expect := "Call me Ishmael" - expectedUserAgent := "I am Groot" - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defaultUserAgent := "Helm/" + strings.TrimPrefix(version.GetVersion(), "v") - if r.UserAgent() != defaultUserAgent { - t.Errorf("Expected '%s', got '%s'", defaultUserAgent, r.UserAgent()) - } - fmt.Fprint(w, expect) - })) - defer srv.Close() - - g, err := All(new(cli.EnvSettings)).ByScheme("http") - if err != nil { - t.Fatal(err) - } - got, err := g.Get(srv.URL, WithURL(srv.URL)) - if err != nil { - t.Fatal(err) - } - - if got.String() != expect { - t.Errorf("Expected %q, got %q", expect, got.String()) - } - - // test with http server - basicAuthSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - username, password, ok := r.BasicAuth() - if !ok || username != "username" || password != "password" { - t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password) - } - if r.UserAgent() != expectedUserAgent { - t.Errorf("Expected '%s', got '%s'", expectedUserAgent, r.UserAgent()) - } - fmt.Fprint(w, expect) - })) - - defer basicAuthSrv.Close() - - u, _ := url.ParseRequestURI(basicAuthSrv.URL) - httpgetter, err := NewHTTPGetter( - WithURL(u.String()), - WithBasicAuth("username", "password"), - WithUserAgent(expectedUserAgent), - ) - if err != nil { - t.Fatal(err) - } - got, err = httpgetter.Get(u.String()) - if err != nil { - t.Fatal(err) - } - - if got.String() != expect { - t.Errorf("Expected %q, got %q", expect, got.String()) - } -} - -func TestDownloadTLS(t *testing.T) { - cd := "../../testdata" - ca, pub, priv := filepath.Join(cd, "rootca.crt"), filepath.Join(cd, "crt.pem"), filepath.Join(cd, "key.pem") - - tlsSrv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) - tlsConf, err := tlsutil.NewClientTLS(pub, priv, ca) - if err != nil { - t.Fatal(errors.Wrap(err, "can't create TLS config for client")) - } - tlsConf.BuildNameToCertificate() - tlsConf.ServerName = "helm.sh" - tlsSrv.TLS = tlsConf - tlsSrv.StartTLS() - defer tlsSrv.Close() - - u, _ := url.ParseRequestURI(tlsSrv.URL) - g, err := NewHTTPGetter( - WithURL(u.String()), - WithTLSClientConfig(pub, priv, ca), - ) - if err != nil { - t.Fatal(err) - } - - if _, err := g.Get(u.String()); err != nil { - t.Error(err) - } - - // now test with TLS config being passed along in .Get (see #6635) - g, err = NewHTTPGetter() - if err != nil { - t.Fatal(err) - } - - if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig(pub, priv, ca)); err != nil { - t.Error(err) - } - - // test with only the CA file (see also #6635) - g, err = NewHTTPGetter() - if err != nil { - t.Fatal(err) - } - - if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig("", "", ca)); err != nil { - t.Error(err) - } -} diff --git a/pkg/getter/plugingetter.go b/pkg/getter/plugingetter.go deleted file mode 100644 index 0d13ade..0000000 --- a/pkg/getter/plugingetter.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package getter - -import ( - "bytes" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/plugin" -) - -// collectPlugins scans for getter plugins. -// This will load plugins according to the cli. -func collectPlugins(settings *cli.EnvSettings) (Providers, error) { - plugins, err := plugin.FindPlugins(settings.PluginsDirectory) - if err != nil { - return nil, err - } - var result Providers - for _, plugin := range plugins { - for _, downloader := range plugin.Metadata.Downloaders { - result = append(result, Provider{ - Schemes: downloader.Protocols, - New: NewPluginGetter( - downloader.Command, - settings, - plugin.Metadata.Name, - plugin.Dir, - ), - }) - } - } - return result, nil -} - -// pluginGetter is a generic type to invoke custom downloaders, -// implemented in plugins. -type pluginGetter struct { - command string - settings *cli.EnvSettings - name string - base string - opts options -} - -// Get runs downloader plugin command -func (p *pluginGetter) Get(href string, options ...Option) (*bytes.Buffer, error) { - for _, opt := range options { - opt(&p.opts) - } - commands := strings.Split(p.command, " ") - argv := append(commands[1:], p.opts.certFile, p.opts.keyFile, p.opts.caFile, href) - prog := exec.Command(filepath.Join(p.base, commands[0]), argv...) - plugin.SetupPluginEnv(p.settings, p.name, p.base) - prog.Env = os.Environ() - buf := bytes.NewBuffer(nil) - prog.Stdout = buf - prog.Stderr = os.Stderr - if err := prog.Run(); err != nil { - if eerr, ok := err.(*exec.ExitError); ok { - os.Stderr.Write(eerr.Stderr) - return nil, errors.Errorf("plugin %q exited with error", p.command) - } - return nil, err - } - return buf, nil -} - -// NewPluginGetter constructs a valid plugin getter -func NewPluginGetter(command string, settings *cli.EnvSettings, name, base string) Constructor { - return func(options ...Option) (Getter, error) { - result := &pluginGetter{ - command: command, - settings: settings, - name: name, - base: base, - } - for _, opt := range options { - opt(&result.opts) - } - return result, nil - } -} diff --git a/pkg/getter/plugingetter_test.go b/pkg/getter/plugingetter_test.go deleted file mode 100644 index 71563e1..0000000 --- a/pkg/getter/plugingetter_test.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package getter - -import ( - "runtime" - "strings" - "testing" - - "helm.sh/helm/v3/pkg/cli" -) - -func TestCollectPlugins(t *testing.T) { - env := &cli.EnvSettings{ - PluginsDirectory: pluginDir, - } - p, err := collectPlugins(env) - if err != nil { - t.Fatal(err) - } - - if len(p) != 2 { - t.Errorf("Expected 2 plugins, got %d: %v", len(p), p) - } - - if _, err := p.ByScheme("test2"); err != nil { - t.Error(err) - } - - if _, err := p.ByScheme("test"); err != nil { - t.Error(err) - } - - if _, err := p.ByScheme("nosuchthing"); err == nil { - t.Fatal("did not expect protocol handler for nosuchthing") - } -} - -func TestPluginGetter(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("TODO: refactor this test to work on windows") - } - - env := &cli.EnvSettings{ - PluginsDirectory: pluginDir, - } - pg := NewPluginGetter("echo", env, "test", ".") - g, err := pg() - if err != nil { - t.Fatal(err) - } - - data, err := g.Get("test://foo/bar") - if err != nil { - t.Fatal(err) - } - - expect := "test://foo/bar" - got := strings.TrimSpace(data.String()) - if got != expect { - t.Errorf("Expected %q, got %q", expect, got) - } -} - -func TestPluginSubCommands(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("TODO: refactor this test to work on windows") - } - - env := &cli.EnvSettings{ - PluginsDirectory: pluginDir, - } - pg := NewPluginGetter("echo -n", env, "test", ".") - g, err := pg() - if err != nil { - t.Fatal(err) - } - - data, err := g.Get("test://foo/bar") - if err != nil { - t.Fatal(err) - } - - expect := " test://foo/bar" - got := data.String() - if got != expect { - t.Errorf("Expected %q, got %q", expect, got) - } -} diff --git a/pkg/getter/testdata/plugins/testgetter/get.sh b/pkg/getter/testdata/plugins/testgetter/get.sh deleted file mode 100755 index cdd9923..0000000 --- a/pkg/getter/testdata/plugins/testgetter/get.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -echo ENVIRONMENT -env - -echo "" -echo ARGUMENTS -echo $@ diff --git a/pkg/getter/testdata/plugins/testgetter/plugin.yaml b/pkg/getter/testdata/plugins/testgetter/plugin.yaml deleted file mode 100644 index d1b929e..0000000 --- a/pkg/getter/testdata/plugins/testgetter/plugin.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: "testgetter" -version: "0.1.0" -usage: "Fetch a package from a test:// source" -description: |- - Print the environment that the plugin was given, then exit. - - This registers the test:// protocol. - -command: "$HELM_PLUGIN_DIR/get.sh" -ignoreFlags: true -downloaders: -#- command: "$HELM_PLUGIN_DIR/get.sh" -- command: "echo" - protocols: - - "test" diff --git a/pkg/getter/testdata/plugins/testgetter2/get.sh b/pkg/getter/testdata/plugins/testgetter2/get.sh deleted file mode 100755 index cdd9923..0000000 --- a/pkg/getter/testdata/plugins/testgetter2/get.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -echo ENVIRONMENT -env - -echo "" -echo ARGUMENTS -echo $@ diff --git a/pkg/getter/testdata/plugins/testgetter2/plugin.yaml b/pkg/getter/testdata/plugins/testgetter2/plugin.yaml deleted file mode 100644 index f1a527e..0000000 --- a/pkg/getter/testdata/plugins/testgetter2/plugin.yaml +++ /dev/null @@ -1,10 +0,0 @@ -name: "testgetter2" -version: "0.1.0" -usage: "Fetch a different package from a test2:// source" -description: "Handle test2 scheme" -command: "$HELM_PLUGIN_DIR/get.sh" -ignoreFlags: true -downloaders: -- command: "echo" - protocols: - - "test2" diff --git a/pkg/getter/testdata/repository/local/index.yaml b/pkg/getter/testdata/repository/local/index.yaml deleted file mode 100644 index efcf30c..0000000 --- a/pkg/getter/testdata/repository/local/index.yaml +++ /dev/null @@ -1,3 +0,0 @@ -apiVersion: v1 -entries: {} -generated: 2017-04-28T12:34:38.900985501-06:00 diff --git a/pkg/getter/testdata/repository/repositories.yaml b/pkg/getter/testdata/repository/repositories.yaml deleted file mode 100644 index 1d884a0..0000000 --- a/pkg/getter/testdata/repository/repositories.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -generated: 2017-04-28T12:34:38.551693035-06:00 -repositories: -- caFile: "" - cache: repository/cache/stable-index.yaml - certFile: "" - keyFile: "" - name: stable - url: https://kubernetes-charts.storage.googleapis.com -- caFile: "" - cache: repository/cache/local-index.yaml - certFile: "" - keyFile: "" - name: local - url: http://127.0.0.1:8879/charts diff --git a/pkg/helmpath/home.go b/pkg/helmpath/home.go deleted file mode 100644 index bd43e88..0000000 --- a/pkg/helmpath/home.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package helmpath calculates filesystem paths to Helm's configuration, cache and data. -package helmpath - -// This helper builds paths to Helm's configuration, cache and data paths. -const lp = lazypath("helm") - -// ConfigPath returns the path where Helm stores configuration. -func ConfigPath(elem ...string) string { return lp.configPath(elem...) } - -// CachePath returns the path where Helm stores cached objects. -func CachePath(elem ...string) string { return lp.cachePath(elem...) } - -// DataPath returns the path where Helm stores data. -func DataPath(elem ...string) string { return lp.dataPath(elem...) } - -// CacheIndexFile returns the path to an index for the given named repository. -func CacheIndexFile(name string) string { - if name != "" { - name += "-" - } - return name + "index.yaml" -} - -// CacheChartsFile returns the path to a text file listing all the charts -// within the given named repository. -func CacheChartsFile(name string) string { - if name != "" { - name += "-" - } - return name + "charts.txt" -} diff --git a/pkg/helmpath/home_unix_test.go b/pkg/helmpath/home_unix_test.go deleted file mode 100644 index 6a72152..0000000 --- a/pkg/helmpath/home_unix_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows - -package helmpath - -import ( - "os" - "runtime" - "testing" - - "helm.sh/helm/v3/pkg/helmpath/xdg" -) - -func TestHelmHome(t *testing.T) { - os.Setenv(xdg.CacheHomeEnvVar, "/cache") - os.Setenv(xdg.ConfigHomeEnvVar, "/config") - os.Setenv(xdg.DataHomeEnvVar, "/data") - isEq := func(t *testing.T, got, expected string) { - t.Helper() - if expected != got { - t.Error(runtime.GOOS) - t.Errorf("Expected %q, got %q", expected, got) - } - } - - isEq(t, CachePath(), "/cache/helm") - isEq(t, ConfigPath(), "/config/helm") - isEq(t, DataPath(), "/data/helm") - - // test to see if lazy-loading environment variables at runtime works - os.Setenv(xdg.CacheHomeEnvVar, "/cache2") - - isEq(t, CachePath(), "/cache2/helm") -} diff --git a/pkg/helmpath/home_windows_test.go b/pkg/helmpath/home_windows_test.go deleted file mode 100644 index af74558..0000000 --- a/pkg/helmpath/home_windows_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build windows - -package helmpath - -import ( - "os" - "testing" - - "helm.sh/helm/v3/pkg/helmpath/xdg" -) - -func TestHelmHome(t *testing.T) { - os.Setenv(xdg.XDGCacheHomeEnvVar, "c:\\") - os.Setenv(xdg.XDGConfigHomeEnvVar, "d:\\") - os.Setenv(xdg.XDGDataHomeEnvVar, "e:\\") - isEq := func(t *testing.T, a, b string) { - if a != b { - t.Errorf("Expected %q, got %q", b, a) - } - } - - isEq(t, CachePath(), "c:\\helm") - isEq(t, ConfigPath(), "d:\\helm") - isEq(t, DataPath(), "e:\\helm") - - // test to see if lazy-loading environment variables at runtime works - os.Setenv(xdg.CacheHomeEnvVar, "f:\\") - - isEq(t, CachePath(), "f:\\helm") -} diff --git a/pkg/helmpath/lazypath.go b/pkg/helmpath/lazypath.go deleted file mode 100644 index 0b90686..0000000 --- a/pkg/helmpath/lazypath.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package helmpath - -import ( - "os" - "path/filepath" - - "helm.sh/helm/v3/pkg/helmpath/xdg" -) - -// lazypath is an lazy-loaded path buffer for the XDG base directory specification. -type lazypath string - -func (l lazypath) path(envVar string, defaultFn func() string, elem ...string) string { - base := os.Getenv(envVar) - if base == "" { - base = defaultFn() - } - return filepath.Join(base, string(l), filepath.Join(elem...)) -} - -// cachePath defines the base directory relative to which user specific non-essential data files -// should be stored. -func (l lazypath) cachePath(elem ...string) string { - return l.path(xdg.CacheHomeEnvVar, cacheHome, filepath.Join(elem...)) -} - -// configPath defines the base directory relative to which user specific configuration files should -// be stored. -func (l lazypath) configPath(elem ...string) string { - return l.path(xdg.ConfigHomeEnvVar, configHome, filepath.Join(elem...)) -} - -// dataPath defines the base directory relative to which user specific data files should be stored. -func (l lazypath) dataPath(elem ...string) string { - return l.path(xdg.DataHomeEnvVar, dataHome, filepath.Join(elem...)) -} diff --git a/pkg/helmpath/lazypath_darwin.go b/pkg/helmpath/lazypath_darwin.go deleted file mode 100644 index e112b83..0000000 --- a/pkg/helmpath/lazypath_darwin.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build darwin - -package helmpath - -import ( - "path/filepath" - - "k8s.io/client-go/util/homedir" -) - -func dataHome() string { - return filepath.Join(homedir.HomeDir(), "Library") -} - -func configHome() string { - return filepath.Join(homedir.HomeDir(), "Library", "Preferences") -} - -func cacheHome() string { - return filepath.Join(homedir.HomeDir(), "Library", "Caches") -} diff --git a/pkg/helmpath/lazypath_darwin_test.go b/pkg/helmpath/lazypath_darwin_test.go deleted file mode 100644 index 9381a44..0000000 --- a/pkg/helmpath/lazypath_darwin_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build darwin - -package helmpath - -import ( - "os" - "path/filepath" - "testing" - - "k8s.io/client-go/util/homedir" - - "helm.sh/helm/v3/pkg/helmpath/xdg" -) - -const ( - appName = "helm" - testFile = "test.txt" - lazy = lazypath(appName) -) - -func TestDataPath(t *testing.T) { - os.Unsetenv(xdg.DataHomeEnvVar) - - expected := filepath.Join(homedir.HomeDir(), "Library", appName, testFile) - - if lazy.dataPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.dataPath(testFile)) - } - - os.Setenv(xdg.DataHomeEnvVar, "/tmp") - - expected = filepath.Join("/tmp", appName, testFile) - - if lazy.dataPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.dataPath(testFile)) - } -} - -func TestConfigPath(t *testing.T) { - os.Unsetenv(xdg.ConfigHomeEnvVar) - - expected := filepath.Join(homedir.HomeDir(), "Library", "Preferences", appName, testFile) - - if lazy.configPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.configPath(testFile)) - } - - os.Setenv(xdg.ConfigHomeEnvVar, "/tmp") - - expected = filepath.Join("/tmp", appName, testFile) - - if lazy.configPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.configPath(testFile)) - } -} - -func TestCachePath(t *testing.T) { - os.Unsetenv(xdg.CacheHomeEnvVar) - - expected := filepath.Join(homedir.HomeDir(), "Library", "Caches", appName, testFile) - - if lazy.cachePath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.cachePath(testFile)) - } - - os.Setenv(xdg.CacheHomeEnvVar, "/tmp") - - expected = filepath.Join("/tmp", appName, testFile) - - if lazy.cachePath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.cachePath(testFile)) - } -} diff --git a/pkg/helmpath/lazypath_unix.go b/pkg/helmpath/lazypath_unix.go deleted file mode 100644 index b4eae9f..0000000 --- a/pkg/helmpath/lazypath_unix.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows,!darwin - -package helmpath - -import ( - "path/filepath" - - "k8s.io/client-go/util/homedir" -) - -// dataHome defines the base directory relative to which user specific data files should be stored. -// -// If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share is used. -func dataHome() string { - return filepath.Join(homedir.HomeDir(), ".local", "share") -} - -// configHome defines the base directory relative to which user specific configuration files should -// be stored. -// -// If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config is used. -func configHome() string { - return filepath.Join(homedir.HomeDir(), ".config") -} - -// cacheHome defines the base directory relative to which user specific non-essential data files -// should be stored. -// -// If $XDG_CACHE_HOME is either not set or empty, a default equal to $HOME/.cache is used. -func cacheHome() string { - return filepath.Join(homedir.HomeDir(), ".cache") -} diff --git a/pkg/helmpath/lazypath_unix_test.go b/pkg/helmpath/lazypath_unix_test.go deleted file mode 100644 index 96d66e7..0000000 --- a/pkg/helmpath/lazypath_unix_test.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows,!darwin - -package helmpath - -import ( - "os" - "path/filepath" - "testing" - - "k8s.io/client-go/util/homedir" - - "helm.sh/helm/v3/pkg/helmpath/xdg" -) - -const ( - appName = "helm" - testFile = "test.txt" - lazy = lazypath(appName) -) - -func TestDataPath(t *testing.T) { - os.Unsetenv(xdg.DataHomeEnvVar) - - expected := filepath.Join(homedir.HomeDir(), ".local", "share", appName, testFile) - - if lazy.dataPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.dataPath(testFile)) - } - - os.Setenv(xdg.DataHomeEnvVar, "/tmp") - - expected = filepath.Join("/tmp", appName, testFile) - - if lazy.dataPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.dataPath(testFile)) - } -} - -func TestConfigPath(t *testing.T) { - os.Unsetenv(xdg.ConfigHomeEnvVar) - - expected := filepath.Join(homedir.HomeDir(), ".config", appName, testFile) - - if lazy.configPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.configPath(testFile)) - } - - os.Setenv(xdg.ConfigHomeEnvVar, "/tmp") - - expected = filepath.Join("/tmp", appName, testFile) - - if lazy.configPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.configPath(testFile)) - } -} - -func TestCachePath(t *testing.T) { - os.Unsetenv(xdg.CacheHomeEnvVar) - - expected := filepath.Join(homedir.HomeDir(), ".cache", appName, testFile) - - if lazy.cachePath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.cachePath(testFile)) - } - - os.Setenv(xdg.CacheHomeEnvVar, "/tmp") - - expected = filepath.Join("/tmp", appName, testFile) - - if lazy.cachePath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.cachePath(testFile)) - } -} diff --git a/pkg/helmpath/lazypath_windows.go b/pkg/helmpath/lazypath_windows.go deleted file mode 100644 index 057a3af..0000000 --- a/pkg/helmpath/lazypath_windows.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build windows - -package helmpath - -import "os" - -func dataHome() string { return configHome() } - -func configHome() string { return os.Getenv("APPDATA") } - -func cacheHome() string { return os.Getenv("TEMP") } diff --git a/pkg/helmpath/lazypath_windows_test.go b/pkg/helmpath/lazypath_windows_test.go deleted file mode 100644 index d02a0a7..0000000 --- a/pkg/helmpath/lazypath_windows_test.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright The Helm Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build windows - -package helmpath - -import ( - "os" - "path/filepath" - "testing" - - "k8s.io/client-go/util/homedir" - - "helm.sh/helm/v3/pkg/helmpath/xdg" -) - -const ( - appName = "helm" - testFile = "test.txt" - lazy = lazypath(appName) -) - -func TestDataPath(t *testing.T) { - os.Unsetenv(DataHomeEnvVar) - os.Setenv("APPDATA", filepath.Join(homedir.HomeDir(), "foo")) - - expected := filepath.Join(homedir.HomeDir(), "foo", appName, testFile) - - if lazy.dataPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.dataPath(testFile)) - } - - os.Setenv(DataHomeEnvVar, filepath.Join(homedir.HomeDir(), "xdg")) - - expected = filepath.Join(homedir.HomeDir(), "xdg", appName, testFile) - - if lazy.dataPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.dataPath(testFile)) - } -} - -func TestConfigPath(t *testing.T) { - os.Unsetenv(xdg.ConfigHomeEnvVar) - os.Setenv("APPDATA", filepath.Join(homedir.HomeDir(), "foo")) - - expected := filepath.Join(homedir.HomeDir(), "foo", appName, testFile) - - if lazy.configPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.configPath(testFile)) - } - - os.Setenv(xdg.ConfigHomeEnvVar, filepath.Join(homedir.HomeDir(), "xdg")) - - expected = filepath.Join(homedir.HomeDir(), "xdg", appName, testFile) - - if lazy.configPath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.configPath(testFile)) - } -} - -func TestCachePath(t *testing.T) { - os.Unsetenv(CacheHomeEnvVar) - os.Setenv("APPDATA", filepath.Join(homedir.HomeDir(), "foo")) - - expected := filepath.Join(homedir.HomeDir(), "foo", appName, testFile) - - if lazy.cachePath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.cachePath(testFile)) - } - - os.Setenv(CacheHomeEnvVar, filepath.Join(homedir.HomeDir(), "xdg")) - - expected = filepath.Join(homedir.HomeDir(), "xdg", appName, testFile) - - if lazy.cachePath(testFile) != expected { - t.Errorf("expected '%s', got '%s'", expected, lazy.cachePath(testFile)) - } -} diff --git a/pkg/helmpath/xdg/xdg.go b/pkg/helmpath/xdg/xdg.go deleted file mode 100644 index eaa3e68..0000000 --- a/pkg/helmpath/xdg/xdg.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package xdg holds constants pertaining to XDG Base Directory Specification. -// -// The XDG Base Directory Specification https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html -// specifies the environment variables that define user-specific base directories for various categories of files. -package xdg - -const ( - // CacheHomeEnvVar is the environment variable used by the - // XDG base directory specification for the cache directory. - CacheHomeEnvVar = "XDG_CACHE_HOME" - - // ConfigHomeEnvVar is the environment variable used by the - // XDG base directory specification for the config directory. - ConfigHomeEnvVar = "XDG_CONFIG_HOME" - - // DataHomeEnvVar is the environment variable used by the - // XDG base directory specification for the data directory. - DataHomeEnvVar = "XDG_DATA_HOME" -) diff --git a/pkg/kube/client.go b/pkg/kube/client.go deleted file mode 100644 index 3853e90..0000000 --- a/pkg/kube/client.go +++ /dev/null @@ -1,576 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube // import "helm.sh/helm/v3/pkg/kube" - -import ( - "context" - "encoding/json" - "fmt" - "io" - "strings" - "sync" - "time" - - jsonpatch "github.com/evanphx/json-patch" - "github.com/pkg/errors" - batch "k8s.io/api/batch/v1" - v1 "k8s.io/api/core/v1" - apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/strategicpatch" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/cli-runtime/pkg/genericclioptions" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/kubernetes/scheme" - cachetools "k8s.io/client-go/tools/cache" - watchtools "k8s.io/client-go/tools/watch" - cmdutil "k8s.io/kubectl/pkg/cmd/util" -) - -// ErrNoObjectsVisited indicates that during a visit operation, no matching objects were found. -var ErrNoObjectsVisited = errors.New("no objects visited") - -// Client represents a client capable of communicating with the Kubernetes API. -type Client struct { - Factory Factory - Log func(string, ...interface{}) -} - -var addToScheme sync.Once - -// New creates a new Client. -func New(getter genericclioptions.RESTClientGetter) *Client { - if getter == nil { - getter = genericclioptions.NewConfigFlags(true) - } - // Add CRDs to the scheme. They are missing by default. - addToScheme.Do(func() { - if err := apiextv1.AddToScheme(scheme.Scheme); err != nil { - // This should never happen. - panic(err) - } - if err := apiextv1beta1.AddToScheme(scheme.Scheme); err != nil { - panic(err) - } - }) - return &Client{ - Factory: cmdutil.NewFactory(getter), - Log: nopLogger, - } -} - -var nopLogger = func(_ string, _ ...interface{}) {} - -// IsReachable tests connectivity to the cluster -func (c *Client) IsReachable() error { - client, _ := c.Factory.KubernetesClientSet() - _, err := client.ServerVersion() - if err != nil { - return errors.New("Kubernetes cluster unreachable") - } - return nil -} - -// Create creates Kubernetes resources specified in the resource list. -func (c *Client) Create(resources ResourceList) (*Result, error) { - c.Log("creating %d resource(s)", len(resources)) - if err := perform(resources, createResource); err != nil { - return nil, err - } - return &Result{Created: resources}, nil -} - -// Wait up to the given timeout for the specified resources to be ready -func (c *Client) Wait(resources ResourceList, timeout time.Duration) error { - cs, err := c.Factory.KubernetesClientSet() - if err != nil { - return err - } - w := waiter{ - c: cs, - log: c.Log, - timeout: timeout, - } - return w.waitForResources(resources) -} - -func (c *Client) namespace() string { - if ns, _, err := c.Factory.ToRawKubeConfigLoader().Namespace(); err == nil { - return ns - } - return v1.NamespaceDefault -} - -// newBuilder returns a new resource builder for structured api objects. -func (c *Client) newBuilder() *resource.Builder { - return c.Factory.NewBuilder(). - ContinueOnError(). - NamespaceParam(c.namespace()). - DefaultNamespace(). - Flatten() -} - -// Build validates for Kubernetes objects and returns unstructured infos. -func (c *Client) Build(reader io.Reader, validate bool) (ResourceList, error) { - schema, err := c.Factory.Validator(validate) - if err != nil { - return nil, err - } - result, err := c.newBuilder(). - Unstructured(). - Schema(schema). - Stream(reader, ""). - Do().Infos() - return result, scrubValidationError(err) -} - -// Update takes the current list of objects and target list of objects and -// creates resources that don't already exists, updates resources that have been -// modified in the target configuration, and deletes resources from the current -// configuration that are not present in the target configuration. If an error -// occurs, a Result will still be returned with the error, containing all -// resource updates, creations, and deletions that were attempted. These can be -// used for cleanup or other logging purposes. -func (c *Client) Update(original, target ResourceList, force bool) (*Result, error) { - updateErrors := []string{} - res := &Result{} - - c.Log("checking %d resources for changes", len(target)) - err := target.Visit(func(info *resource.Info, err error) error { - if err != nil { - return err - } - - helper := resource.NewHelper(info.Client, info.Mapping) - if _, err := helper.Get(info.Namespace, info.Name, info.Export); err != nil { - if !apierrors.IsNotFound(err) { - return errors.Wrap(err, "could not get information about the resource") - } - - // Append the created resource to the results, even if something fails - res.Created = append(res.Created, info) - - // Since the resource does not exist, create it. - if err := createResource(info); err != nil { - return errors.Wrap(err, "failed to create resource") - } - - kind := info.Mapping.GroupVersionKind.Kind - c.Log("Created a new %s called %q in %s\n", kind, info.Name, info.Namespace) - return nil - } - - originalInfo := original.Get(info) - if originalInfo == nil { - kind := info.Mapping.GroupVersionKind.Kind - return errors.Errorf("no %s with the name %q found", kind, info.Name) - } - - if err := updateResource(c, info, originalInfo.Object, force); err != nil { - c.Log("error updating the resource %q:\n\t %v", info.Name, err) - updateErrors = append(updateErrors, err.Error()) - } - // Because we check for errors later, append the info regardless - res.Updated = append(res.Updated, info) - - return nil - }) - - switch { - case err != nil: - return res, err - case len(updateErrors) != 0: - return res, errors.Errorf(strings.Join(updateErrors, " && ")) - } - - for _, info := range original.Difference(target) { - if info.Mapping.GroupVersionKind.Kind == "CustomResourceDefinition" { - c.Log("Skipping the deletion of CustomResourceDefinition %q", info.Name) - continue - } - - c.Log("Deleting %q in %s...", info.Name, info.Namespace) - res.Deleted = append(res.Deleted, info) - if err := deleteResource(info); err != nil { - if apierrors.IsNotFound(err) { - c.Log("Attempted to delete %q, but the resource was missing", info.Name) - } else { - c.Log("Failed to delete %q, err: %s", info.Name, err) - return res, errors.Wrapf(err, "Failed to delete %q", info.Name) - } - } - } - return res, nil -} - -// Delete deletes Kubernetes resources specified in the resources list. It will -// attempt to delete all resources even if one or more fail and collect any -// errors. All successfully deleted items will be returned in the `Deleted` -// ResourceList that is part of the result. -func (c *Client) Delete(resources ResourceList) (*Result, []error) { - var errs []error - res := &Result{} - err := perform(resources, func(info *resource.Info) error { - if info.Mapping.GroupVersionKind.Kind == "CustomResourceDefinition" { - c.Log("Skipping the deletion of CustomResourceDefinition %q", info.Name) - return nil - } - - c.Log("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind) - if err := c.skipIfNotFound(deleteResource(info)); err != nil { - // Collect the error and continue on - errs = append(errs, err) - } else { - res.Deleted = append(res.Deleted, info) - } - return nil - }) - if err != nil { - // Rewrite the message from "no objects visited" if that is what we got - // back - if err == ErrNoObjectsVisited { - err = errors.New("object not found, skipping delete") - } - errs = append(errs, err) - } - if errs != nil { - return nil, errs - } - return res, nil -} - -func (c *Client) skipIfNotFound(err error) error { - if apierrors.IsNotFound(err) { - c.Log("%v", err) - return nil - } - return err -} - -func (c *Client) watchTimeout(t time.Duration) func(*resource.Info) error { - return func(info *resource.Info) error { - return c.watchUntilReady(t, info) - } -} - -// WatchUntilReady watches the resources given and waits until it is ready. -// -// This function is mainly for hook implementations. It watches for a resource to -// hit a particular milestone. The milestone depends on the Kind. -// -// For most kinds, it checks to see if the resource is marked as Added or Modified -// by the Kubernetes event stream. For some kinds, it does more: -// -// - Jobs: A job is marked "Ready" when it has successfully completed. This is -// ascertained by watching the Status fields in a job's output. -// - Pods: A pod is marked "Ready" when it has successfully completed. This is -// ascertained by watching the status.phase field in a pod's output. -// -// Handling for other kinds will be added as necessary. -func (c *Client) WatchUntilReady(resources ResourceList, timeout time.Duration) error { - // For jobs, there's also the option to do poll c.Jobs(namespace).Get(): - // https://github.com/adamreese/kubernetes/blob/master/test/e2e/job.go#L291-L300 - return perform(resources, c.watchTimeout(timeout)) -} - -func perform(infos ResourceList, fn func(*resource.Info) error) error { - if len(infos) == 0 { - return ErrNoObjectsVisited - } - - errs := make(chan error) - go batchPerform(infos, fn, errs) - - for range infos { - err := <-errs - if err != nil { - return err - } - } - return nil -} - -func batchPerform(infos ResourceList, fn func(*resource.Info) error, errs chan<- error) { - var kind string - var wg sync.WaitGroup - for _, info := range infos { - currentKind := info.Object.GetObjectKind().GroupVersionKind().Kind - if kind != currentKind { - wg.Wait() - kind = currentKind - } - wg.Add(1) - go func(i *resource.Info) { - errs <- fn(i) - wg.Done() - }(info) - } -} - -func createResource(info *resource.Info) error { - obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil) - if err != nil { - return err - } - return info.Refresh(obj, true) -} - -func deleteResource(info *resource.Info) error { - policy := metav1.DeletePropagationBackground - opts := &metav1.DeleteOptions{PropagationPolicy: &policy} - _, err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, opts) - return err -} - -func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.PatchType, error) { - oldData, err := json.Marshal(current) - if err != nil { - return nil, types.StrategicMergePatchType, errors.Wrap(err, "serializing current configuration") - } - newData, err := json.Marshal(target.Object) - if err != nil { - return nil, types.StrategicMergePatchType, errors.Wrap(err, "serializing target configuration") - } - - // Fetch the current object for the three way merge - helper := resource.NewHelper(target.Client, target.Mapping) - currentObj, err := helper.Get(target.Namespace, target.Name, target.Export) - if err != nil && !apierrors.IsNotFound(err) { - return nil, types.StrategicMergePatchType, errors.Wrapf(err, "unable to get data for current object %s/%s", target.Namespace, target.Name) - } - - // Even if currentObj is nil (because it was not found), it will marshal just fine - currentData, err := json.Marshal(currentObj) - if err != nil { - return nil, types.StrategicMergePatchType, errors.Wrap(err, "serializing live configuration") - } - - // Get a versioned object - versionedObject := AsVersioned(target) - - // Unstructured objects, such as CRDs, may not have an not registered error - // returned from ConvertToVersion. Anything that's unstructured should - // use the jsonpatch.CreateMergePatch. Strategic Merge Patch is not supported - // on objects like CRDs. - _, isUnstructured := versionedObject.(runtime.Unstructured) - - // On newer K8s versions, CRDs aren't unstructured but has this dedicated type - _, isCRD := versionedObject.(*apiextv1beta1.CustomResourceDefinition) - - if isUnstructured || isCRD { - // fall back to generic JSON merge patch - patch, err := jsonpatch.CreateMergePatch(oldData, newData) - return patch, types.MergePatchType, err - } - - patchMeta, err := strategicpatch.NewPatchMetaFromStruct(versionedObject) - if err != nil { - return nil, types.StrategicMergePatchType, errors.Wrap(err, "unable to create patch metadata from object") - } - - patch, err := strategicpatch.CreateThreeWayMergePatch(oldData, newData, currentData, patchMeta, true) - return patch, types.StrategicMergePatchType, err -} - -func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, force bool) error { - var ( - obj runtime.Object - helper = resource.NewHelper(target.Client, target.Mapping) - kind = target.Mapping.GroupVersionKind.Kind - ) - - patch, patchType, err := createPatch(target, currentObj) - if err != nil { - return errors.Wrap(err, "failed to create patch") - } - - if patch == nil || string(patch) == "{}" { - c.Log("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name) - // This needs to happen to make sure that tiller has the latest info from the API - // Otherwise there will be no labels and other functions that use labels will panic - if err := target.Get(); err != nil { - return errors.Wrap(err, "failed to refresh resource information") - } - return nil - } - - // if --force is applied, attempt to replace the existing resource with the new object. - if force { - obj, err = helper.Replace(target.Namespace, target.Name, true, target.Object) - if err != nil { - return errors.Wrap(err, "failed to replace object") - } - c.Log("Replaced %q with kind %s for kind %s\n", target.Name, currentObj.GetObjectKind().GroupVersionKind().Kind, kind) - } else { - // send patch to server - obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil) - if err != nil { - return errors.Wrapf(err, "cannot patch %q with kind %s", target.Name, kind) - } - } - - target.Refresh(obj, true) - return nil -} - -func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) error { - kind := info.Mapping.GroupVersionKind.Kind - switch kind { - case "Job", "Pod": - default: - return nil - } - - c.Log("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout) - - // Use a selector on the name of the resource. This should be unique for the - // given version and kind - selector, err := fields.ParseSelector(fmt.Sprintf("metadata.name=%s", info.Name)) - if err != nil { - return err - } - lw := cachetools.NewListWatchFromClient(info.Client, info.Mapping.Resource.Resource, info.Namespace, selector) - - // What we watch for depends on the Kind. - // - For a Job, we watch for completion. - // - For all else, we watch until Ready. - // In the future, we might want to add some special logic for types - // like Ingress, Volume, etc. - - ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout) - defer cancel() - _, err = watchtools.ListWatchUntil(ctx, lw, func(e watch.Event) (bool, error) { - // Make sure the incoming object is versioned as we use unstructured - // objects when we build manifests - obj := convertWithMapper(e.Object, info.Mapping) - switch e.Type { - case watch.Added, watch.Modified: - // For things like a secret or a config map, this is the best indicator - // we get. We care mostly about jobs, where what we want to see is - // the status go into a good state. For other types, like ReplicaSet - // we don't really do anything to support these as hooks. - c.Log("Add/Modify event for %s: %v", info.Name, e.Type) - switch kind { - case "Job": - return c.waitForJob(obj, info.Name) - case "Pod": - return c.waitForPodSuccess(obj, info.Name) - } - return true, nil - case watch.Deleted: - c.Log("Deleted event for %s", info.Name) - return true, nil - case watch.Error: - // Handle error and return with an error. - c.Log("Error event for %s", info.Name) - return true, errors.Errorf("failed to deploy %s", info.Name) - default: - return false, nil - } - }) - return err -} - -// waitForJob is a helper that waits for a job to complete. -// -// This operates on an event returned from a watcher. -func (c *Client) waitForJob(obj runtime.Object, name string) (bool, error) { - o, ok := obj.(*batch.Job) - if !ok { - return true, errors.Errorf("expected %s to be a *batch.Job, got %T", name, obj) - } - - for _, c := range o.Status.Conditions { - if c.Type == batch.JobComplete && c.Status == "True" { - return true, nil - } else if c.Type == batch.JobFailed && c.Status == "True" { - return true, errors.Errorf("job failed: %s", c.Reason) - } - } - - c.Log("%s: Jobs active: %d, jobs failed: %d, jobs succeeded: %d", name, o.Status.Active, o.Status.Failed, o.Status.Succeeded) - return false, nil -} - -// waitForPodSuccess is a helper that waits for a pod to complete. -// -// This operates on an event returned from a watcher. -func (c *Client) waitForPodSuccess(obj runtime.Object, name string) (bool, error) { - o, ok := obj.(*v1.Pod) - if !ok { - return true, errors.Errorf("expected %s to be a *v1.Pod, got %T", name, obj) - } - - switch o.Status.Phase { - case v1.PodSucceeded: - fmt.Printf("Pod %s succeeded\n", o.Name) - return true, nil - case v1.PodFailed: - return true, errors.Errorf("pod %s failed", o.Name) - case v1.PodPending: - fmt.Printf("Pod %s pending\n", o.Name) - case v1.PodRunning: - fmt.Printf("Pod %s running\n", o.Name) - } - - return false, nil -} - -// scrubValidationError removes kubectl info from the message. -func scrubValidationError(err error) error { - if err == nil { - return nil - } - const stopValidateMessage = "if you choose to ignore these errors, turn validation off with --validate=false" - - if strings.Contains(err.Error(), stopValidateMessage) { - return errors.New(strings.ReplaceAll(err.Error(), "; "+stopValidateMessage, "")) - } - return err -} - -// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase -// and returns said phase (PodSucceeded or PodFailed qualify). -func (c *Client) WaitAndGetCompletedPodPhase(name string, timeout time.Duration) (v1.PodPhase, error) { - client, _ := c.Factory.KubernetesClientSet() - to := int64(timeout) - watcher, err := client.CoreV1().Pods(c.namespace()).Watch(metav1.ListOptions{ - FieldSelector: fmt.Sprintf("metadata.name=%s", name), - TimeoutSeconds: &to, - }) - - for event := range watcher.ResultChan() { - p, ok := event.Object.(*v1.Pod) - if !ok { - return v1.PodUnknown, fmt.Errorf("%s not a pod", name) - } - switch p.Status.Phase { - case v1.PodFailed: - return v1.PodFailed, nil - case v1.PodSucceeded: - return v1.PodSucceeded, nil - } - } - - return v1.PodUnknown, err -} diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go deleted file mode 100644 index 9e7581d..0000000 --- a/pkg/kube/client_test.go +++ /dev/null @@ -1,505 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube - -import ( - "bytes" - "io" - "io/ioutil" - "net/http" - "strings" - "testing" - - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest/fake" - cmdtesting "k8s.io/kubectl/pkg/cmd/testing" -) - -var unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer -var codec = scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...) - -func objBody(obj runtime.Object) io.ReadCloser { - return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) -} - -func newPod(name string) v1.Pod { - return newPodWithStatus(name, v1.PodStatus{}, "") -} - -func newPodWithStatus(name string, status v1.PodStatus, namespace string) v1.Pod { - ns := v1.NamespaceDefault - if namespace != "" { - ns = namespace - } - return v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - SelfLink: "/api/v1/namespaces/default/pods/" + name, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{{ - Name: "app:v4", - Image: "abc/app:v4", - Ports: []v1.ContainerPort{{Name: "http", ContainerPort: 80}}, - }}, - }, - Status: status, - } -} - -func newPodList(names ...string) v1.PodList { - var list v1.PodList - for _, name := range names { - list.Items = append(list.Items, newPod(name)) - } - return list -} - -func notFoundBody() *metav1.Status { - return &metav1.Status{ - Code: http.StatusNotFound, - Status: metav1.StatusFailure, - Reason: metav1.StatusReasonNotFound, - Message: " \"\" not found", - Details: &metav1.StatusDetails{}, - } -} - -func newResponse(code int, obj runtime.Object) (*http.Response, error) { - header := http.Header{} - header.Set("Content-Type", runtime.ContentTypeJSON) - body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) - return &http.Response{StatusCode: code, Header: header, Body: body}, nil -} - -func newTestClient() *Client { - return &Client{ - Factory: cmdtesting.NewTestFactory().WithNamespace("default"), - Log: nopLogger, - } -} - -func TestUpdate(t *testing.T) { - listA := newPodList("starfish", "otter", "squid") - listB := newPodList("starfish", "otter", "dolphin") - listC := newPodList("starfish", "otter", "dolphin") - listB.Items[0].Spec.Containers[0].Ports = []v1.ContainerPort{{Name: "https", ContainerPort: 443}} - listC.Items[0].Spec.Containers[0].Ports = []v1.ContainerPort{{Name: "https", ContainerPort: 443}} - - var actions []string - - c := newTestClient() - c.Factory.(*cmdtesting.TestFactory).UnstructuredClient = &fake.RESTClient{ - NegotiatedSerializer: unstructuredSerializer, - Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { - p, m := req.URL.Path, req.Method - actions = append(actions, p+":"+m) - t.Logf("got request %s %s", p, m) - switch { - case p == "/namespaces/default/pods/starfish" && m == "GET": - return newResponse(200, &listA.Items[0]) - case p == "/namespaces/default/pods/otter" && m == "GET": - return newResponse(200, &listA.Items[1]) - case p == "/namespaces/default/pods/otter" && m == "PATCH": - data, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Fatalf("could not dump request: %s", err) - } - req.Body.Close() - expected := `{}` - if string(data) != expected { - t.Errorf("expected patch\n%s\ngot\n%s", expected, string(data)) - } - return newResponse(200, &listB.Items[0]) - case p == "/namespaces/default/pods/dolphin" && m == "GET": - return newResponse(404, notFoundBody()) - case p == "/namespaces/default/pods/starfish" && m == "PATCH": - data, err := ioutil.ReadAll(req.Body) - if err != nil { - t.Fatalf("could not dump request: %s", err) - } - req.Body.Close() - expected := `{"spec":{"$setElementOrder/containers":[{"name":"app:v4"}],"containers":[{"$setElementOrder/ports":[{"containerPort":443}],"name":"app:v4","ports":[{"containerPort":443,"name":"https"},{"$patch":"delete","containerPort":80}]}]}}` - if string(data) != expected { - t.Errorf("expected patch\n%s\ngot\n%s", expected, string(data)) - } - return newResponse(200, &listB.Items[0]) - case p == "/namespaces/default/pods" && m == "POST": - return newResponse(200, &listB.Items[1]) - case p == "/namespaces/default/pods/squid" && m == "DELETE": - return newResponse(200, &listB.Items[1]) - default: - t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path) - return nil, nil - } - }), - } - first, err := c.Build(objBody(&listA), false) - if err != nil { - t.Fatal(err) - } - second, err := c.Build(objBody(&listB), false) - if err != nil { - t.Fatal(err) - } - - if _, err := c.Update(first, second, false); err != nil { - t.Fatal(err) - } - // TODO: Find a way to test methods that use Client Set - // Test with a wait - // if err := c.Update("test", objBody(codec, &listB), objBody(codec, &listC), false, 300, true); err != nil { - // t.Fatal(err) - // } - // Test with a wait should fail - // TODO: A way to make this not based off of an extremely short timeout? - // if err := c.Update("test", objBody(codec, &listC), objBody(codec, &listA), false, 2, true); err != nil { - // t.Fatal(err) - // } - expectedActions := []string{ - "/namespaces/default/pods/starfish:GET", - "/namespaces/default/pods/starfish:GET", - "/namespaces/default/pods/starfish:PATCH", - "/namespaces/default/pods/otter:GET", - "/namespaces/default/pods/otter:GET", - "/namespaces/default/pods/otter:GET", - "/namespaces/default/pods/dolphin:GET", - "/namespaces/default/pods:POST", - "/namespaces/default/pods/squid:DELETE", - } - if len(expectedActions) != len(actions) { - t.Errorf("unexpected number of requests, expected %d, got %d", len(expectedActions), len(actions)) - return - } - for k, v := range expectedActions { - if actions[k] != v { - t.Errorf("expected %s request got %s", v, actions[k]) - } - } -} - -func TestBuild(t *testing.T) { - tests := []struct { - name string - namespace string - reader io.Reader - count int - err bool - }{ - { - name: "Valid input", - namespace: "test", - reader: strings.NewReader(guestbookManifest), - count: 6, - }, { - name: "Valid input, deploying resources into different namespaces", - namespace: "test", - reader: strings.NewReader(namespacedGuestbookManifest), - count: 1, - }, - } - - c := newTestClient() - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Test for an invalid manifest - infos, err := c.Build(tt.reader, false) - if err != nil && !tt.err { - t.Errorf("Got error message when no error should have occurred: %v", err) - } else if err != nil && strings.Contains(err.Error(), "--validate=false") { - t.Error("error message was not scrubbed") - } - - if len(infos) != tt.count { - t.Errorf("expected %d result objects, got %d", tt.count, len(infos)) - } - }) - } -} - -func TestPerform(t *testing.T) { - tests := []struct { - name string - reader io.Reader - count int - err bool - errMessage string - }{ - { - name: "Valid input", - reader: strings.NewReader(guestbookManifest), - count: 6, - }, { - name: "Empty manifests", - reader: strings.NewReader(""), - err: true, - errMessage: "no objects visited", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - results := []*resource.Info{} - - fn := func(info *resource.Info) error { - results = append(results, info) - return nil - } - - c := newTestClient() - infos, err := c.Build(tt.reader, false) - if err != nil && err.Error() != tt.errMessage { - t.Errorf("Error while building manifests: %v", err) - } - - err = perform(infos, fn) - if (err != nil) != tt.err { - t.Errorf("expected error: %v, got %v", tt.err, err) - } - if err != nil && err.Error() != tt.errMessage { - t.Errorf("expected error message: %v, got %v", tt.errMessage, err) - } - - if len(results) != tt.count { - t.Errorf("expected %d result objects, got %d", tt.count, len(results)) - } - }) - } -} - -func TestReal(t *testing.T) { - t.Skip("This is a live test, comment this line to run") - c := New(nil) - resources, err := c.Build(strings.NewReader(guestbookManifest), false) - if err != nil { - t.Fatal(err) - } - if _, err := c.Create(resources); err != nil { - t.Fatal(err) - } - - testSvcEndpointManifest := testServiceManifest + "\n---\n" + testEndpointManifest - c = New(nil) - resources, err = c.Build(strings.NewReader(testSvcEndpointManifest), false) - if err != nil { - t.Fatal(err) - } - if _, err := c.Create(resources); err != nil { - t.Fatal(err) - } - - resources, err = c.Build(strings.NewReader(testEndpointManifest), false) - if err != nil { - t.Fatal(err) - } - - if _, errs := c.Delete(resources); errs != nil { - t.Fatal(errs) - } - - resources, err = c.Build(strings.NewReader(testSvcEndpointManifest), false) - if err != nil { - t.Fatal(err) - } - // ensures that delete does not fail if a resource is not found - if _, errs := c.Delete(resources); errs != nil { - t.Fatal(errs) - } -} - -const testServiceManifest = ` -kind: Service -apiVersion: v1 -metadata: - name: my-service -spec: - selector: - app: myapp - ports: - - port: 80 - protocol: TCP - targetPort: 9376 -` - -const testEndpointManifest = ` -kind: Endpoints -apiVersion: v1 -metadata: - name: my-service -subsets: - - addresses: - - ip: "1.2.3.4" - ports: - - port: 9376 -` - -const guestbookManifest = ` -apiVersion: v1 -kind: Service -metadata: - name: redis-master - labels: - app: redis - tier: backend - role: master -spec: - ports: - - port: 6379 - targetPort: 6379 - selector: - app: redis - tier: backend - role: master ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: redis-master -spec: - replicas: 1 - template: - metadata: - labels: - app: redis - role: master - tier: backend - spec: - containers: - - name: master - image: k8s.gcr.io/redis:e2e # or just image: redis - resources: - requests: - cpu: 100m - memory: 100Mi - ports: - - containerPort: 6379 ---- -apiVersion: v1 -kind: Service -metadata: - name: redis-slave - labels: - app: redis - tier: backend - role: slave -spec: - ports: - # the port that this service should serve on - - port: 6379 - selector: - app: redis - tier: backend - role: slave ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: redis-slave -spec: - replicas: 2 - template: - metadata: - labels: - app: redis - role: slave - tier: backend - spec: - containers: - - name: slave - image: gcr.io/google_samples/gb-redisslave:v1 - resources: - requests: - cpu: 100m - memory: 100Mi - env: - - name: GET_HOSTS_FROM - value: dns - ports: - - containerPort: 6379 ---- -apiVersion: v1 -kind: Service -metadata: - name: frontend - labels: - app: guestbook - tier: frontend -spec: - ports: - - port: 80 - selector: - app: guestbook - tier: frontend ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: frontend -spec: - replicas: 3 - template: - metadata: - labels: - app: guestbook - tier: frontend - spec: - containers: - - name: php-redis - image: gcr.io/google-samples/gb-frontend:v4 - resources: - requests: - cpu: 100m - memory: 100Mi - env: - - name: GET_HOSTS_FROM - value: dns - ports: - - containerPort: 80 -` - -const namespacedGuestbookManifest = ` -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: frontend - namespace: guestbook -spec: - replicas: 3 - template: - metadata: - labels: - app: guestbook - tier: frontend - spec: - containers: - - name: php-redis - image: gcr.io/google-samples/gb-frontend:v4 - resources: - requests: - cpu: 100m - memory: 100Mi - env: - - name: GET_HOSTS_FROM - value: dns - ports: - - containerPort: 80 -` diff --git a/pkg/kube/config.go b/pkg/kube/config.go deleted file mode 100644 index 624c4a1..0000000 --- a/pkg/kube/config.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube // import "helm.sh/helm/v3/pkg/kube" - -import "k8s.io/cli-runtime/pkg/genericclioptions" - -// GetConfig returns a Kubernetes client config. -func GetConfig(kubeconfig, context, namespace string) *genericclioptions.ConfigFlags { - cf := genericclioptions.NewConfigFlags(true) - cf.Namespace = &namespace - cf.Context = &context - cf.KubeConfig = &kubeconfig - return cf -} diff --git a/pkg/kube/converter.go b/pkg/kube/converter.go deleted file mode 100644 index a5a6ae1..0000000 --- a/pkg/kube/converter.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube // import "helm.sh/helm/v3/pkg/kube" - -import ( - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/kubernetes/scheme" -) - -// AsVersioned converts the given info into a runtime.Object with the correct -// group and version set -func AsVersioned(info *resource.Info) runtime.Object { - return convertWithMapper(info.Object, info.Mapping) -} - -// convertWithMapper converts the given object with the optional provided -// RESTMapping. If no mapping is provided, the default schema versioner is used -func convertWithMapper(obj runtime.Object, mapping *meta.RESTMapping) runtime.Object { - var gv = runtime.GroupVersioner(schema.GroupVersions(scheme.Scheme.PrioritizedVersionsAllGroups())) - if mapping != nil { - gv = mapping.GroupVersionKind.GroupVersion() - } - if obj, err := runtime.ObjectConvertor(scheme.Scheme).ConvertToVersion(obj, gv); err == nil { - return obj - } - return obj -} diff --git a/pkg/kube/factory.go b/pkg/kube/factory.go deleted file mode 100644 index f47f9d9..0000000 --- a/pkg/kube/factory.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube // import "helm.sh/helm/v3/pkg/kube" - -import ( - "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/kubectl/pkg/validation" -) - -// Factory provides abstractions that allow the Kubectl command to be extended across multiple types -// of resources and different API sets. -type Factory interface { - // ToRawKubeConfigLoader return kubeconfig loader as-is - ToRawKubeConfigLoader() clientcmd.ClientConfig - // KubernetesClientSet gives you back an external clientset - KubernetesClientSet() (*kubernetes.Clientset, error) - // NewBuilder returns an object that assists in loading objects from both disk and the server - // and which implements the common patterns for CLI interactions with generic resources. - NewBuilder() *resource.Builder - // Returns a schema that can validate objects stored on disk. - Validator(validate bool) (validation.Schema, error) -} diff --git a/pkg/kube/fake/fake.go b/pkg/kube/fake/fake.go deleted file mode 100644 index b3f7a39..0000000 --- a/pkg/kube/fake/fake.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package fake implements various fake KubeClients for use in testing -package fake - -import ( - "io" - "time" - - v1 "k8s.io/api/core/v1" - "k8s.io/cli-runtime/pkg/resource" - - "helm.sh/helm/v3/pkg/kube" -) - -// FailingKubeClient implements KubeClient for testing purposes. It also has -// additional errors you can set to fail different functions, otherwise it -// delegates all its calls to `PrintingKubeClient` -type FailingKubeClient struct { - PrintingKubeClient - CreateError error - WaitError error - DeleteError error - WatchUntilReadyError error - UpdateError error - BuildError error - BuildUnstructuredError error - WaitAndGetCompletedPodPhaseError error -} - -// Create returns the configured error if set or prints -func (f *FailingKubeClient) Create(resources kube.ResourceList) (*kube.Result, error) { - if f.CreateError != nil { - return nil, f.CreateError - } - return f.PrintingKubeClient.Create(resources) -} - -// Wait returns the configured error if set or prints -func (f *FailingKubeClient) Wait(resources kube.ResourceList, d time.Duration) error { - if f.WaitError != nil { - return f.WaitError - } - return f.PrintingKubeClient.Wait(resources, d) -} - -// Delete returns the configured error if set or prints -func (f *FailingKubeClient) Delete(resources kube.ResourceList) (*kube.Result, []error) { - if f.DeleteError != nil { - return nil, []error{f.DeleteError} - } - return f.PrintingKubeClient.Delete(resources) -} - -// WatchUntilReady returns the configured error if set or prints -func (f *FailingKubeClient) WatchUntilReady(resources kube.ResourceList, d time.Duration) error { - if f.WatchUntilReadyError != nil { - return f.WatchUntilReadyError - } - return f.PrintingKubeClient.WatchUntilReady(resources, d) -} - -// Update returns the configured error if set or prints -func (f *FailingKubeClient) Update(r, modified kube.ResourceList, ignoreMe bool) (*kube.Result, error) { - if f.UpdateError != nil { - return &kube.Result{}, f.UpdateError - } - return f.PrintingKubeClient.Update(r, modified, ignoreMe) -} - -// Build returns the configured error if set or prints -func (f *FailingKubeClient) Build(r io.Reader, _ bool) (kube.ResourceList, error) { - if f.BuildError != nil { - return []*resource.Info{}, f.BuildError - } - return f.PrintingKubeClient.Build(r, false) -} - -// WaitAndGetCompletedPodPhase returns the configured error if set or prints -func (f *FailingKubeClient) WaitAndGetCompletedPodPhase(s string, d time.Duration) (v1.PodPhase, error) { - if f.WaitAndGetCompletedPodPhaseError != nil { - return v1.PodSucceeded, f.WaitAndGetCompletedPodPhaseError - } - return f.PrintingKubeClient.WaitAndGetCompletedPodPhase(s, d) -} diff --git a/pkg/kube/fake/printer.go b/pkg/kube/fake/printer.go deleted file mode 100644 index 58b389a..0000000 --- a/pkg/kube/fake/printer.go +++ /dev/null @@ -1,100 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package fake - -import ( - "io" - "strings" - "time" - - v1 "k8s.io/api/core/v1" - "k8s.io/cli-runtime/pkg/resource" - - "helm.sh/helm/v3/pkg/kube" -) - -// PrintingKubeClient implements KubeClient, but simply prints the reader to -// the given output. -type PrintingKubeClient struct { - Out io.Writer -} - -// IsReachable checks if the cluster is reachable -func (p *PrintingKubeClient) IsReachable() error { - return nil -} - -// Create prints the values of what would be created with a real KubeClient. -func (p *PrintingKubeClient) Create(resources kube.ResourceList) (*kube.Result, error) { - _, err := io.Copy(p.Out, bufferize(resources)) - if err != nil { - return nil, err - } - return &kube.Result{Created: resources}, nil -} - -func (p *PrintingKubeClient) Wait(resources kube.ResourceList, _ time.Duration) error { - _, err := io.Copy(p.Out, bufferize(resources)) - return err -} - -// Delete implements KubeClient delete. -// -// It only prints out the content to be deleted. -func (p *PrintingKubeClient) Delete(resources kube.ResourceList) (*kube.Result, []error) { - _, err := io.Copy(p.Out, bufferize(resources)) - if err != nil { - return nil, []error{err} - } - return &kube.Result{Deleted: resources}, nil -} - -// WatchUntilReady implements KubeClient WatchUntilReady. -func (p *PrintingKubeClient) WatchUntilReady(resources kube.ResourceList, _ time.Duration) error { - _, err := io.Copy(p.Out, bufferize(resources)) - return err -} - -// Update implements KubeClient Update. -func (p *PrintingKubeClient) Update(_, modified kube.ResourceList, _ bool) (*kube.Result, error) { - _, err := io.Copy(p.Out, bufferize(modified)) - if err != nil { - return nil, err - } - // TODO: This doesn't completely mock out have some that get created, - // updated, and deleted. I don't think these are used in any unit tests, but - // we may want to refactor a way to handle future tests - return &kube.Result{Updated: modified}, nil -} - -// Build implements KubeClient Build. -func (p *PrintingKubeClient) Build(_ io.Reader, _ bool) (kube.ResourceList, error) { - return []*resource.Info{}, nil -} - -// WaitAndGetCompletedPodPhase implements KubeClient WaitAndGetCompletedPodPhase. -func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(_ string, _ time.Duration) (v1.PodPhase, error) { - return v1.PodSucceeded, nil -} - -func bufferize(resources kube.ResourceList) io.Reader { - var builder strings.Builder - for _, info := range resources { - builder.WriteString(info.String() + "\n") - } - return strings.NewReader(builder.String()) -} diff --git a/pkg/kube/interface.go b/pkg/kube/interface.go deleted file mode 100644 index 4bf6121..0000000 --- a/pkg/kube/interface.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube - -import ( - "io" - "time" - - v1 "k8s.io/api/core/v1" -) - -// Interface represents a client capable of communicating with the Kubernetes API. -// -// A KubernetesClient must be concurrency safe. -type Interface interface { - // Create creates one or more resources. - Create(resources ResourceList) (*Result, error) - - Wait(resources ResourceList, timeout time.Duration) error - - // Delete destroys one or more resources. - Delete(resources ResourceList) (*Result, []error) - - // Watch the resource in reader until it is "ready". This method - // - // For Jobs, "ready" means the Job ran to completion (exited without error). - // For Pods, "ready" means the Pod phase is marked "succeeded". - // For all other kinds, it means the kind was created or modified without - // error. - WatchUntilReady(resources ResourceList, timeout time.Duration) error - - // Update updates one or more resources or creates the resource - // if it doesn't exist. - Update(original, target ResourceList, force bool) (*Result, error) - - // Build creates a resource list from a Reader - // - // reader must contain a YAML stream (one or more YAML documents separated - // by "\n---\n") - // - // Validates against OpenAPI schema if validate is true. - Build(reader io.Reader, validate bool) (ResourceList, error) - - // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase - // and returns said phase (PodSucceeded or PodFailed qualify). - WaitAndGetCompletedPodPhase(name string, timeout time.Duration) (v1.PodPhase, error) - - // isReachable checks whether the client is able to connect to the cluster - IsReachable() error -} - -var _ Interface = (*Client)(nil) diff --git a/pkg/kube/resource.go b/pkg/kube/resource.go deleted file mode 100644 index ee8f83a..0000000 --- a/pkg/kube/resource.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube // import "helm.sh/helm/v3/pkg/kube" - -import "k8s.io/cli-runtime/pkg/resource" - -// ResourceList provides convenience methods for comparing collections of Infos. -type ResourceList []*resource.Info - -// Append adds an Info to the Result. -func (r *ResourceList) Append(val *resource.Info) { - *r = append(*r, val) -} - -// Visit implements resource.Visitor. -func (r ResourceList) Visit(fn resource.VisitorFunc) error { - for _, i := range r { - if err := fn(i, nil); err != nil { - return err - } - } - return nil -} - -// Filter returns a new Result with Infos that satisfy the predicate fn. -func (r ResourceList) Filter(fn func(*resource.Info) bool) ResourceList { - var result ResourceList - for _, i := range r { - if fn(i) { - result.Append(i) - } - } - return result -} - -// Get returns the Info from the result that matches the name and kind. -func (r ResourceList) Get(info *resource.Info) *resource.Info { - for _, i := range r { - if isMatchingInfo(i, info) { - return i - } - } - return nil -} - -// Contains checks to see if an object exists. -func (r ResourceList) Contains(info *resource.Info) bool { - for _, i := range r { - if isMatchingInfo(i, info) { - return true - } - } - return false -} - -// Difference will return a new Result with objects not contained in rs. -func (r ResourceList) Difference(rs ResourceList) ResourceList { - return r.Filter(func(info *resource.Info) bool { - return !rs.Contains(info) - }) -} - -// Intersect will return a new Result with objects contained in both Results. -func (r ResourceList) Intersect(rs ResourceList) ResourceList { - return r.Filter(rs.Contains) -} - -// isMatchingInfo returns true if infos match on Name and GroupVersionKind. -func isMatchingInfo(a, b *resource.Info) bool { - return a.Name == b.Name && a.Namespace == b.Namespace && a.Mapping.GroupVersionKind.Kind == b.Mapping.GroupVersionKind.Kind -} diff --git a/pkg/kube/resource_test.go b/pkg/kube/resource_test.go deleted file mode 100644 index 3c906ce..0000000 --- a/pkg/kube/resource_test.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube // import "helm.sh/helm/v3/pkg/kube" - -import ( - "testing" - - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/cli-runtime/pkg/resource" -) - -func TestResourceList(t *testing.T) { - mapping := &meta.RESTMapping{ - Resource: schema.GroupVersionResource{Group: "group", Version: "version", Resource: "pod"}, - } - - info := func(name string) *resource.Info { - return &resource.Info{Name: name, Mapping: mapping} - } - - var r1, r2 ResourceList - r1 = []*resource.Info{info("foo"), info("bar")} - r2 = []*resource.Info{info("bar")} - - if r1.Get(info("bar")).Mapping.Resource.Resource != "pod" { - t.Error("expected get pod") - } - - diff := r1.Difference(r2) - if len(diff) != 1 { - t.Error("expected 1 result") - } - - if !diff.Contains(info("foo")) { - t.Error("expected diff to return foo") - } - - inter := r1.Intersect(r2) - if len(inter) != 1 { - t.Error("expected 1 result") - } - - if !inter.Contains(info("bar")) { - t.Error("expected intersect to return bar") - } -} diff --git a/pkg/kube/result.go b/pkg/kube/result.go deleted file mode 100644 index c3e171c..0000000 --- a/pkg/kube/result.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube - -// Result contains the information of created, updated, and deleted resources -// for various kube API calls along with helper methods for using those -// resources -type Result struct { - Created ResourceList - Updated ResourceList - Deleted ResourceList -} - -// If needed, we can add methods to the Result type for things like diffing diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go deleted file mode 100644 index f0005a6..0000000 --- a/pkg/kube/wait.go +++ /dev/null @@ -1,379 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kube // import "helm.sh/helm/v3/pkg/kube" - -import ( - "fmt" - "time" - - "github.com/pkg/errors" - appsv1 "k8s.io/api/apps/v1" - appsv1beta1 "k8s.io/api/apps/v1beta1" - appsv1beta2 "k8s.io/api/apps/v1beta2" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - - deploymentutil "helm.sh/helm/v3/internal/third_party/k8s.io/kubernetes/deployment/util" -) - -type waiter struct { - c kubernetes.Interface - timeout time.Duration - log func(string, ...interface{}) -} - -// waitForResources polls to get the current status of all pods, PVCs, and Services -// until all are ready or a timeout is reached -func (w *waiter) waitForResources(created ResourceList) error { - w.log("beginning wait for %d resources with timeout of %v", len(created), w.timeout) - - return wait.Poll(2*time.Second, w.timeout, func() (bool, error) { - for _, v := range created { - var ( - // This defaults to true, otherwise we get to a point where - // things will always return false unless one of the objects - // that manages pods has been hit - ok = true - err error - ) - switch value := AsVersioned(v).(type) { - case *corev1.Pod: - pod, err := w.c.CoreV1().Pods(v.Namespace).Get(v.Name, metav1.GetOptions{}) - if err != nil || !w.isPodReady(pod) { - return false, err - } - case *appsv1.Deployment, *appsv1beta1.Deployment, *appsv1beta2.Deployment, *extensionsv1beta1.Deployment: - currentDeployment, err := w.c.AppsV1().Deployments(v.Namespace).Get(v.Name, metav1.GetOptions{}) - if err != nil { - return false, err - } - // If paused deployment will never be ready - if currentDeployment.Spec.Paused { - continue - } - // Find RS associated with deployment - newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, w.c.AppsV1()) - if err != nil || newReplicaSet == nil { - return false, err - } - if !w.deploymentReady(newReplicaSet, currentDeployment) { - return false, nil - } - case *corev1.PersistentVolumeClaim: - claim, err := w.c.CoreV1().PersistentVolumeClaims(v.Namespace).Get(v.Name, metav1.GetOptions{}) - if err != nil { - return false, err - } - if !w.volumeReady(claim) { - return false, nil - } - case *corev1.Service: - svc, err := w.c.CoreV1().Services(v.Namespace).Get(v.Name, metav1.GetOptions{}) - if err != nil { - return false, err - } - if !w.serviceReady(svc) { - return false, nil - } - case *extensionsv1beta1.DaemonSet, *appsv1.DaemonSet, *appsv1beta2.DaemonSet: - ds, err := w.c.AppsV1().DaemonSets(v.Namespace).Get(v.Name, metav1.GetOptions{}) - if err != nil { - return false, err - } - if !w.daemonSetReady(ds) { - return false, nil - } - case *apiextv1beta1.CustomResourceDefinition: - if err := v.Get(); err != nil { - return false, err - } - crd := &apiextv1beta1.CustomResourceDefinition{} - if err := scheme.Scheme.Convert(v.Object, crd, nil); err != nil { - return false, err - } - if !w.crdBetaReady(*crd) { - return false, nil - } - case *apiextv1.CustomResourceDefinition: - if err := v.Get(); err != nil { - return false, err - } - crd := &apiextv1.CustomResourceDefinition{} - if err := scheme.Scheme.Convert(v.Object, crd, nil); err != nil { - return false, err - } - if !w.crdReady(*crd) { - return false, nil - } - case *appsv1.StatefulSet, *appsv1beta1.StatefulSet, *appsv1beta2.StatefulSet: - sts, err := w.c.AppsV1().StatefulSets(v.Namespace).Get(v.Name, metav1.GetOptions{}) - if err != nil { - return false, err - } - if !w.statefulSetReady(sts) { - return false, nil - } - case *corev1.ReplicationController, *extensionsv1beta1.ReplicaSet, *appsv1beta2.ReplicaSet, *appsv1.ReplicaSet: - ok, err = w.podsReadyForObject(v.Namespace, value) - } - if !ok || err != nil { - return false, err - } - } - return true, nil - }) -} - -func (w *waiter) podsReadyForObject(namespace string, obj runtime.Object) (bool, error) { - pods, err := w.podsforObject(namespace, obj) - if err != nil { - return false, err - } - for _, pod := range pods { - if !w.isPodReady(&pod) { - return false, nil - } - } - return true, nil -} - -func (w *waiter) podsforObject(namespace string, obj runtime.Object) ([]corev1.Pod, error) { - selector, err := SelectorsForObject(obj) - if err != nil { - return nil, err - } - list, err := getPods(w.c, namespace, selector.String()) - return list, err -} - -// isPodReady returns true if a pod is ready; false otherwise. -func (w *waiter) isPodReady(pod *corev1.Pod) bool { - for _, c := range pod.Status.Conditions { - if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue { - return true - } - } - w.log("Pod is not ready: %s/%s", pod.GetNamespace(), pod.GetName()) - return false -} - -func (w *waiter) serviceReady(s *corev1.Service) bool { - // ExternalName Services are external to cluster so helm shouldn't be checking to see if they're 'ready' (i.e. have an IP Set) - if s.Spec.Type == corev1.ServiceTypeExternalName { - return true - } - - // Make sure the service is not explicitly set to "None" before checking the IP - if (s.Spec.ClusterIP != corev1.ClusterIPNone && s.Spec.ClusterIP == "") || - // This checks if the service has a LoadBalancer and that balancer has an Ingress defined - (s.Spec.Type == corev1.ServiceTypeLoadBalancer && s.Status.LoadBalancer.Ingress == nil) { - w.log("Service does not have IP address: %s/%s", s.GetNamespace(), s.GetName()) - return false - } - return true -} - -func (w *waiter) volumeReady(v *corev1.PersistentVolumeClaim) bool { - if v.Status.Phase != corev1.ClaimBound { - w.log("PersistentVolumeClaim is not bound: %s/%s", v.GetNamespace(), v.GetName()) - return false - } - return true -} - -func (w *waiter) deploymentReady(rs *appsv1.ReplicaSet, dep *appsv1.Deployment) bool { - expectedReady := *dep.Spec.Replicas - deploymentutil.MaxUnavailable(*dep) - if !(rs.Status.ReadyReplicas >= expectedReady) { - w.log("Deployment is not ready: %s/%s. %d out of %d expected pods are ready", dep.Namespace, dep.Name, rs.Status.ReadyReplicas, expectedReady) - return false - } - return true -} - -func (w *waiter) daemonSetReady(ds *appsv1.DaemonSet) bool { - // If the update strategy is not a rolling update, there will be nothing to wait for - if ds.Spec.UpdateStrategy.Type != appsv1.RollingUpdateDaemonSetStrategyType { - return true - } - - // Make sure all the updated pods have been scheduled - if ds.Status.UpdatedNumberScheduled != ds.Status.DesiredNumberScheduled { - w.log("DaemonSet is not ready: %s/%s. %d out of %d expected pods have been scheduled", ds.Namespace, ds.Name, ds.Status.UpdatedNumberScheduled, ds.Status.DesiredNumberScheduled) - return false - } - maxUnavailable, err := intstr.GetValueFromIntOrPercent(ds.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable, int(ds.Status.DesiredNumberScheduled), true) - if err != nil { - // If for some reason the value is invalid, set max unavailable to the - // number of desired replicas. This is the same behavior as the - // `MaxUnavailable` function in deploymentutil - maxUnavailable = int(ds.Status.DesiredNumberScheduled) - } - - expectedReady := int(ds.Status.DesiredNumberScheduled) - maxUnavailable - if !(int(ds.Status.NumberReady) >= expectedReady) { - w.log("DaemonSet is not ready: %s/%s. %d out of %d expected pods are ready", ds.Namespace, ds.Name, ds.Status.NumberReady, expectedReady) - return false - } - return true -} - -// Because the v1 extensions API is not available on all supported k8s versions -// yet and because Go doesn't support generics, we need to have a duplicate -// function to support the v1beta1 types -func (w *waiter) crdBetaReady(crd apiextv1beta1.CustomResourceDefinition) bool { - for _, cond := range crd.Status.Conditions { - switch cond.Type { - case apiextv1beta1.Established: - if cond.Status == apiextv1beta1.ConditionTrue { - return true - } - case apiextv1beta1.NamesAccepted: - if cond.Status == apiextv1beta1.ConditionFalse { - // This indicates a naming conflict, but it's probably not the - // job of this function to fail because of that. Instead, - // we treat it as a success, since the process should be able to - // continue. - return true - } - } - } - return false -} - -func (w *waiter) crdReady(crd apiextv1.CustomResourceDefinition) bool { - for _, cond := range crd.Status.Conditions { - switch cond.Type { - case apiextv1.Established: - if cond.Status == apiextv1.ConditionTrue { - return true - } - case apiextv1.NamesAccepted: - if cond.Status == apiextv1.ConditionFalse { - // This indicates a naming conflict, but it's probably not the - // job of this function to fail because of that. Instead, - // we treat it as a success, since the process should be able to - // continue. - return true - } - } - } - return false -} - -func (w *waiter) statefulSetReady(sts *appsv1.StatefulSet) bool { - // If the update strategy is not a rolling update, there will be nothing to wait for - if sts.Spec.UpdateStrategy.Type != appsv1.RollingUpdateStatefulSetStrategyType { - return true - } - - // Dereference all the pointers because StatefulSets like them - var partition int - // 1 is the default for replicas if not set - var replicas = 1 - // For some reason, even if the update strategy is a rolling update, the - // actual rollingUpdate field can be nil. If it is, we can safely assume - // there is no partition value - if sts.Spec.UpdateStrategy.RollingUpdate != nil && sts.Spec.UpdateStrategy.RollingUpdate.Partition != nil { - partition = int(*sts.Spec.UpdateStrategy.RollingUpdate.Partition) - } - if sts.Spec.Replicas != nil { - replicas = int(*sts.Spec.Replicas) - } - - // Because an update strategy can use partitioning, we need to calculate the - // number of updated replicas we should have. For example, if the replicas - // is set to 3 and the partition is 2, we'd expect only one pod to be - // updated - expectedReplicas := replicas - partition - - // Make sure all the updated pods have been scheduled - if int(sts.Status.UpdatedReplicas) != expectedReplicas { - w.log("StatefulSet is not ready: %s/%s. %d out of %d expected pods have been scheduled", sts.Namespace, sts.Name, sts.Status.UpdatedReplicas, expectedReplicas) - return false - } - - if int(sts.Status.ReadyReplicas) != replicas { - w.log("StatefulSet is not ready: %s/%s. %d out of %d expected pods are ready", sts.Namespace, sts.Name, sts.Status.ReadyReplicas, replicas) - return false - } - return true -} - -func getPods(client kubernetes.Interface, namespace, selector string) ([]corev1.Pod, error) { - list, err := client.CoreV1().Pods(namespace).List(metav1.ListOptions{ - LabelSelector: selector, - }) - return list.Items, err -} - -// SelectorsForObject returns the pod label selector for a given object -// -// Modified version of https://github.com/kubernetes/kubernetes/blob/v1.14.1/pkg/kubectl/polymorphichelpers/helpers.go#L84 -func SelectorsForObject(object runtime.Object) (selector labels.Selector, err error) { - switch t := object.(type) { - case *extensionsv1beta1.ReplicaSet: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *appsv1.ReplicaSet: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *appsv1beta2.ReplicaSet: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *corev1.ReplicationController: - selector = labels.SelectorFromSet(t.Spec.Selector) - case *appsv1.StatefulSet: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *appsv1beta1.StatefulSet: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *appsv1beta2.StatefulSet: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *extensionsv1beta1.DaemonSet: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *appsv1.DaemonSet: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *appsv1beta2.DaemonSet: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *extensionsv1beta1.Deployment: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *appsv1.Deployment: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *appsv1beta1.Deployment: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *appsv1beta2.Deployment: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *batchv1.Job: - selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) - case *corev1.Service: - if t.Spec.Selector == nil || len(t.Spec.Selector) == 0 { - return nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name) - } - selector = labels.SelectorFromSet(t.Spec.Selector) - - default: - return nil, fmt.Errorf("selector for %T not implemented", object) - } - - return selector, errors.Wrap(err, "invalid label selector") -} diff --git a/pkg/lint/lint.go b/pkg/lint/lint.go deleted file mode 100644 index d479516..0000000 --- a/pkg/lint/lint.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package lint // import "helm.sh/helm/v3/pkg/lint" - -import ( - "path/filepath" - - "helm.sh/helm/v3/pkg/lint/rules" - "helm.sh/helm/v3/pkg/lint/support" -) - -// All runs all of the available linters on the given base directory. -func All(basedir string, values map[string]interface{}, namespace string, strict bool) support.Linter { - // Using abs path to get directory context - chartDir, _ := filepath.Abs(basedir) - - linter := support.Linter{ChartDir: chartDir} - rules.Chartfile(&linter) - rules.Values(&linter) - rules.Templates(&linter, values, namespace, strict) - return linter -} diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go deleted file mode 100644 index 2a982d0..0000000 --- a/pkg/lint/lint_test.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package lint - -import ( - "strings" - "testing" - - "helm.sh/helm/v3/pkg/lint/support" -) - -var values map[string]interface{} - -const namespace = "testNamespace" -const strict = false - -const badChartDir = "rules/testdata/badchartfile" -const badValuesFileDir = "rules/testdata/badvaluesfile" -const badYamlFileDir = "rules/testdata/albatross" -const goodChartDir = "rules/testdata/goodone" - -func TestBadChart(t *testing.T) { - m := All(badChartDir, values, namespace, strict).Messages - if len(m) != 7 { - t.Errorf("Number of errors %v", len(m)) - t.Errorf("All didn't fail with expected errors, got %#v", m) - } - // There should be one INFO, 2 WARNINGs and one ERROR messages, check for them - var i, w, e, e2, e3, e4, e5 bool - for _, msg := range m { - if msg.Severity == support.InfoSev { - if strings.Contains(msg.Err.Error(), "icon is recommended") { - i = true - } - } - if msg.Severity == support.WarningSev { - if strings.Contains(msg.Err.Error(), "directory not found") { - w = true - } - } - if msg.Severity == support.ErrorSev { - if strings.Contains(msg.Err.Error(), "version '0.0.0.0' is not a valid SemVer") { - e = true - } - if strings.Contains(msg.Err.Error(), "name is required") { - e2 = true - } - - if strings.Contains(msg.Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") { - e3 = true - } - - if strings.Contains(msg.Err.Error(), "chart type is not valid in apiVersion") { - e4 = true - } - - if strings.Contains(msg.Err.Error(), "dependencies are not valid in the Chart file with apiVersion") { - e5 = true - } - } - } - if !e || !e2 || !e3 || !e4 || !e5 || !w || !i { - t.Errorf("Didn't find all the expected errors, got %#v", m) - } -} - -func TestInvalidYaml(t *testing.T) { - m := All(badYamlFileDir, values, namespace, strict).Messages - if len(m) != 1 { - t.Fatalf("All didn't fail with expected errors, got %#v", m) - } - if !strings.Contains(m[0].Err.Error(), "deliberateSyntaxError") { - t.Errorf("All didn't have the error for deliberateSyntaxError") - } -} - -func TestBadValues(t *testing.T) { - m := All(badValuesFileDir, values, namespace, strict).Messages - if len(m) < 1 { - t.Fatalf("All didn't fail with expected errors, got %#v", m) - } - if !strings.Contains(m[0].Err.Error(), "cannot unmarshal") { - t.Errorf("All didn't have the error for invalid key format: %s", m[0].Err) - } -} - -func TestGoodChart(t *testing.T) { - m := All(goodChartDir, values, namespace, strict).Messages - if len(m) != 0 { - t.Errorf("All failed but shouldn't have: %#v", m) - } -} diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go deleted file mode 100644 index 91a64fe..0000000 --- a/pkg/lint/rules/chartfile.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules // import "helm.sh/helm/v3/pkg/lint/rules" - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/Masterminds/semver/v3" - "github.com/asaskevich/govalidator" - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/lint/support" -) - -// Chartfile runs a set of linter rules related to Chart.yaml file -func Chartfile(linter *support.Linter) { - chartFileName := "Chart.yaml" - chartPath := filepath.Join(linter.ChartDir, chartFileName) - - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartYamlNotDirectory(chartPath)) - - chartFile, err := chartutil.LoadChartfile(chartPath) - validChartFile := linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartYamlFormat(err)) - - // Guard clause. Following linter rules require a parsable ChartFile - if !validChartFile { - return - } - - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile)) - - // Chart metadata - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersion(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartMaintainer(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartSources(chartFile)) - linter.RunLinterRule(support.InfoSev, chartFileName, validateChartIconPresence(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartIconURL(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartType(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartDependencies(chartFile)) -} - -func validateChartYamlNotDirectory(chartPath string) error { - fi, err := os.Stat(chartPath) - - if err == nil && fi.IsDir() { - return errors.New("should be a file, not a directory") - } - return nil -} - -func validateChartYamlFormat(chartFileError error) error { - if chartFileError != nil { - return errors.Errorf("unable to parse YAML\n\t%s", chartFileError.Error()) - } - return nil -} - -func validateChartName(cf *chart.Metadata) error { - if cf.Name == "" { - return errors.New("name is required") - } - return nil -} - -func validateChartAPIVersion(cf *chart.Metadata) error { - if cf.APIVersion == "" { - return errors.New("apiVersion is required. The value must be either \"v1\" or \"v2\"") - } - - if cf.APIVersion != chart.APIVersionV1 && cf.APIVersion != chart.APIVersionV2 { - return fmt.Errorf("apiVersion '%s' is not valid. The value must be either \"v1\" or \"v2\"", cf.APIVersion) - } - - return nil -} - -func validateChartVersion(cf *chart.Metadata) error { - if cf.Version == "" { - return errors.New("version is required") - } - - version, err := semver.NewVersion(cf.Version) - - if err != nil { - return errors.Errorf("version '%s' is not a valid SemVer", cf.Version) - } - - c, err := semver.NewConstraint(">0.0.0-0") - if err != nil { - return err - } - valid, msg := c.Validate(version) - - if !valid && len(msg) > 0 { - return errors.Errorf("version %v", msg[0]) - } - - return nil -} - -func validateChartMaintainer(cf *chart.Metadata) error { - for _, maintainer := range cf.Maintainers { - if maintainer.Name == "" { - return errors.New("each maintainer requires a name") - } else if maintainer.Email != "" && !govalidator.IsEmail(maintainer.Email) { - return errors.Errorf("invalid email '%s' for maintainer '%s'", maintainer.Email, maintainer.Name) - } else if maintainer.URL != "" && !govalidator.IsURL(maintainer.URL) { - return errors.Errorf("invalid url '%s' for maintainer '%s'", maintainer.URL, maintainer.Name) - } - } - return nil -} - -func validateChartSources(cf *chart.Metadata) error { - for _, source := range cf.Sources { - if source == "" || !govalidator.IsRequestURL(source) { - return errors.Errorf("invalid source URL '%s'", source) - } - } - return nil -} - -func validateChartIconPresence(cf *chart.Metadata) error { - if cf.Icon == "" { - return errors.New("icon is recommended") - } - return nil -} - -func validateChartIconURL(cf *chart.Metadata) error { - if cf.Icon != "" && !govalidator.IsRequestURL(cf.Icon) { - return errors.Errorf("invalid icon URL '%s'", cf.Icon) - } - return nil -} - -func validateChartDependencies(cf *chart.Metadata) error { - if len(cf.Dependencies) > 0 && cf.APIVersion != chart.APIVersionV2 { - return fmt.Errorf("dependencies are not valid in the Chart file with apiVersion '%s'. They are valid in apiVersion '%s'", cf.APIVersion, chart.APIVersionV2) - } - return nil -} - -func validateChartType(cf *chart.Metadata) error { - if len(cf.Type) > 0 && cf.APIVersion != chart.APIVersionV2 { - return fmt.Errorf("chart type is not valid in apiVersion '%s'. It is valid in apiVersion '%s'", cf.APIVersion, chart.APIVersionV2) - } - return nil -} diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go deleted file mode 100644 index d032dd1..0000000 --- a/pkg/lint/rules/chartfile_test.go +++ /dev/null @@ -1,219 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/lint/support" -) - -const ( - badChartDir = "testdata/badchartfile" -) - -var ( - badChartFilePath = filepath.Join(badChartDir, "Chart.yaml") - nonExistingChartFilePath = filepath.Join(os.TempDir(), "Chart.yaml") -) - -var badChart, _ = chartutil.LoadChartfile(badChartFilePath) - -// Validation functions Test -func TestValidateChartYamlNotDirectory(t *testing.T) { - _ = os.Mkdir(nonExistingChartFilePath, os.ModePerm) - defer os.Remove(nonExistingChartFilePath) - - err := validateChartYamlNotDirectory(nonExistingChartFilePath) - if err == nil { - t.Errorf("validateChartYamlNotDirectory to return a linter error, got no error") - } -} - -func TestValidateChartYamlFormat(t *testing.T) { - err := validateChartYamlFormat(errors.New("Read error")) - if err == nil { - t.Errorf("validateChartYamlFormat to return a linter error, got no error") - } - - err = validateChartYamlFormat(nil) - if err != nil { - t.Errorf("validateChartYamlFormat to return no error, got a linter error") - } -} - -func TestValidateChartName(t *testing.T) { - err := validateChartName(badChart) - if err == nil { - t.Errorf("validateChartName to return a linter error, got no error") - } -} - -func TestValidateChartVersion(t *testing.T) { - var failTest = []struct { - Version string - ErrorMsg string - }{ - {"", "version is required"}, - {"1.2.3.4", "version '1.2.3.4' is not a valid SemVer"}, - {"waps", "'waps' is not a valid SemVer"}, - {"-3", "'-3' is not a valid SemVer"}, - } - - var successTest = []string{"0.0.1", "0.0.1+build", "0.0.1-beta"} - - for _, test := range failTest { - badChart.Version = test.Version - err := validateChartVersion(badChart) - if err == nil || !strings.Contains(err.Error(), test.ErrorMsg) { - t.Errorf("validateChartVersion(%s) to return \"%s\", got no error", test.Version, test.ErrorMsg) - } - } - - for _, version := range successTest { - badChart.Version = version - err := validateChartVersion(badChart) - if err != nil { - t.Errorf("validateChartVersion(%s) to return no error, got a linter error", version) - } - } -} - -func TestValidateChartMaintainer(t *testing.T) { - var failTest = []struct { - Name string - Email string - ErrorMsg string - }{ - {"", "", "each maintainer requires a name"}, - {"", "test@test.com", "each maintainer requires a name"}, - {"John Snow", "wrongFormatEmail.com", "invalid email"}, - } - - var successTest = []struct { - Name string - Email string - }{ - {"John Snow", ""}, - {"John Snow", "john@winterfell.com"}, - } - - for _, test := range failTest { - badChart.Maintainers = []*chart.Maintainer{{Name: test.Name, Email: test.Email}} - err := validateChartMaintainer(badChart) - if err == nil || !strings.Contains(err.Error(), test.ErrorMsg) { - t.Errorf("validateChartMaintainer(%s, %s) to return \"%s\", got no error", test.Name, test.Email, test.ErrorMsg) - } - } - - for _, test := range successTest { - badChart.Maintainers = []*chart.Maintainer{{Name: test.Name, Email: test.Email}} - err := validateChartMaintainer(badChart) - if err != nil { - t.Errorf("validateChartMaintainer(%s, %s) to return no error, got %s", test.Name, test.Email, err.Error()) - } - } -} - -func TestValidateChartSources(t *testing.T) { - var failTest = []string{"", "RiverRun", "john@winterfell", "riverrun.io"} - var successTest = []string{"http://riverrun.io", "https://riverrun.io", "https://riverrun.io/blackfish"} - for _, test := range failTest { - badChart.Sources = []string{test} - err := validateChartSources(badChart) - if err == nil || !strings.Contains(err.Error(), "invalid source URL") { - t.Errorf("validateChartSources(%s) to return \"invalid source URL\", got no error", test) - } - } - - for _, test := range successTest { - badChart.Sources = []string{test} - err := validateChartSources(badChart) - if err != nil { - t.Errorf("validateChartSources(%s) to return no error, got %s", test, err.Error()) - } - } -} - -func TestValidateChartIconPresence(t *testing.T) { - err := validateChartIconPresence(badChart) - if err == nil { - t.Errorf("validateChartIconPresence to return a linter error, got no error") - } -} - -func TestValidateChartIconURL(t *testing.T) { - var failTest = []string{"RiverRun", "john@winterfell", "riverrun.io"} - var successTest = []string{"http://riverrun.io", "https://riverrun.io", "https://riverrun.io/blackfish.png"} - for _, test := range failTest { - badChart.Icon = test - err := validateChartIconURL(badChart) - if err == nil || !strings.Contains(err.Error(), "invalid icon URL") { - t.Errorf("validateChartIconURL(%s) to return \"invalid icon URL\", got no error", test) - } - } - - for _, test := range successTest { - badChart.Icon = test - err := validateChartSources(badChart) - if err != nil { - t.Errorf("validateChartIconURL(%s) to return no error, got %s", test, err.Error()) - } - } -} - -func TestChartfile(t *testing.T) { - linter := support.Linter{ChartDir: badChartDir} - Chartfile(&linter) - msgs := linter.Messages - - if len(msgs) != 6 { - t.Errorf("Expected 6 errors, got %d", len(msgs)) - } - - if !strings.Contains(msgs[0].Err.Error(), "name is required") { - t.Errorf("Unexpected message 0: %s", msgs[0].Err) - } - - if !strings.Contains(msgs[1].Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") { - t.Errorf("Unexpected message 1: %s", msgs[1].Err) - } - - if !strings.Contains(msgs[2].Err.Error(), "version '0.0.0.0' is not a valid SemVer") { - t.Errorf("Unexpected message 2: %s", msgs[2].Err) - } - - if !strings.Contains(msgs[3].Err.Error(), "icon is recommended") { - t.Errorf("Unexpected message 3: %s", msgs[3].Err) - } - - if !strings.Contains(msgs[4].Err.Error(), "chart type is not valid in apiVersion") { - t.Errorf("Unexpected message 4: %s", msgs[4].Err) - } - - if !strings.Contains(msgs[5].Err.Error(), "dependencies are not valid in the Chart file with apiVersion") { - t.Errorf("Unexpected message 5: %s", msgs[5].Err) - } - -} diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go deleted file mode 100644 index 5c6cd73..0000000 --- a/pkg/lint/rules/template.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "os" - "path/filepath" - "regexp" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/engine" - "helm.sh/helm/v3/pkg/lint/support" -) - -var ( - crdHookSearch = regexp.MustCompile(`"?helm\.sh/hook"?:\s+crd-install`) - releaseTimeSearch = regexp.MustCompile(`\.Release\.Time`) -) - -// Templates lints the templates in the Linter. -func Templates(linter *support.Linter, values map[string]interface{}, namespace string, strict bool) { - path := "templates/" - templatesPath := filepath.Join(linter.ChartDir, path) - - templatesDirExist := linter.RunLinterRule(support.WarningSev, path, validateTemplatesDir(templatesPath)) - - // Templates directory is optional for now - if !templatesDirExist { - return - } - - // Load chart and parse templates, based on tiller/release_server - chart, err := loader.Load(linter.ChartDir) - - chartLoaded := linter.RunLinterRule(support.ErrorSev, path, err) - - if !chartLoaded { - return - } - - options := chartutil.ReleaseOptions{ - Name: "testRelease", - Namespace: namespace, - } - - cvals, err := chartutil.CoalesceValues(chart, values) - if err != nil { - return - } - valuesToRender, err := chartutil.ToRenderValues(chart, cvals, options, nil) - if err != nil { - linter.RunLinterRule(support.ErrorSev, path, err) - return - } - var e engine.Engine - e.Strict = strict - e.LintMode = true - renderedContentMap, err := e.Render(chart, valuesToRender) - - renderOk := linter.RunLinterRule(support.ErrorSev, path, err) - - if !renderOk { - return - } - - /* Iterate over all the templates to check: - - It is a .yaml file - - All the values in the template file is defined - - {{}} include | quote - - Generated content is a valid Yaml file - - Metadata.Namespace is not set - */ - for _, template := range chart.Templates { - fileName, data := template.Name, template.Data - path = fileName - - linter.RunLinterRule(support.ErrorSev, path, validateAllowedExtension(fileName)) - // These are v3 specific checks to make sure and warn people if their - // chart is not compatible with v3 - linter.RunLinterRule(support.WarningSev, path, validateNoCRDHooks(data)) - linter.RunLinterRule(support.ErrorSev, path, validateNoReleaseTime(data)) - - // We only apply the following lint rules to yaml files - if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" { - continue - } - - // NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1463 - // Check that all the templates have a matching value - //linter.RunLinterRule(support.WarningSev, path, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate)) - - // NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1037 - // linter.RunLinterRule(support.WarningSev, path, validateQuotes(string(preExecutedTemplate))) - - renderedContent := renderedContentMap[filepath.Join(chart.Name(), fileName)] - var yamlStruct K8sYamlStruct - // Even though K8sYamlStruct only defines Metadata namespace, an error in any other - // key will be raised as well - err := yaml.Unmarshal([]byte(renderedContent), &yamlStruct) - - validYaml := linter.RunLinterRule(support.ErrorSev, path, validateYamlContent(err)) - - if !validYaml { - continue - } - } -} - -// Validation functions -func validateTemplatesDir(templatesPath string) error { - if fi, err := os.Stat(templatesPath); err != nil { - return errors.New("directory not found") - } else if !fi.IsDir() { - return errors.New("not a directory") - } - return nil -} - -func validateAllowedExtension(fileName string) error { - ext := filepath.Ext(fileName) - validExtensions := []string{".yaml", ".yml", ".tpl", ".txt"} - - for _, b := range validExtensions { - if b == ext { - return nil - } - } - - return errors.Errorf("file extension '%s' not valid. Valid extensions are .yaml, .yml, .tpl, or .txt", ext) -} - -func validateYamlContent(err error) error { - return errors.Wrap(err, "unable to parse YAML") -} - -func validateNoCRDHooks(manifest []byte) error { - if crdHookSearch.Match(manifest) { - return errors.New("manifest is a crd-install hook. This hook is no longer supported in v3 and all CRDs should also exist the crds/ directory at the top level of the chart") - } - return nil -} - -func validateNoReleaseTime(manifest []byte) error { - if releaseTimeSearch.Match(manifest) { - return errors.New(".Release.Time has been removed in v3, please replace with the `now` function in your templates") - } - return nil -} - -// K8sYamlStruct stubs a Kubernetes YAML file. -// Need to access for now to Namespace only -type K8sYamlStruct struct { - Metadata struct { - Namespace string - } -} diff --git a/pkg/lint/rules/template_test.go b/pkg/lint/rules/template_test.go deleted file mode 100644 index ddb46ab..0000000 --- a/pkg/lint/rules/template_test.go +++ /dev/null @@ -1,103 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "os" - "path/filepath" - "strings" - "testing" - - "helm.sh/helm/v3/pkg/lint/support" -) - -const templateTestBasedir = "./testdata/albatross" - -func TestValidateAllowedExtension(t *testing.T) { - var failTest = []string{"/foo", "/test.toml"} - for _, test := range failTest { - err := validateAllowedExtension(test) - if err == nil || !strings.Contains(err.Error(), "Valid extensions are .yaml, .yml, .tpl, or .txt") { - t.Errorf("validateAllowedExtension('%s') to return \"Valid extensions are .yaml, .yml, .tpl, or .txt\", got no error", test) - } - } - var successTest = []string{"/foo.yaml", "foo.yaml", "foo.tpl", "/foo/bar/baz.yaml", "NOTES.txt"} - for _, test := range successTest { - err := validateAllowedExtension(test) - if err != nil { - t.Errorf("validateAllowedExtension('%s') to return no error but got \"%s\"", test, err.Error()) - } - } -} - -var values = map[string]interface{}{"nameOverride": "", "httpPort": 80} - -const namespace = "testNamespace" -const strict = false - -func TestTemplateParsing(t *testing.T) { - linter := support.Linter{ChartDir: templateTestBasedir} - Templates(&linter, values, namespace, strict) - res := linter.Messages - - if len(res) != 1 { - t.Fatalf("Expected one error, got %d, %v", len(res), res) - } - - if !strings.Contains(res[0].Err.Error(), "deliberateSyntaxError") { - t.Errorf("Unexpected error: %s", res[0]) - } -} - -var wrongTemplatePath = filepath.Join(templateTestBasedir, "templates", "fail.yaml") -var ignoredTemplatePath = filepath.Join(templateTestBasedir, "fail.yaml.ignored") - -// Test a template with all the existing features: -// namespaces, partial templates -func TestTemplateIntegrationHappyPath(t *testing.T) { - // Rename file so it gets ignored by the linter - os.Rename(wrongTemplatePath, ignoredTemplatePath) - defer os.Rename(ignoredTemplatePath, wrongTemplatePath) - - linter := support.Linter{ChartDir: templateTestBasedir} - Templates(&linter, values, namespace, strict) - res := linter.Messages - - if len(res) != 0 { - t.Fatalf("Expected no error, got %d, %v", len(res), res) - } -} - -func TestV3Fail(t *testing.T) { - linter := support.Linter{ChartDir: "./testdata/v3-fail"} - Templates(&linter, values, namespace, strict) - res := linter.Messages - - if len(res) != 3 { - t.Fatalf("Expected 3 errors, got %d, %v", len(res), res) - } - - if !strings.Contains(res[0].Err.Error(), ".Release.Time has been removed in v3") { - t.Errorf("Unexpected error: %s", res[0].Err) - } - if !strings.Contains(res[1].Err.Error(), "manifest is a crd-install hook") { - t.Errorf("Unexpected error: %s", res[1].Err) - } - if !strings.Contains(res[2].Err.Error(), "manifest is a crd-install hook") { - t.Errorf("Unexpected error: %s", res[2].Err) - } -} diff --git a/pkg/lint/rules/testdata/albatross/Chart.yaml b/pkg/lint/rules/testdata/albatross/Chart.yaml deleted file mode 100644 index 21124ac..0000000 --- a/pkg/lint/rules/testdata/albatross/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: albatross -description: testing chart -version: 199.44.12345-Alpha.1+cafe009 -icon: http://riverrun.io diff --git a/pkg/lint/rules/testdata/albatross/templates/_helpers.tpl b/pkg/lint/rules/testdata/albatross/templates/_helpers.tpl deleted file mode 100644 index 24f76db..0000000 --- a/pkg/lint/rules/testdata/albatross/templates/_helpers.tpl +++ /dev/null @@ -1,16 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{define "name"}}{{default "nginx" .Values.nameOverride | trunc 63 | trimSuffix "-" }}{{end}} - -{{/* -Create a default fully qualified app name. - -We truncate at 63 chars because some Kubernetes name fields are limited to this -(by the DNS naming spec). -*/}} -{{define "fullname"}} -{{- $name := default "nginx" .Values.nameOverride -}} -{{printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{end}} diff --git a/pkg/lint/rules/testdata/albatross/templates/fail.yaml b/pkg/lint/rules/testdata/albatross/templates/fail.yaml deleted file mode 100644 index a11e0e9..0000000 --- a/pkg/lint/rules/testdata/albatross/templates/fail.yaml +++ /dev/null @@ -1 +0,0 @@ -{{ deliberateSyntaxError }} diff --git a/pkg/lint/rules/testdata/albatross/templates/svc.yaml b/pkg/lint/rules/testdata/albatross/templates/svc.yaml deleted file mode 100644 index 16bb27d..0000000 --- a/pkg/lint/rules/testdata/albatross/templates/svc.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# This is a service gateway to the replica set created by the deployment. -# Take a look at the deployment.yaml for general notes about this chart. -apiVersion: v1 -kind: Service -metadata: - name: "{{ .Values.name }}" - labels: - app.kubernetes.io/managed-by: {{ .Release.Service | quote }} - app.kubernetes.io/instance: {{ .Release.Name | quote }} - helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}" - kubeVersion: {{ .Capabilities.KubeVersion.Major }} -spec: - ports: - - port: {{default 80 .Values.httpPort | quote}} - targetPort: 80 - protocol: TCP - name: http - selector: - app.kubernetes.io/name: {{template "fullname" .}} diff --git a/pkg/lint/rules/testdata/albatross/values.yaml b/pkg/lint/rules/testdata/albatross/values.yaml deleted file mode 100644 index 74cc6a0..0000000 --- a/pkg/lint/rules/testdata/albatross/values.yaml +++ /dev/null @@ -1 +0,0 @@ -name: "mariner" diff --git a/pkg/lint/rules/testdata/badchartfile/Chart.yaml b/pkg/lint/rules/testdata/badchartfile/Chart.yaml deleted file mode 100644 index b80cf5f..0000000 --- a/pkg/lint/rules/testdata/badchartfile/Chart.yaml +++ /dev/null @@ -1,11 +0,0 @@ -description: A Helm chart for Kubernetes -version: 0.0.0.0 -home: "" -type: application -dependencies: -- name: mariadb - version: 5.x.x - repository: https://kubernetes-charts.storage.googleapis.com/ - condition: mariadb.enabled - tags: - - database diff --git a/pkg/lint/rules/testdata/badchartfile/values.yaml b/pkg/lint/rules/testdata/badchartfile/values.yaml deleted file mode 100644 index 9f36703..0000000 --- a/pkg/lint/rules/testdata/badchartfile/values.yaml +++ /dev/null @@ -1 +0,0 @@ -# Default values for badchartfile. diff --git a/pkg/lint/rules/testdata/badvaluesfile/Chart.yaml b/pkg/lint/rules/testdata/badvaluesfile/Chart.yaml deleted file mode 100644 index 632919d..0000000 --- a/pkg/lint/rules/testdata/badvaluesfile/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v1 -name: badvaluesfile -description: A Helm chart for Kubernetes -version: 0.0.1 -home: "" -icon: http://riverrun.io diff --git a/pkg/lint/rules/testdata/badvaluesfile/templates/badvaluesfile.yaml b/pkg/lint/rules/testdata/badvaluesfile/templates/badvaluesfile.yaml deleted file mode 100644 index 6c2ceb8..0000000 --- a/pkg/lint/rules/testdata/badvaluesfile/templates/badvaluesfile.yaml +++ /dev/null @@ -1,2 +0,0 @@ -metadata: - name: {{.name | default "foo" | title}} diff --git a/pkg/lint/rules/testdata/badvaluesfile/values.yaml b/pkg/lint/rules/testdata/badvaluesfile/values.yaml deleted file mode 100644 index b5a1027..0000000 --- a/pkg/lint/rules/testdata/badvaluesfile/values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -# Invalid value for badvaluesfile for testing lint fails with invalid yaml format -name= "value" diff --git a/pkg/lint/rules/testdata/goodone/Chart.yaml b/pkg/lint/rules/testdata/goodone/Chart.yaml deleted file mode 100644 index cb7a4bf..0000000 --- a/pkg/lint/rules/testdata/goodone/Chart.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -name: goodone -description: good testing chart -version: 199.44.12345-Alpha.1+cafe009 -icon: http://riverrun.io diff --git a/pkg/lint/rules/testdata/goodone/templates/goodone.yaml b/pkg/lint/rules/testdata/goodone/templates/goodone.yaml deleted file mode 100644 index 0e77f46..0000000 --- a/pkg/lint/rules/testdata/goodone/templates/goodone.yaml +++ /dev/null @@ -1,2 +0,0 @@ -metadata: - name: {{.Values.name | default "foo" | title}} diff --git a/pkg/lint/rules/testdata/goodone/values.yaml b/pkg/lint/rules/testdata/goodone/values.yaml deleted file mode 100644 index fe9abd9..0000000 --- a/pkg/lint/rules/testdata/goodone/values.yaml +++ /dev/null @@ -1 +0,0 @@ -name: "goodone here" diff --git a/pkg/lint/rules/testdata/v3-fail/Chart.yaml b/pkg/lint/rules/testdata/v3-fail/Chart.yaml deleted file mode 100644 index efbad1c..0000000 --- a/pkg/lint/rules/testdata/v3-fail/Chart.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v2 -name: v3-fail -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. -appVersion: 1.16.0 diff --git a/pkg/lint/rules/testdata/v3-fail/templates/_helpers.tpl b/pkg/lint/rules/testdata/v3-fail/templates/_helpers.tpl deleted file mode 100644 index 0b89e72..0000000 --- a/pkg/lint/rules/testdata/v3-fail/templates/_helpers.tpl +++ /dev/null @@ -1,63 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "v3-fail.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "v3-fail.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "v3-fail.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "v3-fail.labels" -}} -helm.sh/chart: {{ include "v3-fail.chart" . }} -{{ include "v3-fail.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Selector labels -*/}} -{{- define "v3-fail.selectorLabels" -}} -app.kubernetes.io/name: {{ include "v3-fail.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "v3-fail.serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include "v3-fail.fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} diff --git a/pkg/lint/rules/testdata/v3-fail/templates/deployment.yaml b/pkg/lint/rules/testdata/v3-fail/templates/deployment.yaml deleted file mode 100644 index 6d651ab..0000000 --- a/pkg/lint/rules/testdata/v3-fail/templates/deployment.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "v3-fail.fullname" . }} - labels: - nope: {{ .Release.Time }} - {{- include "v3-fail.labels" . | nindent 4 }} -spec: - replicas: {{ .Values.replicaCount }} - selector: - matchLabels: - {{- include "v3-fail.selectorLabels" . | nindent 6 }} - template: - metadata: - labels: - {{- include "v3-fail.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "v3-fail.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: http - containerPort: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/pkg/lint/rules/testdata/v3-fail/templates/ingress.yaml b/pkg/lint/rules/testdata/v3-fail/templates/ingress.yaml deleted file mode 100644 index b2e78d9..0000000 --- a/pkg/lint/rules/testdata/v3-fail/templates/ingress.yaml +++ /dev/null @@ -1,42 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "v3-fail.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "v3-fail.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - "helm.sh/hook": crd-install - {{- toYaml . | nindent 4 }} - {{- end }} -spec: -{{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} -{{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ . }} - backend: - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} -{{- end }} diff --git a/pkg/lint/rules/testdata/v3-fail/templates/service.yaml b/pkg/lint/rules/testdata/v3-fail/templates/service.yaml deleted file mode 100644 index 79a0f40..0000000 --- a/pkg/lint/rules/testdata/v3-fail/templates/service.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "v3-fail.fullname" . }} - annotations: - helm.sh/hook: crd-install - labels: - {{- include "v3-fail.labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include "v3-fail.selectorLabels" . | nindent 4 }} diff --git a/pkg/lint/rules/testdata/v3-fail/values.yaml b/pkg/lint/rules/testdata/v3-fail/values.yaml deleted file mode 100644 index 01d99b4..0000000 --- a/pkg/lint/rules/testdata/v3-fail/values.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# Default values for v3-fail. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - type: ClusterIP - port: 80 - -ingress: - enabled: false - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: [] - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -nodeSelector: {} - -tolerations: [] - -affinity: {} diff --git a/pkg/lint/rules/values.go b/pkg/lint/rules/values.go deleted file mode 100644 index 0f202f4..0000000 --- a/pkg/lint/rules/values.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "io/ioutil" - "os" - "path/filepath" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/lint/support" -) - -// Values lints a chart's values.yaml file. -func Values(linter *support.Linter) { - file := "values.yaml" - vf := filepath.Join(linter.ChartDir, file) - fileExists := linter.RunLinterRule(support.InfoSev, file, validateValuesFileExistence(vf)) - - if !fileExists { - return - } - - linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf)) -} - -func validateValuesFileExistence(valuesPath string) error { - _, err := os.Stat(valuesPath) - if err != nil { - return errors.Errorf("file does not exist") - } - return nil -} - -func validateValuesFile(valuesPath string) error { - values, err := chartutil.ReadValuesFile(valuesPath) - if err != nil { - return errors.Wrap(err, "unable to parse YAML") - } - - ext := filepath.Ext(valuesPath) - schemaPath := valuesPath[:len(valuesPath)-len(ext)] + ".schema.json" - schema, err := ioutil.ReadFile(schemaPath) - if len(schema) == 0 { - return nil - } - if err != nil { - return err - } - return chartutil.ValidateAgainstSingleSchema(values, schema) -} diff --git a/pkg/lint/support/doc.go b/pkg/lint/support/doc.go deleted file mode 100644 index b9a9d09..0000000 --- a/pkg/lint/support/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package support contains tools for linting charts. - -Linting is the process of testing charts for errors or warnings regarding -formatting, compilation, or standards compliance. -*/ -package support // import "helm.sh/helm/v3/pkg/lint/support" diff --git a/pkg/lint/support/message.go b/pkg/lint/support/message.go deleted file mode 100644 index 5efbc7a..0000000 --- a/pkg/lint/support/message.go +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package support - -import "fmt" - -// Severity indicates the severity of a Message. -const ( - // UnknownSev indicates that the severity of the error is unknown, and should not stop processing. - UnknownSev = iota - // InfoSev indicates information, for example missing values.yaml file - InfoSev - // WarningSev indicates that something does not meet code standards, but will likely function. - WarningSev - // ErrorSev indicates that something will not likely function. - ErrorSev -) - -// sev matches the *Sev states. -var sev = []string{"UNKNOWN", "INFO", "WARNING", "ERROR"} - -// Linter encapsulates a linting run of a particular chart. -type Linter struct { - Messages []Message - // The highest severity of all the failing lint rules - HighestSeverity int - ChartDir string -} - -// Message describes an error encountered while linting. -type Message struct { - // Severity is one of the *Sev constants - Severity int - Path string - Err error -} - -func (m Message) Error() string { - return fmt.Sprintf("[%s] %s: %s", sev[m.Severity], m.Path, m.Err.Error()) -} - -// NewMessage creates a new Message struct -func NewMessage(severity int, path string, err error) Message { - return Message{Severity: severity, Path: path, Err: err} -} - -// RunLinterRule returns true if the validation passed -func (l *Linter) RunLinterRule(severity int, path string, err error) bool { - // severity is out of bound - if severity < 0 || severity >= len(sev) { - return false - } - - if err != nil { - l.Messages = append(l.Messages, NewMessage(severity, path, err)) - - if severity > l.HighestSeverity { - l.HighestSeverity = severity - } - } - return err == nil -} diff --git a/pkg/lint/support/message_test.go b/pkg/lint/support/message_test.go deleted file mode 100644 index 9e12a63..0000000 --- a/pkg/lint/support/message_test.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package support - -import ( - "testing" - - "github.com/pkg/errors" -) - -var linter = Linter{} -var errLint = errors.New("lint failed") - -func TestRunLinterRule(t *testing.T) { - var tests = []struct { - Severity int - LintError error - ExpectedMessages int - ExpectedReturn bool - ExpectedHighestSeverity int - }{ - {InfoSev, errLint, 1, false, InfoSev}, - {WarningSev, errLint, 2, false, WarningSev}, - {ErrorSev, errLint, 3, false, ErrorSev}, - // No error so it returns true - {ErrorSev, nil, 3, true, ErrorSev}, - // Retains highest severity - {InfoSev, errLint, 4, false, ErrorSev}, - // Invalid severity values - {4, errLint, 4, false, ErrorSev}, - {22, errLint, 4, false, ErrorSev}, - {-1, errLint, 4, false, ErrorSev}, - } - - for _, test := range tests { - isValid := linter.RunLinterRule(test.Severity, "chart", test.LintError) - if len(linter.Messages) != test.ExpectedMessages { - t.Errorf("RunLinterRule(%d, \"chart\", %v), linter.Messages should now have %d message, we got %d", test.Severity, test.LintError, test.ExpectedMessages, len(linter.Messages)) - } - - if linter.HighestSeverity != test.ExpectedHighestSeverity { - t.Errorf("RunLinterRule(%d, \"chart\", %v), linter.HighestSeverity should be %d, we got %d", test.Severity, test.LintError, test.ExpectedHighestSeverity, linter.HighestSeverity) - } - - if isValid != test.ExpectedReturn { - t.Errorf("RunLinterRule(%d, \"chart\", %v), should have returned %t but returned %t", test.Severity, test.LintError, test.ExpectedReturn, isValid) - } - } -} - -func TestMessage(t *testing.T) { - m := Message{ErrorSev, "Chart.yaml", errors.New("Foo")} - if m.Error() != "[ERROR] Chart.yaml: Foo" { - t.Errorf("Unexpected output: %s", m.Error()) - } - - m = Message{WarningSev, "templates/", errors.New("Bar")} - if m.Error() != "[WARNING] templates/: Bar" { - t.Errorf("Unexpected output: %s", m.Error()) - } - - m = Message{InfoSev, "templates/rc.yaml", errors.New("FooBar")} - if m.Error() != "[INFO] templates/rc.yaml: FooBar" { - t.Errorf("Unexpected output: %s", m.Error()) - } -} diff --git a/pkg/plugin/cache/cache.go b/pkg/plugin/cache/cache.go deleted file mode 100644 index 5f3345b..0000000 --- a/pkg/plugin/cache/cache.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package cache provides a key generator for vcs urls. -package cache // import "helm.sh/helm/v3/pkg/plugin/cache" - -import ( - "net/url" - "regexp" - "strings" -) - -// Thanks glide! - -// scpSyntaxRe matches the SCP-like addresses used to access repos over SSH. -var scpSyntaxRe = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) - -// Key generates a cache key based on a url or scp string. The key is file -// system safe. -func Key(repo string) (string, error) { - var ( - u *url.URL - err error - ) - if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil { - // Match SCP-like syntax and convert it to a URL. - // Eg, "git@github.com:user/repo" becomes - // "ssh://git@github.com/user/repo". - u = &url.URL{ - User: url.User(m[1]), - Host: m[2], - Path: "/" + m[3], - } - } else { - u, err = url.Parse(repo) - if err != nil { - return "", err - } - } - - var key strings.Builder - if u.Scheme != "" { - key.WriteString(u.Scheme) - key.WriteString("-") - } - if u.User != nil && u.User.Username() != "" { - key.WriteString(u.User.Username()) - key.WriteString("-") - } - key.WriteString(u.Host) - if u.Path != "" { - key.WriteString(strings.ReplaceAll(u.Path, "/", "-")) - } - return strings.ReplaceAll(key.String(), ":", "-"), nil -} diff --git a/pkg/plugin/hooks.go b/pkg/plugin/hooks.go deleted file mode 100644 index e348151..0000000 --- a/pkg/plugin/hooks.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package plugin // import "helm.sh/helm/v3/pkg/plugin" - -// Types of hooks -const ( - // Install is executed after the plugin is added. - Install = "install" - // Delete is executed after the plugin is removed. - Delete = "delete" - // Update is executed after the plugin is updated. - Update = "update" -) - -// Hooks is a map of events to commands. -type Hooks map[string]string diff --git a/pkg/plugin/installer/base.go b/pkg/plugin/installer/base.go deleted file mode 100644 index a8ec974..0000000 --- a/pkg/plugin/installer/base.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package installer // import "helm.sh/helm/v3/pkg/plugin/installer" - -import ( - "os" - "path/filepath" - - "helm.sh/helm/v3/pkg/helmpath" -) - -type base struct { - // Source is the reference to a plugin - Source string -} - -func newBase(source string) base { - return base{source} -} - -// link creates a symlink from the plugin source to the base path. -func (b *base) link(from string) error { - debug("symlinking %s to %s", from, b.Path()) - return os.Symlink(from, b.Path()) -} - -// Path is where the plugin will be symlinked to. -func (b *base) Path() string { - if b.Source == "" { - return "" - } - return helmpath.DataPath("plugins", filepath.Base(b.Source)) -} diff --git a/pkg/plugin/installer/doc.go b/pkg/plugin/installer/doc.go deleted file mode 100644 index 3e3b2eb..0000000 --- a/pkg/plugin/installer/doc.go +++ /dev/null @@ -1,17 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package installer provides an interface for installing Helm plugins. -package installer // import "helm.sh/helm/v3/pkg/plugin/installer" diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go deleted file mode 100644 index ea4ac7b..0000000 --- a/pkg/plugin/installer/http_installer.go +++ /dev/null @@ -1,206 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package installer // import "helm.sh/helm/v3/pkg/plugin/installer" - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "io" - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/plugin/cache" -) - -// HTTPInstaller installs plugins from an archive served by a web server. -type HTTPInstaller struct { - CacheDir string - PluginName string - base - extractor Extractor - getter getter.Getter -} - -// TarGzExtractor extracts gzip compressed tar archives -type TarGzExtractor struct{} - -// Extractor provides an interface for extracting archives -type Extractor interface { - Extract(buffer *bytes.Buffer, targetDir string) error -} - -// Extractors contains a map of suffixes and matching implementations of extractor to return -var Extractors = map[string]Extractor{ - ".tar.gz": &TarGzExtractor{}, - ".tgz": &TarGzExtractor{}, -} - -// NewExtractor creates a new extractor matching the source file name -func NewExtractor(source string) (Extractor, error) { - for suffix, extractor := range Extractors { - if strings.HasSuffix(source, suffix) { - return extractor, nil - } - } - return nil, errors.Errorf("no extractor implemented yet for %s", source) -} - -// NewHTTPInstaller creates a new HttpInstaller. -func NewHTTPInstaller(source string) (*HTTPInstaller, error) { - - key, err := cache.Key(source) - if err != nil { - return nil, err - } - - extractor, err := NewExtractor(source) - if err != nil { - return nil, err - } - - get, err := getter.All(new(cli.EnvSettings)).ByScheme("http") - if err != nil { - return nil, err - } - - i := &HTTPInstaller{ - CacheDir: helmpath.CachePath("plugins", key), - PluginName: stripPluginName(filepath.Base(source)), - base: newBase(source), - extractor: extractor, - getter: get, - } - return i, nil -} - -// helper that relies on some sort of convention for plugin name (plugin-name-) -func stripPluginName(name string) string { - var strippedName string - for suffix := range Extractors { - if strings.HasSuffix(name, suffix) { - strippedName = strings.TrimSuffix(name, suffix) - break - } - } - re := regexp.MustCompile(`(.*)-[0-9]+\..*`) - return re.ReplaceAllString(strippedName, `$1`) -} - -// Install downloads and extracts the tarball into the cache directory -// and creates a symlink to the plugin directory. -// -// Implements Installer. -func (i *HTTPInstaller) Install() error { - - pluginData, err := i.getter.Get(i.Source) - if err != nil { - return err - } - - err = i.extractor.Extract(pluginData, i.CacheDir) - if err != nil { - return err - } - - if !isPlugin(i.CacheDir) { - return ErrMissingMetadata - } - - src, err := filepath.Abs(i.CacheDir) - if err != nil { - return err - } - - return i.link(src) -} - -// Update updates a local repository -// Not implemented for now since tarball most likely will be packaged by version -func (i *HTTPInstaller) Update() error { - return errors.Errorf("method Update() not implemented for HttpInstaller") -} - -// Override link because we want to use HttpInstaller.Path() not base.Path() -func (i *HTTPInstaller) link(from string) error { - debug("symlinking %s to %s", from, i.Path()) - return os.Symlink(from, i.Path()) -} - -// Path is overridden because we want to join on the plugin name not the file name -func (i HTTPInstaller) Path() string { - if i.base.Source == "" { - return "" - } - return helmpath.DataPath("plugins", i.PluginName) -} - -// Extract extracts compressed archives -// -// Implements Extractor. -func (g *TarGzExtractor) Extract(buffer *bytes.Buffer, targetDir string) error { - uncompressedStream, err := gzip.NewReader(buffer) - if err != nil { - return err - } - - tarReader := tar.NewReader(uncompressedStream) - - os.MkdirAll(targetDir, 0755) - - for { - header, err := tarReader.Next() - - if err == io.EOF { - break - } - - if err != nil { - return err - } - - path := filepath.Join(targetDir, header.Name) - - switch header.Typeflag { - case tar.TypeDir: - if err := os.Mkdir(path, 0755); err != nil { - return err - } - case tar.TypeReg: - outFile, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) - if err != nil { - return err - } - if _, err := io.Copy(outFile, tarReader); err != nil { - outFile.Close() - return err - } - outFile.Close() - default: - return errors.Errorf("unknown type: %b in %s", header.Typeflag, header.Name) - } - } - - return nil - -} diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go deleted file mode 100644 index cfa2e4c..0000000 --- a/pkg/plugin/installer/http_installer_test.go +++ /dev/null @@ -1,268 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package installer // import "helm.sh/helm/v3/pkg/plugin/installer" - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "encoding/base64" - "io/ioutil" - "os" - "path/filepath" - "syscall" - "testing" - - "github.com/pkg/errors" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/helmpath" -) - -var _ Installer = new(HTTPInstaller) - -// Fake http client -type TestHTTPGetter struct { - MockResponse *bytes.Buffer - MockError error -} - -func (t *TestHTTPGetter) Get(href string, _ ...getter.Option) (*bytes.Buffer, error) { - return t.MockResponse, t.MockError -} - -// Fake plugin tarball data -var fakePluginB64 = "H4sIAKRj51kAA+3UX0vCUBgGcC9jn+Iwuk3Peza3GeyiUlJQkcogCOzgli7dJm4TvYk+a5+k479UqquUCJ/fLs549sLO2TnvWnJa9aXnjwujYdYLovxMhsPcfnHOLdNkOXthM/IVQQYjg2yyLLJ4kXGhLp5j0z3P41tZksqxmspL3B/O+j/XtZu1y8rdYzkOZRCxduKPk53ny6Wwz/GfIIf1As8lxzGJSmoHNLJZphKHG4YpTCE0wVk3DULfpSJ3DMMqkj3P5JfMYLdX1Vr9Ie/5E5cstcdC8K04iGLX5HaJuKpWL17F0TCIBi5pf/0pjtLhun5j3f9v6r7wfnI/H0eNp9d1/5P6Gez0vzo7wsoxfrAZbTny/o9k6J8z/VkO/LPlWdC1iVpbEEcq5nmeJ13LEtmbV0k2r2PrOs9PuuNglC5rL1Y5S/syXRQmutaNw1BGnnp8Wq3UG51WvX1da3bKtZtCN/R09DwAAAAAAAAAAAAAAAAAAADAb30AoMczDwAoAAA=" - -func TestStripName(t *testing.T) { - if stripPluginName("fake-plugin-0.0.1.tar.gz") != "fake-plugin" { - t.Errorf("name does not match expected value") - } - if stripPluginName("fake-plugin-0.0.1.tgz") != "fake-plugin" { - t.Errorf("name does not match expected value") - } - if stripPluginName("fake-plugin.tgz") != "fake-plugin" { - t.Errorf("name does not match expected value") - } - if stripPluginName("fake-plugin.tar.gz") != "fake-plugin" { - t.Errorf("name does not match expected value") - } -} - -func TestHTTPInstaller(t *testing.T) { - defer ensure.HelmHome(t)() - source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" - - if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { - t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) - } - - i, err := NewForSource(source, "0.0.1") - if err != nil { - t.Errorf("unexpected error: %s", err) - } - - // ensure a HTTPInstaller was returned - httpInstaller, ok := i.(*HTTPInstaller) - if !ok { - t.Error("expected a HTTPInstaller") - } - - // inject fake http client responding with minimal plugin tarball - mockTgz, err := base64.StdEncoding.DecodeString(fakePluginB64) - if err != nil { - t.Fatalf("Could not decode fake tgz plugin: %s", err) - } - - httpInstaller.getter = &TestHTTPGetter{ - MockResponse: bytes.NewBuffer(mockTgz), - } - - // install the plugin - if err := Install(i); err != nil { - t.Error(err) - } - if i.Path() != helmpath.DataPath("plugins", "fake-plugin") { - t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path()) - } - - // Install again to test plugin exists error - if err := Install(i); err == nil { - t.Error("expected error for plugin exists, got none") - } else if err.Error() != "plugin already exists" { - t.Errorf("expected error for plugin exists, got (%v)", err) - } - -} - -func TestHTTPInstallerNonExistentVersion(t *testing.T) { - defer ensure.HelmHome(t)() - source := "https://repo.localdomain/plugins/fake-plugin-0.0.2.tar.gz" - - if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { - t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) - } - - i, err := NewForSource(source, "0.0.2") - if err != nil { - t.Errorf("unexpected error: %s", err) - } - - // ensure a HTTPInstaller was returned - httpInstaller, ok := i.(*HTTPInstaller) - if !ok { - t.Error("expected a HTTPInstaller") - } - - // inject fake http client responding with error - httpInstaller.getter = &TestHTTPGetter{ - MockError: errors.Errorf("failed to download plugin for some reason"), - } - - // attempt to install the plugin - if err := Install(i); err == nil { - t.Error("expected error from http client") - } - -} - -func TestHTTPInstallerUpdate(t *testing.T) { - source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" - defer ensure.HelmHome(t)() - - if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { - t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) - } - - i, err := NewForSource(source, "0.0.1") - if err != nil { - t.Errorf("unexpected error: %s", err) - } - - // ensure a HTTPInstaller was returned - httpInstaller, ok := i.(*HTTPInstaller) - if !ok { - t.Error("expected a HTTPInstaller") - } - - // inject fake http client responding with minimal plugin tarball - mockTgz, err := base64.StdEncoding.DecodeString(fakePluginB64) - if err != nil { - t.Fatalf("Could not decode fake tgz plugin: %s", err) - } - - httpInstaller.getter = &TestHTTPGetter{ - MockResponse: bytes.NewBuffer(mockTgz), - } - - // install the plugin before updating - if err := Install(i); err != nil { - t.Error(err) - } - if i.Path() != helmpath.DataPath("plugins", "fake-plugin") { - t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path()) - } - - // Update plugin, should fail because it is not implemented - if err := Update(i); err == nil { - t.Error("update method not implemented for http installer") - } -} - -func TestExtract(t *testing.T) { - source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" - - tempDir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tempDir) - - // Set the umask to default open permissions so we can actually test - oldmask := syscall.Umask(0000) - defer func() { - syscall.Umask(oldmask) - }() - - // Write a tarball to a buffer for us to extract - var tarbuf bytes.Buffer - tw := tar.NewWriter(&tarbuf) - var files = []struct { - Name, Body string - Mode int64 - }{ - {"plugin.yaml", "plugin metadata", 0600}, - {"README.md", "some text", 0777}, - } - for _, file := range files { - hdr := &tar.Header{ - Name: file.Name, - Typeflag: tar.TypeReg, - Mode: file.Mode, - Size: int64(len(file.Body)), - } - if err := tw.WriteHeader(hdr); err != nil { - t.Fatal(err) - } - if _, err := tw.Write([]byte(file.Body)); err != nil { - t.Fatal(err) - } - } - if err := tw.Close(); err != nil { - t.Fatal(err) - } - - var buf bytes.Buffer - gz := gzip.NewWriter(&buf) - if _, err := gz.Write(tarbuf.Bytes()); err != nil { - t.Fatal(err) - } - gz.Close() - // END tarball creation - - extractor, err := NewExtractor(source) - if err != nil { - t.Fatal(err) - } - - if err = extractor.Extract(&buf, tempDir); err != nil { - t.Errorf("Did not expect error but got error: %v", err) - } - - pluginYAMLFullPath := filepath.Join(tempDir, "plugin.yaml") - if info, err := os.Stat(pluginYAMLFullPath); err != nil { - if os.IsNotExist(err) { - t.Errorf("Expected %s to exist but doesn't", pluginYAMLFullPath) - } else { - t.Error(err) - } - } else if info.Mode().Perm() != 0600 { - t.Errorf("Expected %s to have 0600 mode it but has %o", pluginYAMLFullPath, info.Mode().Perm()) - } - - readmeFullPath := filepath.Join(tempDir, "README.md") - if info, err := os.Stat(readmeFullPath); err != nil { - if os.IsNotExist(err) { - t.Errorf("Expected %s to exist but doesn't", readmeFullPath) - } else { - t.Error(err) - } - } else if info.Mode().Perm() != 0777 { - t.Errorf("Expected %s to have 0777 mode it but has %o", readmeFullPath, info.Mode().Perm()) - } - -} diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go deleted file mode 100644 index 14a02a8..0000000 --- a/pkg/plugin/installer/installer.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package installer - -import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" -) - -// ErrMissingMetadata indicates that plugin.yaml is missing. -var ErrMissingMetadata = errors.New("plugin metadata (plugin.yaml) missing") - -// Debug enables verbose output. -var Debug bool - -// Installer provides an interface for installing helm client plugins. -type Installer interface { - // Install adds a plugin. - Install() error - // Path is the directory of the installed plugin. - Path() string - // Update updates a plugin. - Update() error -} - -// Install installs a plugin. -func Install(i Installer) error { - if err := os.MkdirAll(filepath.Dir(i.Path()), 0755); err != nil { - return err - } - if _, pathErr := os.Stat(i.Path()); !os.IsNotExist(pathErr) { - return errors.New("plugin already exists") - } - return i.Install() -} - -// Update updates a plugin. -func Update(i Installer) error { - if _, pathErr := os.Stat(i.Path()); os.IsNotExist(pathErr) { - return errors.New("plugin does not exist") - } - return i.Update() -} - -// NewForSource determines the correct Installer for the given source. -func NewForSource(source, version string) (Installer, error) { - // Check if source is a local directory - if isLocalReference(source) { - return NewLocalInstaller(source) - } else if isRemoteHTTPArchive(source) { - return NewHTTPInstaller(source) - } - return NewVCSInstaller(source, version) -} - -// FindSource determines the correct Installer for the given source. -func FindSource(location string) (Installer, error) { - installer, err := existingVCSRepo(location) - if err != nil && err.Error() == "Cannot detect VCS" { - return installer, errors.New("cannot get information about plugin source") - } - return installer, err -} - -// isLocalReference checks if the source exists on the filesystem. -func isLocalReference(source string) bool { - _, err := os.Stat(source) - return err == nil -} - -// isRemoteHTTPArchive checks if the source is a http/https url and is an archive -func isRemoteHTTPArchive(source string) bool { - if strings.HasPrefix(source, "http://") || strings.HasPrefix(source, "https://") { - for suffix := range Extractors { - if strings.HasSuffix(source, suffix) { - return true - } - } - } - return false -} - -// isPlugin checks if the directory contains a plugin.yaml file. -func isPlugin(dirname string) bool { - _, err := os.Stat(filepath.Join(dirname, "plugin.yaml")) - return err == nil -} - -func debug(format string, args ...interface{}) { - if Debug { - format = fmt.Sprintf("[debug] %s\n", format) - fmt.Printf(format, args...) - } -} diff --git a/pkg/plugin/installer/local_installer.go b/pkg/plugin/installer/local_installer.go deleted file mode 100644 index 662ef5b..0000000 --- a/pkg/plugin/installer/local_installer.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package installer // import "helm.sh/helm/v3/pkg/plugin/installer" - -import ( - "path/filepath" - - "github.com/pkg/errors" -) - -// LocalInstaller installs plugins from the filesystem. -type LocalInstaller struct { - base -} - -// NewLocalInstaller creates a new LocalInstaller. -func NewLocalInstaller(source string) (*LocalInstaller, error) { - src, err := filepath.Abs(source) - if err != nil { - return nil, errors.Wrap(err, "unable to get absolute path to plugin") - } - i := &LocalInstaller{ - base: newBase(src), - } - return i, nil -} - -// Install creates a symlink to the plugin directory. -// -// Implements Installer. -func (i *LocalInstaller) Install() error { - if !isPlugin(i.Source) { - return ErrMissingMetadata - } - return i.link(i.Source) -} - -// Update updates a local repository -func (i *LocalInstaller) Update() error { - debug("local repository is auto-updated") - return nil -} diff --git a/pkg/plugin/installer/local_installer_test.go b/pkg/plugin/installer/local_installer_test.go deleted file mode 100644 index d11e448..0000000 --- a/pkg/plugin/installer/local_installer_test.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package installer // import "helm.sh/helm/v3/pkg/plugin/installer" - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "helm.sh/helm/v3/pkg/helmpath" -) - -var _ Installer = new(LocalInstaller) - -func TestLocalInstaller(t *testing.T) { - // Make a temp dir - tdir, err := ioutil.TempDir("", "helm-installer-") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tdir) - if err := ioutil.WriteFile(filepath.Join(tdir, "plugin.yaml"), []byte{}, 0644); err != nil { - t.Fatal(err) - } - - source := "../testdata/plugdir/echo" - i, err := NewForSource(source, "") - if err != nil { - t.Errorf("unexpected error: %s", err) - } - - if err := Install(i); err != nil { - t.Fatal(err) - } - - if i.Path() != helmpath.DataPath("plugins", "echo") { - t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path()) - } -} diff --git a/pkg/plugin/installer/vcs_installer.go b/pkg/plugin/installer/vcs_installer.go deleted file mode 100644 index 1a5d74c..0000000 --- a/pkg/plugin/installer/vcs_installer.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package installer // import "helm.sh/helm/v3/pkg/plugin/installer" - -import ( - "os" - "sort" - - "github.com/Masterminds/semver/v3" - "github.com/Masterminds/vcs" - "github.com/pkg/errors" - - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/plugin/cache" -) - -// VCSInstaller installs plugins from remote a repository. -type VCSInstaller struct { - Repo vcs.Repo - Version string - base -} - -func existingVCSRepo(location string) (Installer, error) { - repo, err := vcs.NewRepo("", location) - if err != nil { - return nil, err - } - i := &VCSInstaller{ - Repo: repo, - base: newBase(repo.Remote()), - } - return i, err -} - -// NewVCSInstaller creates a new VCSInstaller. -func NewVCSInstaller(source, version string) (*VCSInstaller, error) { - key, err := cache.Key(source) - if err != nil { - return nil, err - } - cachedpath := helmpath.CachePath("plugins", key) - repo, err := vcs.NewRepo(source, cachedpath) - if err != nil { - return nil, err - } - i := &VCSInstaller{ - Repo: repo, - Version: version, - base: newBase(source), - } - return i, err -} - -// Install clones a remote repository and creates a symlink to the plugin directory. -// -// Implements Installer. -func (i *VCSInstaller) Install() error { - if err := i.sync(i.Repo); err != nil { - return err - } - - ref, err := i.solveVersion(i.Repo) - if err != nil { - return err - } - if ref != "" { - if err := i.setVersion(i.Repo, ref); err != nil { - return err - } - } - - if !isPlugin(i.Repo.LocalPath()) { - return ErrMissingMetadata - } - - return i.link(i.Repo.LocalPath()) -} - -// Update updates a remote repository -func (i *VCSInstaller) Update() error { - debug("updating %s", i.Repo.Remote()) - if i.Repo.IsDirty() { - return errors.New("plugin repo was modified") - } - if err := i.Repo.Update(); err != nil { - return err - } - if !isPlugin(i.Repo.LocalPath()) { - return ErrMissingMetadata - } - return nil -} - -func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) { - if i.Version == "" { - return "", nil - } - - if repo.IsReference(i.Version) { - return i.Version, nil - } - - // Create the constraint first to make sure it's valid before - // working on the repo. - constraint, err := semver.NewConstraint(i.Version) - if err != nil { - return "", err - } - - // Get the tags - refs, err := repo.Tags() - if err != nil { - return "", err - } - debug("found refs: %s", refs) - - // Convert and filter the list to semver.Version instances - semvers := getSemVers(refs) - - // Sort semver list - sort.Sort(sort.Reverse(semver.Collection(semvers))) - for _, v := range semvers { - if constraint.Check(v) { - // If the constraint passes get the original reference - ver := v.Original() - debug("setting to %s", ver) - return ver, nil - } - } - - return "", errors.Errorf("requested version %q does not exist for plugin %q", i.Version, i.Repo.Remote()) -} - -// setVersion attempts to checkout the version -func (i *VCSInstaller) setVersion(repo vcs.Repo, ref string) error { - debug("setting version to %q", i.Version) - return repo.UpdateVersion(ref) -} - -// sync will clone or update a remote repo. -func (i *VCSInstaller) sync(repo vcs.Repo) error { - if _, err := os.Stat(repo.LocalPath()); os.IsNotExist(err) { - debug("cloning %s to %s", repo.Remote(), repo.LocalPath()) - return repo.Get() - } - debug("updating %s", repo.Remote()) - return repo.Update() -} - -// Filter a list of versions to only included semantic versions. The response -// is a mapping of the original version to the semantic version. -func getSemVers(refs []string) []*semver.Version { - var sv []*semver.Version - for _, r := range refs { - if v, err := semver.NewVersion(r); err == nil { - sv = append(sv, v) - } - } - return sv -} diff --git a/pkg/plugin/installer/vcs_installer_test.go b/pkg/plugin/installer/vcs_installer_test.go deleted file mode 100644 index ce1ee60..0000000 --- a/pkg/plugin/installer/vcs_installer_test.go +++ /dev/null @@ -1,179 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package installer // import "helm.sh/helm/v3/pkg/plugin/installer" - -import ( - "fmt" - "os" - "path/filepath" - "testing" - - "github.com/Masterminds/vcs" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/helmpath" -) - -var _ Installer = new(VCSInstaller) - -type testRepo struct { - local, remote, current string - tags, branches []string - err error - vcs.Repo -} - -func (r *testRepo) LocalPath() string { return r.local } -func (r *testRepo) Remote() string { return r.remote } -func (r *testRepo) Update() error { return r.err } -func (r *testRepo) Get() error { return r.err } -func (r *testRepo) IsReference(string) bool { return false } -func (r *testRepo) Tags() ([]string, error) { return r.tags, r.err } -func (r *testRepo) Branches() ([]string, error) { return r.branches, r.err } -func (r *testRepo) UpdateVersion(version string) error { - r.current = version - return r.err -} - -func TestVCSInstaller(t *testing.T) { - defer ensure.HelmHome(t)() - - if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { - t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) - } - - source := "https://github.com/adamreese/helm-env" - testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo") - repo := &testRepo{ - local: testRepoPath, - tags: []string{"0.1.0", "0.1.1"}, - } - - i, err := NewForSource(source, "~0.1.0") - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - // ensure a VCSInstaller was returned - vcsInstaller, ok := i.(*VCSInstaller) - if !ok { - t.Fatal("expected a VCSInstaller") - } - - // set the testRepo in the VCSInstaller - vcsInstaller.Repo = repo - - if err := Install(i); err != nil { - t.Fatal(err) - } - if repo.current != "0.1.1" { - t.Errorf("expected version '0.1.1', got %q", repo.current) - } - if i.Path() != helmpath.DataPath("plugins", "helm-env") { - t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path()) - } - - // Install again to test plugin exists error - if err := Install(i); err == nil { - t.Error("expected error for plugin exists, got none") - } else if err.Error() != "plugin already exists" { - t.Errorf("expected error for plugin exists, got (%v)", err) - } - - // Testing FindSource method, expect error because plugin code is not a cloned repository - if _, err := FindSource(i.Path()); err == nil { - t.Error("expected error for inability to find plugin source, got none") - } else if err.Error() != "cannot get information about plugin source" { - t.Errorf("expected error for inability to find plugin source, got (%v)", err) - } -} - -func TestVCSInstallerNonExistentVersion(t *testing.T) { - defer ensure.HelmHome(t)() - - source := "https://github.com/adamreese/helm-env" - version := "0.2.0" - - i, err := NewForSource(source, version) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - // ensure a VCSInstaller was returned - _, ok := i.(*VCSInstaller) - if !ok { - t.Fatal("expected a VCSInstaller") - } - - if err := Install(i); err == nil { - t.Error("expected error for version does not exists, got none") - } else if err.Error() != fmt.Sprintf("requested version %q does not exist for plugin %q", version, source) { - t.Errorf("expected error for version does not exists, got (%v)", err) - } -} -func TestVCSInstallerUpdate(t *testing.T) { - defer ensure.HelmHome(t)() - - source := "https://github.com/adamreese/helm-env" - - i, err := NewForSource(source, "") - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - // ensure a VCSInstaller was returned - _, ok := i.(*VCSInstaller) - if !ok { - t.Fatal("expected a VCSInstaller") - } - - if err := Update(i); err == nil { - t.Fatal("expected error for plugin does not exist, got none") - } else if err.Error() != "plugin does not exist" { - t.Fatalf("expected error for plugin does not exist, got (%v)", err) - } - - // Install plugin before update - if err := Install(i); err != nil { - t.Fatal(err) - } - - // Test FindSource method for positive result - pluginInfo, err := FindSource(i.Path()) - if err != nil { - t.Fatal(err) - } - - repoRemote := pluginInfo.(*VCSInstaller).Repo.Remote() - if repoRemote != source { - t.Fatalf("invalid source found, expected %q got %q", source, repoRemote) - } - - // Update plugin - if err := Update(i); err != nil { - t.Fatal(err) - } - - // Test update failure - os.Remove(filepath.Join(i.Path(), "plugin.yaml")) - // Testing update for error - if err := Update(i); err == nil { - t.Error("expected error for plugin modified, got none") - } else if err.Error() != "plugin repo was modified" { - t.Errorf("expected error for plugin modified, got (%v)", err) - } - -} diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go deleted file mode 100644 index 2eb354f..0000000 --- a/pkg/plugin/plugin.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package plugin // import "helm.sh/helm/v3/pkg/plugin" - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "runtime" - "strings" - - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/cli" -) - -const pluginFileName = "plugin.yaml" - -// Downloaders represents the plugins capability if it can retrieve -// charts from special sources -type Downloaders struct { - // Protocols are the list of schemes from the charts URL. - Protocols []string `json:"protocols"` - // Command is the executable path with which the plugin performs - // the actual download for the corresponding Protocols - Command string `json:"command"` -} - -// PlatformCommand represents a command for a particular operating system and architecture -type PlatformCommand struct { - OperatingSystem string `json:"os"` - Architecture string `json:"arch"` - Command string `json:"command"` -} - -// Metadata describes a plugin. -// -// This is the plugin equivalent of a chart.Metadata. -type Metadata struct { - // Name is the name of the plugin - Name string `json:"name"` - - // Version is a SemVer 2 version of the plugin. - Version string `json:"version"` - - // Usage is the single-line usage text shown in help - Usage string `json:"usage"` - - // Description is a long description shown in places like `helm help` - Description string `json:"description"` - - // Command is the command, as a single string. - // - // The command will be passed through environment expansion, so env vars can - // be present in this command. Unless IgnoreFlags is set, this will - // also merge the flags passed from Helm. - // - // Note that command is not executed in a shell. To do so, we suggest - // pointing the command to a shell script. - // - // The following rules will apply to processing commands: - // - If platformCommand is present, it will be searched first - // - If both OS and Arch match the current platform, search will stop and the command will be executed - // - If OS matches and there is no more specific match, the command will be executed - // - If no OS/Arch match is found, the default command will be executed - // - If no command is present and no matches are found in platformCommand, Helm will exit with an error - PlatformCommand []PlatformCommand `json:"platformCommand"` - Command string `json:"command"` - - // IgnoreFlags ignores any flags passed in from Helm - // - // For example, if the plugin is invoked as `helm --debug myplugin`, if this - // is false, `--debug` will be appended to `--command`. If this is true, - // the `--debug` flag will be discarded. - IgnoreFlags bool `json:"ignoreFlags"` - - // Hooks are commands that will run on events. - Hooks Hooks - - // Downloaders field is used if the plugin supply downloader mechanism - // for special protocols. - Downloaders []Downloaders `json:"downloaders"` -} - -// Plugin represents a plugin. -type Plugin struct { - // Metadata is a parsed representation of a plugin.yaml - Metadata *Metadata - // Dir is the string path to the directory that holds the plugin. - Dir string -} - -// The following rules will apply to processing the Plugin.PlatformCommand.Command: -// - If both OS and Arch match the current platform, search will stop and the command will be prepared for execution -// - If OS matches and there is no more specific match, the command will be prepared for execution -// - If no OS/Arch match is found, return nil -func getPlatformCommand(cmds []PlatformCommand) []string { - var command []string - eq := strings.EqualFold - for _, c := range cmds { - if eq(c.OperatingSystem, runtime.GOOS) { - command = strings.Split(os.ExpandEnv(c.Command), " ") - } - if eq(c.OperatingSystem, runtime.GOOS) && eq(c.Architecture, runtime.GOARCH) { - return strings.Split(os.ExpandEnv(c.Command), " ") - } - } - return command -} - -// PrepareCommand takes a Plugin.PlatformCommand.Command, a Plugin.Command and will applying the following processing: -// - If platformCommand is present, it will be searched first -// - If both OS and Arch match the current platform, search will stop and the command will be prepared for execution -// - If OS matches and there is no more specific match, the command will be prepared for execution -// - If no OS/Arch match is found, the default command will be prepared for execution -// - If no command is present and no matches are found in platformCommand, will exit with an error -// -// It merges extraArgs into any arguments supplied in the plugin. It -// returns the name of the command and an args array. -// -// The result is suitable to pass to exec.Command. -func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string, error) { - var parts []string - platCmdLen := len(p.Metadata.PlatformCommand) - if platCmdLen > 0 { - parts = getPlatformCommand(p.Metadata.PlatformCommand) - } - if platCmdLen == 0 || parts == nil { - parts = strings.Split(os.ExpandEnv(p.Metadata.Command), " ") - } - if len(parts) == 0 || parts[0] == "" { - return "", nil, fmt.Errorf("No plugin command is applicable") - } - - main := parts[0] - baseArgs := []string{} - if len(parts) > 1 { - baseArgs = parts[1:] - } - if !p.Metadata.IgnoreFlags { - baseArgs = append(baseArgs, extraArgs...) - } - return main, baseArgs, nil -} - -// LoadDir loads a plugin from the given directory. -func LoadDir(dirname string) (*Plugin, error) { - data, err := ioutil.ReadFile(filepath.Join(dirname, pluginFileName)) - if err != nil { - return nil, err - } - - plug := &Plugin{Dir: dirname} - if err := yaml.Unmarshal(data, &plug.Metadata); err != nil { - return nil, err - } - return plug, nil -} - -// LoadAll loads all plugins found beneath the base directory. -// -// This scans only one directory level. -func LoadAll(basedir string) ([]*Plugin, error) { - plugins := []*Plugin{} - // We want basedir/*/plugin.yaml - scanpath := filepath.Join(basedir, "*", pluginFileName) - matches, err := filepath.Glob(scanpath) - if err != nil { - return plugins, err - } - - if matches == nil { - return plugins, nil - } - - for _, yaml := range matches { - dir := filepath.Dir(yaml) - p, err := LoadDir(dir) - if err != nil { - return plugins, err - } - plugins = append(plugins, p) - } - return plugins, nil -} - -// FindPlugins returns a list of YAML files that describe plugins. -func FindPlugins(plugdirs string) ([]*Plugin, error) { - found := []*Plugin{} - // Let's get all UNIXy and allow path separators - for _, p := range filepath.SplitList(plugdirs) { - matches, err := LoadAll(p) - if err != nil { - return matches, err - } - found = append(found, matches...) - } - return found, nil -} - -// SetupPluginEnv prepares os.Env for plugins. It operates on os.Env because -// the plugin subsystem itself needs access to the environment variables -// created here. -func SetupPluginEnv(settings *cli.EnvSettings, name, base string) { - env := settings.EnvVars() - env["HELM_PLUGIN_NAME"] = name - env["HELM_PLUGIN_DIR"] = base - for key, val := range env { - os.Setenv(key, val) - } -} diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go deleted file mode 100644 index c869e4c..0000000 --- a/pkg/plugin/plugin_test.go +++ /dev/null @@ -1,286 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package plugin // import "helm.sh/helm/v3/pkg/plugin" - -import ( - "os" - "path/filepath" - "reflect" - "runtime" - "testing" - - "helm.sh/helm/v3/pkg/cli" -) - -func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) { - cmd, args, err := p.PrepareCommand(extraArgs) - if err != nil { - t.Errorf(err.Error()) - } - if cmd != "echo" { - t.Errorf("Expected echo, got %q", cmd) - } - - if l := len(args); l != 5 { - t.Errorf("expected 5 args, got %d", l) - } - - expect := []string{"-n", osStrCmp, "--debug", "--foo", "bar"} - for i := 0; i < len(args); i++ { - if expect[i] != args[i] { - t.Errorf("Expected arg=%q, got %q", expect[i], args[i]) - } - } - - // Test with IgnoreFlags. This should omit --debug, --foo, bar - p.Metadata.IgnoreFlags = true - cmd, args, err = p.PrepareCommand(extraArgs) - if err != nil { - t.Errorf(err.Error()) - } - if cmd != "echo" { - t.Errorf("Expected echo, got %q", cmd) - } - if l := len(args); l != 2 { - t.Errorf("expected 2 args, got %d", l) - } - expect = []string{"-n", osStrCmp} - for i := 0; i < len(args); i++ { - if expect[i] != args[i] { - t.Errorf("Expected arg=%q, got %q", expect[i], args[i]) - } - } -} - -func TestPrepareCommand(t *testing.T) { - p := &Plugin{ - Dir: "/tmp", // Unused - Metadata: &Metadata{ - Name: "test", - Command: "echo -n foo", - }, - } - argv := []string{"--debug", "--foo", "bar"} - - checkCommand(p, argv, "foo", t) -} - -func TestPlatformPrepareCommand(t *testing.T) { - p := &Plugin{ - Dir: "/tmp", // Unused - Metadata: &Metadata{ - Name: "test", - Command: "echo -n os-arch", - PlatformCommand: []PlatformCommand{ - {OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"}, - {OperatingSystem: "linux", Architecture: "amd64", Command: "echo -n linux-amd64"}, - {OperatingSystem: "linux", Architecture: "arm64", Command: "echo -n linux-arm64"}, - {OperatingSystem: "linux", Architecture: "ppc64le", Command: "echo -n linux-ppc64le"}, - {OperatingSystem: "linux", Architecture: "s390x", Command: "echo -n linux-s390x"}, - {OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"}, - }, - }, - } - var osStrCmp string - os := runtime.GOOS - arch := runtime.GOARCH - if os == "linux" && arch == "i386" { - osStrCmp = "linux-i386" - } else if os == "linux" && arch == "amd64" { - osStrCmp = "linux-amd64" - } else if os == "linux" && arch == "arm64" { - osStrCmp = "linux-arm64" - } else if os == "linux" && arch == "ppc64le" { - osStrCmp = "linux-ppc64le" - } else if os == "linux" && arch == "s390x" { - osStrCmp = "linux-s390x" - } else if os == "windows" && arch == "amd64" { - osStrCmp = "win-64" - } else { - osStrCmp = "os-arch" - } - - argv := []string{"--debug", "--foo", "bar"} - checkCommand(p, argv, osStrCmp, t) -} - -func TestPartialPlatformPrepareCommand(t *testing.T) { - p := &Plugin{ - Dir: "/tmp", // Unused - Metadata: &Metadata{ - Name: "test", - Command: "echo -n os-arch", - PlatformCommand: []PlatformCommand{ - {OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"}, - {OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"}, - }, - }, - } - var osStrCmp string - os := runtime.GOOS - arch := runtime.GOARCH - if os == "linux" { - osStrCmp = "linux-i386" - } else if os == "windows" && arch == "amd64" { - osStrCmp = "win-64" - } else { - osStrCmp = "os-arch" - } - - argv := []string{"--debug", "--foo", "bar"} - checkCommand(p, argv, osStrCmp, t) -} - -func TestNoPrepareCommand(t *testing.T) { - p := &Plugin{ - Dir: "/tmp", // Unused - Metadata: &Metadata{ - Name: "test", - }, - } - argv := []string{"--debug", "--foo", "bar"} - - _, _, err := p.PrepareCommand(argv) - if err == nil { - t.Errorf("Expected error to be returned") - } -} - -func TestNoMatchPrepareCommand(t *testing.T) { - p := &Plugin{ - Dir: "/tmp", // Unused - Metadata: &Metadata{ - Name: "test", - PlatformCommand: []PlatformCommand{ - {OperatingSystem: "no-os", Architecture: "amd64", Command: "echo -n linux-i386"}, - }, - }, - } - argv := []string{"--debug", "--foo", "bar"} - - if _, _, err := p.PrepareCommand(argv); err == nil { - t.Errorf("Expected error to be returned") - } -} - -func TestLoadDir(t *testing.T) { - dirname := "testdata/plugdir/hello" - plug, err := LoadDir(dirname) - if err != nil { - t.Fatalf("error loading Hello plugin: %s", err) - } - - if plug.Dir != dirname { - t.Errorf("Expected dir %q, got %q", dirname, plug.Dir) - } - - expect := &Metadata{ - Name: "hello", - Version: "0.1.0", - Usage: "usage", - Description: "description", - Command: "$HELM_PLUGIN_SELF/hello.sh", - IgnoreFlags: true, - Hooks: map[string]string{ - Install: "echo installing...", - }, - } - - if !reflect.DeepEqual(expect, plug.Metadata) { - t.Errorf("Expected plugin metadata %v, got %v", expect, plug.Metadata) - } -} - -func TestDownloader(t *testing.T) { - dirname := "testdata/plugdir/downloader" - plug, err := LoadDir(dirname) - if err != nil { - t.Fatalf("error loading Hello plugin: %s", err) - } - - if plug.Dir != dirname { - t.Errorf("Expected dir %q, got %q", dirname, plug.Dir) - } - - expect := &Metadata{ - Name: "downloader", - Version: "1.2.3", - Usage: "usage", - Description: "download something", - Command: "echo Hello", - Downloaders: []Downloaders{ - { - Protocols: []string{"myprotocol", "myprotocols"}, - Command: "echo Download", - }, - }, - } - - if !reflect.DeepEqual(expect, plug.Metadata) { - t.Errorf("Expected metadata %v, got %v", expect, plug.Metadata) - } -} - -func TestLoadAll(t *testing.T) { - - // Verify that empty dir loads: - if plugs, err := LoadAll("testdata"); err != nil { - t.Fatalf("error loading dir with no plugins: %s", err) - } else if len(plugs) > 0 { - t.Fatalf("expected empty dir to have 0 plugins") - } - - basedir := "testdata/plugdir" - plugs, err := LoadAll(basedir) - if err != nil { - t.Fatalf("Could not load %q: %s", basedir, err) - } - - if l := len(plugs); l != 3 { - t.Fatalf("expected 3 plugins, found %d", l) - } - - if plugs[0].Metadata.Name != "downloader" { - t.Errorf("Expected first plugin to be echo, got %q", plugs[0].Metadata.Name) - } - if plugs[1].Metadata.Name != "echo" { - t.Errorf("Expected first plugin to be echo, got %q", plugs[0].Metadata.Name) - } - if plugs[2].Metadata.Name != "hello" { - t.Errorf("Expected second plugin to be hello, got %q", plugs[1].Metadata.Name) - } -} - -func TestSetupEnv(t *testing.T) { - name := "pequod" - base := filepath.Join("testdata/helmhome/helm/plugins", name) - - s := &cli.EnvSettings{ - PluginsDirectory: "testdata/helmhome/helm/plugins", - } - - SetupPluginEnv(s, name, base) - for _, tt := range []struct { - name, expect string - }{ - {"HELM_PLUGIN_NAME", name}, - {"HELM_PLUGIN_DIR", base}, - } { - if got := os.Getenv(tt.name); got != tt.expect { - t.Errorf("Expected $%s=%q, got %q", tt.name, tt.expect, got) - } - } -} diff --git a/pkg/plugin/testdata/plugdir/downloader/plugin.yaml b/pkg/plugin/testdata/plugdir/downloader/plugin.yaml deleted file mode 100644 index c0b9037..0000000 --- a/pkg/plugin/testdata/plugdir/downloader/plugin.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: "downloader" -version: "1.2.3" -usage: "usage" -description: |- - download something -command: "echo Hello" -downloaders: - - protocols: - - "myprotocol" - - "myprotocols" - command: "echo Download" diff --git a/pkg/plugin/testdata/plugdir/echo/plugin.yaml b/pkg/plugin/testdata/plugdir/echo/plugin.yaml deleted file mode 100644 index 8baa35b..0000000 --- a/pkg/plugin/testdata/plugdir/echo/plugin.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: "echo" -version: "1.2.3" -usage: "echo something" -description: |- - This is a testing fixture. -command: "echo Hello" -hooks: - install: "echo Installing" diff --git a/pkg/plugin/testdata/plugdir/hello/hello.sh b/pkg/plugin/testdata/plugdir/hello/hello.sh deleted file mode 100755 index dcfd588..0000000 --- a/pkg/plugin/testdata/plugdir/hello/hello.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -echo "Hello from a Helm plugin" - -echo "PARAMS" -echo $* - -$HELM_BIN ls --all - diff --git a/pkg/plugin/testdata/plugdir/hello/plugin.yaml b/pkg/plugin/testdata/plugdir/hello/plugin.yaml deleted file mode 100644 index 6a78756..0000000 --- a/pkg/plugin/testdata/plugdir/hello/plugin.yaml +++ /dev/null @@ -1,10 +0,0 @@ -name: "hello" -version: "0.1.0" -usage: "usage" -description: |- - description -command: "$HELM_PLUGIN_SELF/hello.sh" -ignoreFlags: true -install: "echo installing..." -hooks: - install: "echo installing..." diff --git a/pkg/provenance/doc.go b/pkg/provenance/doc.go deleted file mode 100644 index 3d2d0ea..0000000 --- a/pkg/provenance/doc.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package provenance provides tools for establishing the authenticity of a chart. - -In Helm, provenance is established via several factors. The primary factor is the -cryptographic signature of a chart. Chart authors may sign charts, which in turn -provide the necessary metadata to ensure the integrity of the chart file, the -Chart.yaml, and the referenced Docker images. - -A provenance file is clear-signed. This provides cryptographic verification that -a particular block of information (Chart.yaml, archive file, images) have not -been tampered with or altered. To learn more, read the GnuPG documentation on -clear signatures: -https://www.gnupg.org/gph/en/manual/x135.html - -The cryptography used by Helm should be compatible with OpenGPG. For example, -you should be able to verify a signature by importing the desired public key -and using `gpg --verify`, `keybase pgp verify`, or similar: - - $ gpg --verify some.sig - gpg: Signature made Mon Jul 25 17:23:44 2016 MDT using RSA key ID 1FC18762 - gpg: Good signature from "Helm Testing (This key should only be used for testing. DO NOT TRUST.) " [ultimate] -*/ -package provenance // import "helm.sh/helm/v3/pkg/provenance" diff --git a/pkg/provenance/sign.go b/pkg/provenance/sign.go deleted file mode 100644 index 5d16779..0000000 --- a/pkg/provenance/sign.go +++ /dev/null @@ -1,409 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package provenance - -import ( - "bytes" - "crypto" - "encoding/hex" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "golang.org/x/crypto/openpgp" - "golang.org/x/crypto/openpgp/clearsign" - "golang.org/x/crypto/openpgp/packet" - "sigs.k8s.io/yaml" - - hapi "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" -) - -var defaultPGPConfig = packet.Config{ - DefaultHash: crypto.SHA512, -} - -// SumCollection represents a collection of file and image checksums. -// -// Files are of the form: -// FILENAME: "sha256:SUM" -// Images are of the form: -// "IMAGE:TAG": "sha256:SUM" -// Docker optionally supports sha512, and if this is the case, the hash marker -// will be 'sha512' instead of 'sha256'. -type SumCollection struct { - Files map[string]string `json:"files"` - Images map[string]string `json:"images,omitempty"` -} - -// Verification contains information about a verification operation. -type Verification struct { - // SignedBy contains the entity that signed a chart. - SignedBy *openpgp.Entity - // FileHash is the hash, prepended with the scheme, for the file that was verified. - FileHash string - // FileName is the name of the file that FileHash verifies. - FileName string -} - -// Signatory signs things. -// -// Signatories can be constructed from a PGP private key file using NewFromFiles -// or they can be constructed manually by setting the Entity to a valid -// PGP entity. -// -// The same Signatory can be used to sign or validate multiple charts. -type Signatory struct { - // The signatory for this instance of Helm. This is used for signing. - Entity *openpgp.Entity - // The keyring for this instance of Helm. This is used for verification. - KeyRing openpgp.EntityList -} - -// NewFromFiles constructs a new Signatory from the PGP key in the given filename. -// -// This will emit an error if it cannot find a valid GPG keyfile (entity) at the -// given location. -// -// Note that the keyfile may have just a public key, just a private key, or -// both. The Signatory methods may have different requirements of the keys. For -// example, ClearSign must have a valid `openpgp.Entity.PrivateKey` before it -// can sign something. -func NewFromFiles(keyfile, keyringfile string) (*Signatory, error) { - e, err := loadKey(keyfile) - if err != nil { - return nil, err - } - - ring, err := loadKeyRing(keyringfile) - if err != nil { - return nil, err - } - - return &Signatory{ - Entity: e, - KeyRing: ring, - }, nil -} - -// NewFromKeyring reads a keyring file and creates a Signatory. -// -// If id is not the empty string, this will also try to find an Entity in the -// keyring whose name matches, and set that as the signing entity. It will return -// an error if the id is not empty and also not found. -func NewFromKeyring(keyringfile, id string) (*Signatory, error) { - ring, err := loadKeyRing(keyringfile) - if err != nil { - return nil, err - } - - s := &Signatory{KeyRing: ring} - - // If the ID is empty, we can return now. - if id == "" { - return s, nil - } - - // We're gonna go all GnuPG on this and look for a string that _contains_. If - // two or more keys contain the string and none are a direct match, we error - // out. - var candidate *openpgp.Entity - vague := false - for _, e := range ring { - for n := range e.Identities { - if n == id { - s.Entity = e - return s, nil - } - if strings.Contains(n, id) { - if candidate != nil { - vague = true - } - candidate = e - } - } - } - if vague { - return s, errors.Errorf("more than one key contain the id %q", id) - } - - s.Entity = candidate - return s, nil -} - -// PassphraseFetcher returns a passphrase for decrypting keys. -// -// This is used as a callback to read a passphrase from some other location. The -// given name is the Name field on the key, typically of the form: -// -// USER_NAME (COMMENT) -type PassphraseFetcher func(name string) ([]byte, error) - -// DecryptKey decrypts a private key in the Signatory. -// -// If the key is not encrypted, this will return without error. -// -// If the key does not exist, this will return an error. -// -// If the key exists, but cannot be unlocked with the passphrase returned by -// the PassphraseFetcher, this will return an error. -// -// If the key is successfully unlocked, it will return nil. -func (s *Signatory) DecryptKey(fn PassphraseFetcher) error { - if s.Entity == nil { - return errors.New("private key not found") - } else if s.Entity.PrivateKey == nil { - return errors.New("provided key is not a private key. Try providing a keyring with secret keys") - } - - // Nothing else to do if key is not encrypted. - if !s.Entity.PrivateKey.Encrypted { - return nil - } - - fname := "Unknown" - for i := range s.Entity.Identities { - if i != "" { - fname = i - break - } - } - - p, err := fn(fname) - if err != nil { - return err - } - - return s.Entity.PrivateKey.Decrypt(p) -} - -// ClearSign signs a chart with the given key. -// -// This takes the path to a chart archive file and a key, and it returns a clear signature. -// -// The Signatory must have a valid Entity.PrivateKey for this to work. If it does -// not, an error will be returned. -func (s *Signatory) ClearSign(chartpath string) (string, error) { - if s.Entity == nil { - return "", errors.New("private key not found") - } else if s.Entity.PrivateKey == nil { - return "", errors.New("provided key is not a private key. Try providing a keyring with secret keys") - } - - if fi, err := os.Stat(chartpath); err != nil { - return "", err - } else if fi.IsDir() { - return "", errors.New("cannot sign a directory") - } - - out := bytes.NewBuffer(nil) - - b, err := messageBlock(chartpath) - if err != nil { - return "", nil - } - - // Sign the buffer - w, err := clearsign.Encode(out, s.Entity.PrivateKey, &defaultPGPConfig) - if err != nil { - return "", err - } - _, err = io.Copy(w, b) - w.Close() - return out.String(), err -} - -// Verify checks a signature and verifies that it is legit for a chart. -func (s *Signatory) Verify(chartpath, sigpath string) (*Verification, error) { - ver := &Verification{} - for _, fname := range []string{chartpath, sigpath} { - if fi, err := os.Stat(fname); err != nil { - return ver, err - } else if fi.IsDir() { - return ver, errors.Errorf("%s cannot be a directory", fname) - } - } - - // First verify the signature - sig, err := s.decodeSignature(sigpath) - if err != nil { - return ver, errors.Wrap(err, "failed to decode signature") - } - - by, err := s.verifySignature(sig) - if err != nil { - return ver, err - } - ver.SignedBy = by - - // Second, verify the hash of the tarball. - sum, err := DigestFile(chartpath) - if err != nil { - return ver, err - } - _, sums, err := parseMessageBlock(sig.Plaintext) - if err != nil { - return ver, err - } - - sum = "sha256:" + sum - basename := filepath.Base(chartpath) - if sha, ok := sums.Files[basename]; !ok { - return ver, errors.Errorf("provenance does not contain a SHA for a file named %q", basename) - } else if sha != sum { - return ver, errors.Errorf("sha256 sum does not match for %s: %q != %q", basename, sha, sum) - } - ver.FileHash = sum - ver.FileName = basename - - // TODO: when image signing is added, verify that here. - - return ver, nil -} - -func (s *Signatory) decodeSignature(filename string) (*clearsign.Block, error) { - data, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - - block, _ := clearsign.Decode(data) - if block == nil { - // There was no sig in the file. - return nil, errors.New("signature block not found") - } - - return block, nil -} - -// verifySignature verifies that the given block is validly signed, and returns the signer. -func (s *Signatory) verifySignature(block *clearsign.Block) (*openpgp.Entity, error) { - return openpgp.CheckDetachedSignature( - s.KeyRing, - bytes.NewBuffer(block.Bytes), - block.ArmoredSignature.Body, - ) -} - -func messageBlock(chartpath string) (*bytes.Buffer, error) { - var b *bytes.Buffer - // Checksum the archive - chash, err := DigestFile(chartpath) - if err != nil { - return b, err - } - - base := filepath.Base(chartpath) - sums := &SumCollection{ - Files: map[string]string{ - base: "sha256:" + chash, - }, - } - - // Load the archive into memory. - chart, err := loader.LoadFile(chartpath) - if err != nil { - return b, err - } - - // Buffer a hash + checksums YAML file - data, err := yaml.Marshal(chart.Metadata) - if err != nil { - return b, err - } - - // FIXME: YAML uses ---\n as a file start indicator, but this is not legal in a PGP - // clearsign block. So we use ...\n, which is the YAML document end marker. - // http://yaml.org/spec/1.2/spec.html#id2800168 - b = bytes.NewBuffer(data) - b.WriteString("\n...\n") - - data, err = yaml.Marshal(sums) - if err != nil { - return b, err - } - b.Write(data) - - return b, nil -} - -// parseMessageBlock -func parseMessageBlock(data []byte) (*hapi.Metadata, *SumCollection, error) { - // This sucks. - parts := bytes.Split(data, []byte("\n...\n")) - if len(parts) < 2 { - return nil, nil, errors.New("message block must have at least two parts") - } - - md := &hapi.Metadata{} - sc := &SumCollection{} - - if err := yaml.Unmarshal(parts[0], md); err != nil { - return md, sc, err - } - err := yaml.Unmarshal(parts[1], sc) - return md, sc, err -} - -// loadKey loads a GPG key found at a particular path. -func loadKey(keypath string) (*openpgp.Entity, error) { - f, err := os.Open(keypath) - if err != nil { - return nil, err - } - defer f.Close() - - pr := packet.NewReader(f) - return openpgp.ReadEntity(pr) -} - -func loadKeyRing(ringpath string) (openpgp.EntityList, error) { - f, err := os.Open(ringpath) - if err != nil { - return nil, err - } - defer f.Close() - return openpgp.ReadKeyRing(f) -} - -// DigestFile calculates a SHA256 hash (like Docker) for a given file. -// -// It takes the path to the archive file, and returns a string representation of -// the SHA256 sum. -// -// The intended use of this function is to generate a sum of a chart TGZ file. -func DigestFile(filename string) (string, error) { - f, err := os.Open(filename) - if err != nil { - return "", err - } - defer f.Close() - return Digest(f) -} - -// Digest hashes a reader and returns a SHA256 digest. -// -// Helm uses SHA256 as its default hash for all non-cryptographic applications. -func Digest(in io.Reader) (string, error) { - hash := crypto.SHA256.New() - if _, err := io.Copy(hash, in); err != nil { - return "", nil - } - return hex.EncodeToString(hash.Sum(nil)), nil -} diff --git a/pkg/provenance/sign_test.go b/pkg/provenance/sign_test.go deleted file mode 100644 index 1f4d2d2..0000000 --- a/pkg/provenance/sign_test.go +++ /dev/null @@ -1,312 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package provenance - -import ( - "io/ioutil" - "os" - "path/filepath" - "strings" - "testing" - - pgperrors "golang.org/x/crypto/openpgp/errors" -) - -const ( - // testKeyFile is the secret key. - // Generating keys should be done with `gpg --gen-key`. The current key - // was generated to match Go's defaults (RSA/RSA 2048). It has no pass - // phrase. Use `gpg --export-secret-keys helm-test` to export the secret. - testKeyfile = "testdata/helm-test-key.secret" - - // testPasswordKeyFile is a keyfile with a password. - testPasswordKeyfile = "testdata/helm-password-key.secret" - - // testPubfile is the public key file. - // Use `gpg --export helm-test` to export the public key. - testPubfile = "testdata/helm-test-key.pub" - - // Generated name for the PGP key in testKeyFile. - testKeyName = `Helm Testing (This key should only be used for testing. DO NOT TRUST.) ` - - testPasswordKeyName = `password key (fake) ` - - testChartfile = "testdata/hashtest-1.2.3.tgz" - - // testSigBlock points to a signature generated by an external tool. - // This file was generated with GnuPG: - // gpg --clearsign -u helm-test --openpgp testdata/msgblock.yaml - testSigBlock = "testdata/msgblock.yaml.asc" - - // testTamperedSigBlock is a tampered copy of msgblock.yaml.asc - testTamperedSigBlock = "testdata/msgblock.yaml.tampered" - - // testSumfile points to a SHA256 sum generated by an external tool. - // We always want to validate against an external tool's representation to - // verify that we haven't done something stupid. This file was generated - // with shasum. - // shasum -a 256 hashtest-1.2.3.tgz > testdata/hashtest.sha256 - testSumfile = "testdata/hashtest.sha256" -) - -// testMessageBlock represents the expected message block for the testdata/hashtest chart. -const testMessageBlock = `apiVersion: v1 -description: Test chart versioning -name: hashtest -version: 1.2.3 - -... -files: - hashtest-1.2.3.tgz: sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 -` - -func TestMessageBlock(t *testing.T) { - out, err := messageBlock(testChartfile) - if err != nil { - t.Fatal(err) - } - got := out.String() - - if got != testMessageBlock { - t.Errorf("Expected:\n%q\nGot\n%q\n", testMessageBlock, got) - } -} - -func TestParseMessageBlock(t *testing.T) { - md, sc, err := parseMessageBlock([]byte(testMessageBlock)) - if err != nil { - t.Fatal(err) - } - - if md.Name != "hashtest" { - t.Errorf("Expected name %q, got %q", "hashtest", md.Name) - } - - if lsc := len(sc.Files); lsc != 1 { - t.Errorf("Expected 1 file, got %d", lsc) - } - - if hash, ok := sc.Files["hashtest-1.2.3.tgz"]; !ok { - t.Errorf("hashtest file not found in Files") - } else if hash != "sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888" { - t.Errorf("Unexpected hash: %q", hash) - } -} - -func TestLoadKey(t *testing.T) { - k, err := loadKey(testKeyfile) - if err != nil { - t.Fatal(err) - } - - if _, ok := k.Identities[testKeyName]; !ok { - t.Errorf("Expected to load a key for user %q", testKeyName) - } -} - -func TestLoadKeyRing(t *testing.T) { - k, err := loadKeyRing(testPubfile) - if err != nil { - t.Fatal(err) - } - - if len(k) > 1 { - t.Errorf("Expected 1, got %d", len(k)) - } - - for _, e := range k { - if ii, ok := e.Identities[testKeyName]; !ok { - t.Errorf("Expected %s in %v", testKeyName, ii) - } - } -} - -func TestDigest(t *testing.T) { - f, err := os.Open(testChartfile) - if err != nil { - t.Fatal(err) - } - defer f.Close() - - hash, err := Digest(f) - if err != nil { - t.Fatal(err) - } - - sig, err := readSumFile(testSumfile) - if err != nil { - t.Fatal(err) - } - - if !strings.Contains(sig, hash) { - t.Errorf("Expected %s to be in %s", hash, sig) - } -} - -func TestNewFromFiles(t *testing.T) { - s, err := NewFromFiles(testKeyfile, testPubfile) - if err != nil { - t.Fatal(err) - } - - if _, ok := s.Entity.Identities[testKeyName]; !ok { - t.Errorf("Expected to load a key for user %q", testKeyName) - } -} - -func TestDigestFile(t *testing.T) { - hash, err := DigestFile(testChartfile) - if err != nil { - t.Fatal(err) - } - - sig, err := readSumFile(testSumfile) - if err != nil { - t.Fatal(err) - } - - if !strings.Contains(sig, hash) { - t.Errorf("Expected %s to be in %s", hash, sig) - } -} - -func TestDecryptKey(t *testing.T) { - k, err := NewFromKeyring(testPasswordKeyfile, testPasswordKeyName) - if err != nil { - t.Fatal(err) - } - - if !k.Entity.PrivateKey.Encrypted { - t.Fatal("Key is not encrypted") - } - - // We give this a simple callback that returns the password. - if err := k.DecryptKey(func(s string) ([]byte, error) { - return []byte("secret"), nil - }); err != nil { - t.Fatal(err) - } - - // Re-read the key (since we already unlocked it) - k, err = NewFromKeyring(testPasswordKeyfile, testPasswordKeyName) - if err != nil { - t.Fatal(err) - } - // Now we give it a bogus password. - if err := k.DecryptKey(func(s string) ([]byte, error) { - return []byte("secrets_and_lies"), nil - }); err == nil { - t.Fatal("Expected an error when giving a bogus passphrase") - } -} - -func TestClearSign(t *testing.T) { - signer, err := NewFromFiles(testKeyfile, testPubfile) - if err != nil { - t.Fatal(err) - } - - sig, err := signer.ClearSign(testChartfile) - if err != nil { - t.Fatal(err) - } - t.Logf("Sig:\n%s", sig) - - if !strings.Contains(sig, testMessageBlock) { - t.Errorf("expected message block to be in sig: %s", sig) - } -} - -func TestDecodeSignature(t *testing.T) { - // Unlike other tests, this does a round-trip test, ensuring that a signature - // generated by the library can also be verified by the library. - - signer, err := NewFromFiles(testKeyfile, testPubfile) - if err != nil { - t.Fatal(err) - } - - sig, err := signer.ClearSign(testChartfile) - if err != nil { - t.Fatal(err) - } - - f, err := ioutil.TempFile("", "helm-test-sig-") - if err != nil { - t.Fatal(err) - } - - tname := f.Name() - defer func() { - os.Remove(tname) - }() - f.WriteString(sig) - f.Close() - - sig2, err := signer.decodeSignature(tname) - if err != nil { - t.Fatal(err) - } - - by, err := signer.verifySignature(sig2) - if err != nil { - t.Fatal(err) - } - - if _, ok := by.Identities[testKeyName]; !ok { - t.Errorf("Expected identity %q", testKeyName) - } -} - -func TestVerify(t *testing.T) { - signer, err := NewFromFiles(testKeyfile, testPubfile) - if err != nil { - t.Fatal(err) - } - - if ver, err := signer.Verify(testChartfile, testSigBlock); err != nil { - t.Errorf("Failed to pass verify. Err: %s", err) - } else if len(ver.FileHash) == 0 { - t.Error("Verification is missing hash.") - } else if ver.SignedBy == nil { - t.Error("No SignedBy field") - } else if ver.FileName != filepath.Base(testChartfile) { - t.Errorf("FileName is unexpectedly %q", ver.FileName) - } - - if _, err = signer.Verify(testChartfile, testTamperedSigBlock); err == nil { - t.Errorf("Expected %s to fail.", testTamperedSigBlock) - } - - switch err.(type) { - case pgperrors.SignatureError: - t.Logf("Tampered sig block error: %s (%T)", err, err) - default: - t.Errorf("Expected invalid signature error, got %q (%T)", err, err) - } -} - -// readSumFile reads a file containing a sum generated by the UNIX shasum tool. -func readSumFile(sumfile string) (string, error) { - data, err := ioutil.ReadFile(sumfile) - if err != nil { - return "", err - } - - sig := string(data) - parts := strings.SplitN(sig, " ", 2) - return parts[0], nil -} diff --git a/pkg/provenance/testdata/hashtest-1.2.3.tgz b/pkg/provenance/testdata/hashtest-1.2.3.tgz deleted file mode 100644 index 7bbc533..0000000 Binary files a/pkg/provenance/testdata/hashtest-1.2.3.tgz and /dev/null differ diff --git a/pkg/provenance/testdata/hashtest-1.2.3.tgz.prov b/pkg/provenance/testdata/hashtest-1.2.3.tgz.prov deleted file mode 100755 index 3a788cd..0000000 --- a/pkg/provenance/testdata/hashtest-1.2.3.tgz.prov +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA512 - -apiVersion: v1 -description: Test chart versioning -name: hashtest -version: 1.2.3 - -... -files: - hashtest-1.2.3.tgz: sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 ------BEGIN PGP SIGNATURE----- - -wsBcBAEBCgAQBQJcon2ICRCEO7+YH8GHYgAASEAIAHD4Rad+LF47qNydI+k7x3aC -/qkdsqxE9kCUHtTJkZObE/Zmj2w3Opq0gcQftz4aJ2G9raqPDvwOzxnTxOkGfUdK -qIye48gFHzr2a7HnMTWr+HLQc4Gg+9kysIwkW4TM8wYV10osysYjBrhcafrHzFSK -791dBHhXP/aOrJQbFRob0GRFQ4pXdaSww1+kVaZLiKSPkkMKt9uk9Po1ggJYSIDX -uzXNcr78jTWACqkAtwx8+CJ8yzcGeuXSVNABDgbmAgpY0YT+Bz/UOWq4Q7tyuWnS -x9BKrvcb+Gc/6S0oK0Ffp8K4iSWYp79uH1bZ2oBS1yajA0c5h5i7qI3N4cabREw= -=YgnR ------END PGP SIGNATURE----- \ No newline at end of file diff --git a/pkg/provenance/testdata/hashtest.sha256 b/pkg/provenance/testdata/hashtest.sha256 deleted file mode 100644 index 05173ed..0000000 --- a/pkg/provenance/testdata/hashtest.sha256 +++ /dev/null @@ -1 +0,0 @@ -c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 hashtest-1.2.3.tgz diff --git a/pkg/provenance/testdata/hashtest/.helmignore b/pkg/provenance/testdata/hashtest/.helmignore deleted file mode 100644 index 435b756..0000000 --- a/pkg/provenance/testdata/hashtest/.helmignore +++ /dev/null @@ -1,5 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -.git diff --git a/pkg/provenance/testdata/hashtest/Chart.yaml b/pkg/provenance/testdata/hashtest/Chart.yaml deleted file mode 100644 index 6edf5f8..0000000 --- a/pkg/provenance/testdata/hashtest/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: Test chart versioning -name: hashtest -version: 1.2.3 diff --git a/pkg/provenance/testdata/hashtest/values.yaml b/pkg/provenance/testdata/hashtest/values.yaml deleted file mode 100644 index 0827a01..0000000 --- a/pkg/provenance/testdata/hashtest/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for hashtest. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/pkg/provenance/testdata/helm-password-key.secret b/pkg/provenance/testdata/helm-password-key.secret deleted file mode 100644 index 03c8aa5..0000000 Binary files a/pkg/provenance/testdata/helm-password-key.secret and /dev/null differ diff --git a/pkg/provenance/testdata/helm-test-key.pub b/pkg/provenance/testdata/helm-test-key.pub deleted file mode 100644 index 38714f2..0000000 Binary files a/pkg/provenance/testdata/helm-test-key.pub and /dev/null differ diff --git a/pkg/provenance/testdata/helm-test-key.secret b/pkg/provenance/testdata/helm-test-key.secret deleted file mode 100644 index a966aef..0000000 Binary files a/pkg/provenance/testdata/helm-test-key.secret and /dev/null differ diff --git a/pkg/provenance/testdata/msgblock.yaml b/pkg/provenance/testdata/msgblock.yaml deleted file mode 100644 index c16293f..0000000 --- a/pkg/provenance/testdata/msgblock.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -description: Test chart versioning -name: hashtest -version: 1.2.3 - -... -files: - hashtest-1.2.3.tgz: sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 diff --git a/pkg/provenance/testdata/msgblock.yaml.asc b/pkg/provenance/testdata/msgblock.yaml.asc deleted file mode 100644 index b4187b7..0000000 --- a/pkg/provenance/testdata/msgblock.yaml.asc +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA512 - -apiVersion: v1 -description: Test chart versioning -name: hashtest -version: 1.2.3 - -... -files: - hashtest-1.2.3.tgz: sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 ------BEGIN PGP SIGNATURE----- - -iQFJBAEBCgAzFiEEXmFTibU8o38O5gvThDu/mB/Bh2IFAlyiiDcVHGhlbG0tdGVz -dGluZ0BoZWxtLnNoAAoJEIQ7v5gfwYdiILAH/2f3GMVh+ZY5a+szOBudcuivjTcz -0Im1MwWQZfB1po3Yu7smWZbf5tJCzvVpYtvRlfa0nguuIh763MwOh9Q7dBXOLAxm -VCxqHm3svnNenBNfOpIygaMTgMZKxI4RrsKBgwPOTmlNtKg2lVaCiJAI30TXE6bB -/DwEYX0wmTssrAcSpTzOOSC+zHnPKew+5A3SY3ms+gAtVAcLepmJjI7RS7RhQxDl -AG+rWYis5gpDrk3U9OG1EOxqbftOAMqUl/kwI9eu5cPouN85rWwMe5pvHAvuyr/y -caYdlXDHTZsXmBuvfiUX6gqXtrpPCyKTCP+RzNf3+bXJM8m3u3gbMjGvKjU= -=vHcU ------END PGP SIGNATURE----- diff --git a/pkg/provenance/testdata/msgblock.yaml.tampered b/pkg/provenance/testdata/msgblock.yaml.tampered deleted file mode 100644 index f15811b..0000000 --- a/pkg/provenance/testdata/msgblock.yaml.tampered +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA512 - -description: Test chart versioning -name: hashtest -version: 1.2.3+tampered - -... -files: - hashtest-1.2.3.tgz: sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75 ------BEGIN PGP SIGNATURE----- -Comment: GPGTools - https://gpgtools.org - -iQEcBAEBCgAGBQJXlp8KAAoJEIQ7v5gfwYdiE7sIAJYDiza+asekeooSXLvQiK+G -PKnveqQpx49EZ6L7Y7UlW25SyH8EjXXHeJysDywCXF3w4luxN9n56ffU0KEW11IY -F+JSjmgIWLS6ti7ZAGEi6JInQ/30rOAIpTEBRBL2IueW3m63mezrGK6XkBlGqpor -C9WKeqLi+DWlMoBtsEy3Uk0XP6pn/qBFICYAbLQQU0sCCUT8CBA8f8aidxi7aw9t -i404yYF+Dvc6i4JlSG77SV0ZJBWllUvsWoCd9Jli0NAuaMqmE7mzcEt/dE+Fm2Ql -Bx3tr1WS4xTRiFQdcOttOl93H+OaHTh+Y0qqLTzzpCvqmttG0HfI6lMeCs7LeyA= -=vEK+ ------END PGP SIGNATURE----- diff --git a/pkg/provenance/testdata/regen-hashtest.sh b/pkg/provenance/testdata/regen-hashtest.sh deleted file mode 100755 index 4381fd0..0000000 --- a/pkg/provenance/testdata/regen-hashtest.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -helm package hashtest -shasum -a 256 hashtest-1.2.3.tgz > hashtest.sha256 diff --git a/pkg/release/hook.go b/pkg/release/hook.go deleted file mode 100644 index 662320f..0000000 --- a/pkg/release/hook.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package release - -import ( - "helm.sh/helm/v3/pkg/time" -) - -// HookEvent specifies the hook event -type HookEvent string - -// Hook event types -const ( - HookPreInstall HookEvent = "pre-install" - HookPostInstall HookEvent = "post-install" - HookPreDelete HookEvent = "pre-delete" - HookPostDelete HookEvent = "post-delete" - HookPreUpgrade HookEvent = "pre-upgrade" - HookPostUpgrade HookEvent = "post-upgrade" - HookPreRollback HookEvent = "pre-rollback" - HookPostRollback HookEvent = "post-rollback" - HookTest HookEvent = "test" -) - -func (x HookEvent) String() string { return string(x) } - -// HookDeletePolicy specifies the hook delete policy -type HookDeletePolicy string - -// Hook delete policy types -const ( - HookSucceeded HookDeletePolicy = "hook-succeeded" - HookFailed HookDeletePolicy = "hook-failed" - HookBeforeHookCreation HookDeletePolicy = "before-hook-creation" -) - -func (x HookDeletePolicy) String() string { return string(x) } - -// HookAnnotation is the label name for a hook -const HookAnnotation = "helm.sh/hook" - -// HookWeightAnnotation is the label name for a hook weight -const HookWeightAnnotation = "helm.sh/hook-weight" - -// HookDeleteAnnotation is the label name for the delete policy for a hook -const HookDeleteAnnotation = "helm.sh/hook-delete-policy" - -// Hook defines a hook object. -type Hook struct { - Name string `json:"name,omitempty"` - // Kind is the Kubernetes kind. - Kind string `json:"kind,omitempty"` - // Path is the chart-relative path to the template. - Path string `json:"path,omitempty"` - // Manifest is the manifest contents. - Manifest string `json:"manifest,omitempty"` - // Events are the events that this hook fires on. - Events []HookEvent `json:"events,omitempty"` - // LastRun indicates the date/time this was last run. - LastRun HookExecution `json:"last_run,omitempty"` - // Weight indicates the sort order for execution among similar Hook type - Weight int `json:"weight,omitempty"` - // DeletePolicies are the policies that indicate when to delete the hook - DeletePolicies []HookDeletePolicy `json:"delete_policies,omitempty"` -} - -// A HookExecution records the result for the last execution of a hook for a given release. -type HookExecution struct { - // StartedAt indicates the date/time this hook was started - StartedAt time.Time `json:"started_at,omitempty"` - // CompletedAt indicates the date/time this hook was completed. - CompletedAt time.Time `json:"completed_at,omitempty"` - // Phase indicates whether the hook completed successfully - Phase HookPhase `json:"phase"` -} - -// A HookPhase indicates the state of a hook execution -type HookPhase string - -const ( - // HookPhaseUnknown indicates that a hook is in an unknown state - HookPhaseUnknown HookPhase = "Unknown" - // HookPhaseRunning indicates that a hook is currently executing - HookPhaseRunning HookPhase = "Running" - // HookPhaseSucceeded indicates that hook execution succeeded - HookPhaseSucceeded HookPhase = "Succeeded" - // HookPhaseFailed indicates that hook execution failed - HookPhaseFailed HookPhase = "Failed" -) - -// Strng converts a hook phase to a printable string -func (x HookPhase) String() string { return string(x) } diff --git a/pkg/release/info.go b/pkg/release/info.go deleted file mode 100644 index 0cb2bab..0000000 --- a/pkg/release/info.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package release - -import ( - "helm.sh/helm/v3/pkg/time" -) - -// Info describes release information. -type Info struct { - // FirstDeployed is when the release was first deployed. - FirstDeployed time.Time `json:"first_deployed,omitempty"` - // LastDeployed is when the release was last deployed. - LastDeployed time.Time `json:"last_deployed,omitempty"` - // Deleted tracks when this object was deleted. - Deleted time.Time `json:"deleted"` - // Description is human-friendly "log entry" about this release. - Description string `json:"description,omitempty"` - // Status is the current state of the release - Status Status `json:"status,omitempty"` - // Contains the rendered templates/NOTES.txt if available - Notes string `json:"notes,omitempty"` -} diff --git a/pkg/release/mock.go b/pkg/release/mock.go deleted file mode 100644 index 3abbd57..0000000 --- a/pkg/release/mock.go +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package release - -import ( - "math/rand" - - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/time" -) - -// MockHookTemplate is the hook template used for all mock release objects. -var MockHookTemplate = `apiVersion: v1 -kind: Job -metadata: - annotations: - "helm.sh/hook": pre-install -` - -// MockManifest is the manifest used for all mock release objects. -var MockManifest = `apiVersion: v1 -kind: Secret -metadata: - name: fixture -` - -// MockReleaseOptions allows for user-configurable options on mock release objects. -type MockReleaseOptions struct { - Name string - Version int - Chart *chart.Chart - Status Status - Namespace string -} - -// Mock creates a mock release object based on options set by MockReleaseOptions. This function should typically not be used outside of testing. -func Mock(opts *MockReleaseOptions) *Release { - date := time.Unix(242085845, 0).UTC() - - name := opts.Name - if name == "" { - name = "testrelease-" + string(rand.Intn(100)) - } - - version := 1 - if opts.Version != 0 { - version = opts.Version - } - - namespace := opts.Namespace - if namespace == "" { - namespace = "default" - } - - ch := opts.Chart - if opts.Chart == nil { - ch = &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "foo", - Version: "0.1.0-beta.1", - AppVersion: "1.0", - }, - Templates: []*chart.File{ - {Name: "templates/foo.tpl", Data: []byte(MockManifest)}, - }, - } - } - - scode := StatusDeployed - if len(opts.Status) > 0 { - scode = opts.Status - } - - info := &Info{ - FirstDeployed: date, - LastDeployed: date, - Status: scode, - Description: "Release mock", - Notes: "Some mock release notes!", - } - - return &Release{ - Name: name, - Info: info, - Chart: ch, - Config: map[string]interface{}{"name": "value"}, - Version: version, - Namespace: namespace, - Hooks: []*Hook{ - { - Name: "pre-install-hook", - Kind: "Job", - Path: "pre-install-hook.yaml", - Manifest: MockHookTemplate, - LastRun: HookExecution{}, - Events: []HookEvent{HookPreInstall}, - }, - }, - Manifest: MockManifest, - } -} diff --git a/pkg/release/release.go b/pkg/release/release.go deleted file mode 100644 index a436998..0000000 --- a/pkg/release/release.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package release - -import "helm.sh/helm/v3/pkg/chart" - -// Release describes a deployment of a chart, together with the chart -// and the variables used to deploy that chart. -type Release struct { - // Name is the name of the release - Name string `json:"name,omitempty"` - // Info provides information about a release - Info *Info `json:"info,omitempty"` - // Chart is the chart that was released. - Chart *chart.Chart `json:"chart,omitempty"` - // Config is the set of extra Values added to the chart. - // These values override the default values inside of the chart. - Config map[string]interface{} `json:"config,omitempty"` - // Manifest is the string representation of the rendered template. - Manifest string `json:"manifest,omitempty"` - // Hooks are all of the hooks declared for this release. - Hooks []*Hook `json:"hooks,omitempty"` - // Version is an int which represents the version of the release. - Version int `json:"version,omitempty"` - // Namespace is the kubernetes namespace of the release. - Namespace string `json:"namespace,omitempty"` -} - -// SetStatus is a helper for setting the status on a release. -func (r *Release) SetStatus(status Status, msg string) { - r.Info.Status = status - r.Info.Description = msg -} diff --git a/pkg/release/responses.go b/pkg/release/responses.go deleted file mode 100644 index 7ee1fc2..0000000 --- a/pkg/release/responses.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package release - -// UninstallReleaseResponse represents a successful response to an uninstall request. -type UninstallReleaseResponse struct { - // Release is the release that was marked deleted. - Release *Release `json:"release,omitempty"` - // Info is an uninstall message - Info string `json:"info,omitempty"` -} diff --git a/pkg/release/status.go b/pkg/release/status.go deleted file mode 100644 index 49b0f15..0000000 --- a/pkg/release/status.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package release - -// Status is the status of a release -type Status string - -// Describe the status of a release -// NOTE: Make sure to update cmd/helm/status.go when adding or modifying any of these statuses. -const ( - // StatusUnknown indicates that a release is in an uncertain state. - StatusUnknown Status = "unknown" - // StatusDeployed indicates that the release has been pushed to Kubernetes. - StatusDeployed Status = "deployed" - // StatusUninstalled indicates that a release has been uninstalled from Kubernetes. - StatusUninstalled Status = "uninstalled" - // StatusSuperseded indicates that this release object is outdated and a newer one exists. - StatusSuperseded Status = "superseded" - // StatusFailed indicates that the release was not successfully deployed. - StatusFailed Status = "failed" - // StatusUninstalling indicates that a uninstall operation is underway. - StatusUninstalling Status = "uninstalling" - // StatusPendingInstall indicates that an install operation is underway. - StatusPendingInstall Status = "pending-install" - // StatusPendingUpgrade indicates that an upgrade operation is underway. - StatusPendingUpgrade Status = "pending-upgrade" - // StatusPendingRollback indicates that an rollback operation is underway. - StatusPendingRollback Status = "pending-rollback" -) - -func (x Status) String() string { return string(x) } diff --git a/pkg/releaseutil/filter.go b/pkg/releaseutil/filter.go deleted file mode 100644 index dbd0df8..0000000 --- a/pkg/releaseutil/filter.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil // import "helm.sh/helm/v3/pkg/releaseutil" - -import rspb "helm.sh/helm/v3/pkg/release" - -// FilterFunc returns true if the release object satisfies -// the predicate of the underlying filter func. -type FilterFunc func(*rspb.Release) bool - -// Check applies the FilterFunc to the release object. -func (fn FilterFunc) Check(rls *rspb.Release) bool { - if rls == nil { - return false - } - return fn(rls) -} - -// Filter applies the filter(s) to the list of provided releases -// returning the list that satisfies the filtering predicate. -func (fn FilterFunc) Filter(rels []*rspb.Release) (rets []*rspb.Release) { - for _, rel := range rels { - if fn.Check(rel) { - rets = append(rets, rel) - } - } - return -} - -// Any returns a FilterFunc that filters a list of releases -// determined by the predicate 'f0 || f1 || ... || fn'. -func Any(filters ...FilterFunc) FilterFunc { - return func(rls *rspb.Release) bool { - for _, filter := range filters { - if filter(rls) { - return true - } - } - return false - } -} - -// All returns a FilterFunc that filters a list of releases -// determined by the predicate 'f0 && f1 && ... && fn'. -func All(filters ...FilterFunc) FilterFunc { - return func(rls *rspb.Release) bool { - for _, filter := range filters { - if !filter(rls) { - return false - } - } - return true - } -} - -// StatusFilter filters a set of releases by status code. -func StatusFilter(status rspb.Status) FilterFunc { - return FilterFunc(func(rls *rspb.Release) bool { - if rls == nil { - return true - } - return rls.Info.Status == status - }) -} diff --git a/pkg/releaseutil/filter_test.go b/pkg/releaseutil/filter_test.go deleted file mode 100644 index 31ac306..0000000 --- a/pkg/releaseutil/filter_test.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil // import "helm.sh/helm/v3/pkg/releaseutil" - -import ( - "testing" - - rspb "helm.sh/helm/v3/pkg/release" -) - -func TestFilterAny(t *testing.T) { - ls := Any(StatusFilter(rspb.StatusUninstalled)).Filter(releases) - if len(ls) != 2 { - t.Fatalf("expected 2 results, got '%d'", len(ls)) - } - - r0, r1 := ls[0], ls[1] - switch { - case r0.Info.Status != rspb.StatusUninstalled: - t.Fatalf("expected UNINSTALLED result, got '%s'", r1.Info.Status.String()) - case r1.Info.Status != rspb.StatusUninstalled: - t.Fatalf("expected UNINSTALLED result, got '%s'", r1.Info.Status.String()) - } -} - -func TestFilterAll(t *testing.T) { - fn := FilterFunc(func(rls *rspb.Release) bool { - // true if not uninstalled and version < 4 - v0 := !StatusFilter(rspb.StatusUninstalled).Check(rls) - v1 := rls.Version < 4 - return v0 && v1 - }) - - ls := All(fn).Filter(releases) - if len(ls) != 1 { - t.Fatalf("expected 1 result, got '%d'", len(ls)) - } - - switch r0 := ls[0]; { - case r0.Version == 4: - t.Fatal("got release with status revision 4") - case r0.Info.Status == rspb.StatusUninstalled: - t.Fatal("got release with status UNINSTALLED") - } -} diff --git a/pkg/releaseutil/kind_sorter.go b/pkg/releaseutil/kind_sorter.go deleted file mode 100644 index 92ffa03..0000000 --- a/pkg/releaseutil/kind_sorter.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil - -import "sort" - -// KindSortOrder is an ordering of Kinds. -type KindSortOrder []string - -// InstallOrder is the order in which manifests should be installed (by Kind). -// -// Those occurring earlier in the list get installed before those occurring later in the list. -var InstallOrder KindSortOrder = []string{ - "Namespace", - "NetworkPolicy", - "ResourceQuota", - "LimitRange", - "PodSecurityPolicy", - "PodDisruptionBudget", - "ServiceAccount", - "Secret", - "ConfigMap", - "StorageClass", - "PersistentVolume", - "PersistentVolumeClaim", - "CustomResourceDefinition", - "ClusterRole", - "ClusterRoleList", - "ClusterRoleBinding", - "ClusterRoleBindingList", - "Role", - "RoleList", - "RoleBinding", - "RoleBindingList", - "Service", - "DaemonSet", - "Pod", - "ReplicationController", - "ReplicaSet", - "Deployment", - "HorizontalPodAutoscaler", - "StatefulSet", - "Job", - "CronJob", - "Ingress", - "APIService", -} - -// UninstallOrder is the order in which manifests should be uninstalled (by Kind). -// -// Those occurring earlier in the list get uninstalled before those occurring later in the list. -var UninstallOrder KindSortOrder = []string{ - "APIService", - "Ingress", - "Service", - "CronJob", - "Job", - "StatefulSet", - "HorizontalPodAutoscaler", - "Deployment", - "ReplicaSet", - "ReplicationController", - "Pod", - "DaemonSet", - "RoleBindingList", - "RoleBinding", - "RoleList", - "Role", - "ClusterRoleBindingList", - "ClusterRoleBinding", - "ClusterRoleList", - "ClusterRole", - "CustomResourceDefinition", - "PersistentVolumeClaim", - "PersistentVolume", - "StorageClass", - "ConfigMap", - "Secret", - "ServiceAccount", - "PodDisruptionBudget", - "PodSecurityPolicy", - "LimitRange", - "ResourceQuota", - "NetworkPolicy", - "Namespace", -} - -// sortByKind does an in-place sort of manifests by Kind. -// -// Results are sorted by 'ordering', keeping order of items with equal kind/priority -func sortByKind(manifests []Manifest, ordering KindSortOrder) []Manifest { - ks := newKindSorter(manifests, ordering) - sort.Stable(ks) - return ks.manifests -} - -type kindSorter struct { - ordering map[string]int - manifests []Manifest -} - -func newKindSorter(m []Manifest, s KindSortOrder) *kindSorter { - o := make(map[string]int, len(s)) - for v, k := range s { - o[k] = v - } - - return &kindSorter{ - manifests: m, - ordering: o, - } -} - -func (k *kindSorter) Len() int { return len(k.manifests) } - -func (k *kindSorter) Swap(i, j int) { k.manifests[i], k.manifests[j] = k.manifests[j], k.manifests[i] } - -func (k *kindSorter) Less(i, j int) bool { - a := k.manifests[i] - b := k.manifests[j] - first, aok := k.ordering[a.Head.Kind] - second, bok := k.ordering[b.Head.Kind] - - if !aok && !bok { - // if both are unknown then sort alphabetically by kind, keep original order if same kind - if a.Head.Kind != b.Head.Kind { - return a.Head.Kind < b.Head.Kind - } - return first < second - } - // unknown kind is last - if !aok { - return false - } - if !bok { - return true - } - // sort different kinds, keep original order if same priority - return first < second -} diff --git a/pkg/releaseutil/kind_sorter_test.go b/pkg/releaseutil/kind_sorter_test.go deleted file mode 100644 index 4747e82..0000000 --- a/pkg/releaseutil/kind_sorter_test.go +++ /dev/null @@ -1,268 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil - -import ( - "bytes" - "testing" -) - -func TestKindSorter(t *testing.T) { - manifests := []Manifest{ - { - Name: "i", - Head: &SimpleHead{Kind: "ClusterRole"}, - }, - { - Name: "I", - Head: &SimpleHead{Kind: "ClusterRoleList"}, - }, - { - Name: "j", - Head: &SimpleHead{Kind: "ClusterRoleBinding"}, - }, - { - Name: "J", - Head: &SimpleHead{Kind: "ClusterRoleBindingList"}, - }, - { - Name: "f", - Head: &SimpleHead{Kind: "ConfigMap"}, - }, - { - Name: "u", - Head: &SimpleHead{Kind: "CronJob"}, - }, - { - Name: "2", - Head: &SimpleHead{Kind: "CustomResourceDefinition"}, - }, - { - Name: "n", - Head: &SimpleHead{Kind: "DaemonSet"}, - }, - { - Name: "r", - Head: &SimpleHead{Kind: "Deployment"}, - }, - { - Name: "!", - Head: &SimpleHead{Kind: "HonkyTonkSet"}, - }, - { - Name: "v", - Head: &SimpleHead{Kind: "Ingress"}, - }, - { - Name: "t", - Head: &SimpleHead{Kind: "Job"}, - }, - { - Name: "c", - Head: &SimpleHead{Kind: "LimitRange"}, - }, - { - Name: "a", - Head: &SimpleHead{Kind: "Namespace"}, - }, - { - Name: "A", - Head: &SimpleHead{Kind: "NetworkPolicy"}, - }, - { - Name: "g", - Head: &SimpleHead{Kind: "PersistentVolume"}, - }, - { - Name: "h", - Head: &SimpleHead{Kind: "PersistentVolumeClaim"}, - }, - { - Name: "o", - Head: &SimpleHead{Kind: "Pod"}, - }, - { - Name: "3", - Head: &SimpleHead{Kind: "PodDisruptionBudget"}, - }, - { - Name: "C", - Head: &SimpleHead{Kind: "PodSecurityPolicy"}, - }, - { - Name: "q", - Head: &SimpleHead{Kind: "ReplicaSet"}, - }, - { - Name: "p", - Head: &SimpleHead{Kind: "ReplicationController"}, - }, - { - Name: "b", - Head: &SimpleHead{Kind: "ResourceQuota"}, - }, - { - Name: "k", - Head: &SimpleHead{Kind: "Role"}, - }, - { - Name: "K", - Head: &SimpleHead{Kind: "RoleList"}, - }, - { - Name: "l", - Head: &SimpleHead{Kind: "RoleBinding"}, - }, - { - Name: "L", - Head: &SimpleHead{Kind: "RoleBindingList"}, - }, - { - Name: "e", - Head: &SimpleHead{Kind: "Secret"}, - }, - { - Name: "m", - Head: &SimpleHead{Kind: "Service"}, - }, - { - Name: "d", - Head: &SimpleHead{Kind: "ServiceAccount"}, - }, - { - Name: "s", - Head: &SimpleHead{Kind: "StatefulSet"}, - }, - { - Name: "1", - Head: &SimpleHead{Kind: "StorageClass"}, - }, - { - Name: "w", - Head: &SimpleHead{Kind: "APIService"}, - }, - { - Name: "x", - Head: &SimpleHead{Kind: "HorizontalPodAutoscaler"}, - }, - } - - for _, test := range []struct { - description string - order KindSortOrder - expected string - }{ - {"install", InstallOrder, "aAbcC3def1gh2iIjJkKlLmnopqrxstuvw!"}, - {"uninstall", UninstallOrder, "wvmutsxrqponLlKkJjIi2hg1fed3CcbAa!"}, - } { - var buf bytes.Buffer - t.Run(test.description, func(t *testing.T) { - if got, want := len(test.expected), len(manifests); got != want { - t.Fatalf("Expected %d names in order, got %d", want, got) - } - defer buf.Reset() - for _, r := range sortByKind(manifests, test.order) { - buf.WriteString(r.Name) - } - if got := buf.String(); got != test.expected { - t.Errorf("Expected %q, got %q", test.expected, got) - } - }) - } -} - -// TestKindSorterKeepOriginalOrder verifies manifests of same kind are kept in original order -func TestKindSorterKeepOriginalOrder(t *testing.T) { - manifests := []Manifest{ - { - Name: "a", - Head: &SimpleHead{Kind: "ClusterRole"}, - }, - { - Name: "A", - Head: &SimpleHead{Kind: "ClusterRole"}, - }, - { - Name: "0", - Head: &SimpleHead{Kind: "ConfigMap"}, - }, - { - Name: "1", - Head: &SimpleHead{Kind: "ConfigMap"}, - }, - { - Name: "z", - Head: &SimpleHead{Kind: "ClusterRoleBinding"}, - }, - { - Name: "!", - Head: &SimpleHead{Kind: "ClusterRoleBinding"}, - }, - { - Name: "u2", - Head: &SimpleHead{Kind: "Unknown"}, - }, - { - Name: "u1", - Head: &SimpleHead{Kind: "Unknown"}, - }, - { - Name: "t3", - Head: &SimpleHead{Kind: "Unknown2"}, - }, - } - for _, test := range []struct { - description string - order KindSortOrder - expected string - }{ - // expectation is sorted by kind (unknown is last) and within each group of same kind, the order is kept - {"cm,clusterRole,clusterRoleBinding,Unknown,Unknown2", InstallOrder, "01aAz!u2u1t3"}, - } { - var buf bytes.Buffer - t.Run(test.description, func(t *testing.T) { - defer buf.Reset() - for _, r := range sortByKind(manifests, test.order) { - buf.WriteString(r.Name) - } - if got := buf.String(); got != test.expected { - t.Errorf("Expected %q, got %q", test.expected, got) - } - }) - } -} - -func TestKindSorterNamespaceAgainstUnknown(t *testing.T) { - unknown := Manifest{ - Name: "a", - Head: &SimpleHead{Kind: "Unknown"}, - } - namespace := Manifest{ - Name: "b", - Head: &SimpleHead{Kind: "Namespace"}, - } - - manifests := []Manifest{unknown, namespace} - sortByKind(manifests, InstallOrder) - - expectedOrder := []Manifest{namespace, unknown} - for i, manifest := range manifests { - if expectedOrder[i].Name != manifest.Name { - t.Errorf("Expected %s, got %s", expectedOrder[i].Name, manifest.Name) - } - } -} diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go deleted file mode 100644 index 0b04a45..0000000 --- a/pkg/releaseutil/manifest.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil - -import ( - "fmt" - "regexp" - "strconv" - "strings" -) - -// SimpleHead defines what the structure of the head of a manifest file -type SimpleHead struct { - Version string `json:"apiVersion"` - Kind string `json:"kind,omitempty"` - Metadata *struct { - Name string `json:"name"` - Annotations map[string]string `json:"annotations"` - } `json:"metadata,omitempty"` -} - -var sep = regexp.MustCompile("(?:^|\\s*\n)---\\s*") - -// SplitManifests takes a string of manifest and returns a map contains individual manifests -func SplitManifests(bigFile string) map[string]string { - // Basically, we're quickly splitting a stream of YAML documents into an - // array of YAML docs. The file name is just a place holder, but should be - // integer-sortable so that manifests get output in the same order as the - // input (see `BySplitManifestsOrder`). - tpl := "manifest-%d" - res := map[string]string{} - // Making sure that any extra whitespace in YAML stream doesn't interfere in splitting documents correctly. - bigFileTmp := strings.TrimSpace(bigFile) - docs := sep.Split(bigFileTmp, -1) - var count int - for _, d := range docs { - if d == "" { - continue - } - - d = strings.TrimSpace(d) - res[fmt.Sprintf(tpl, count)] = d - count = count + 1 - } - return res -} - -// BySplitManifestsOrder sorts by in-file manifest order, as provided in function `SplitManifests` -type BySplitManifestsOrder []string - -func (a BySplitManifestsOrder) Len() int { return len(a) } -func (a BySplitManifestsOrder) Less(i, j int) bool { - // Split `manifest-%d` - anum, _ := strconv.ParseInt(a[i][len("manifest-"):], 10, 0) - bnum, _ := strconv.ParseInt(a[j][len("manifest-"):], 10, 0) - return anum < bnum -} -func (a BySplitManifestsOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/pkg/releaseutil/manifest_sorter.go b/pkg/releaseutil/manifest_sorter.go deleted file mode 100644 index 24b0c3c..0000000 --- a/pkg/releaseutil/manifest_sorter.go +++ /dev/null @@ -1,233 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil - -import ( - "log" - "path" - "sort" - "strconv" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/release" -) - -// Manifest represents a manifest file, which has a name and some content. -type Manifest struct { - Name string - Content string - Head *SimpleHead -} - -// manifestFile represents a file that contains a manifest. -type manifestFile struct { - entries map[string]string - path string - apis chartutil.VersionSet -} - -// result is an intermediate structure used during sorting. -type result struct { - hooks []*release.Hook - generic []Manifest -} - -// TODO: Refactor this out. It's here because naming conventions were not followed through. -// So fix the Test hook names and then remove this. -var events = map[string]release.HookEvent{ - release.HookPreInstall.String(): release.HookPreInstall, - release.HookPostInstall.String(): release.HookPostInstall, - release.HookPreDelete.String(): release.HookPreDelete, - release.HookPostDelete.String(): release.HookPostDelete, - release.HookPreUpgrade.String(): release.HookPreUpgrade, - release.HookPostUpgrade.String(): release.HookPostUpgrade, - release.HookPreRollback.String(): release.HookPreRollback, - release.HookPostRollback.String(): release.HookPostRollback, - release.HookTest.String(): release.HookTest, - // Support test-success for backward compatibility with Helm 2 tests - "test-success": release.HookTest, -} - -// SortManifests takes a map of filename/YAML contents, splits the file -// by manifest entries, and sorts the entries into hook types. -// -// The resulting hooks struct will be populated with all of the generated hooks. -// Any file that does not declare one of the hook types will be placed in the -// 'generic' bucket. -// -// Files that do not parse into the expected format are simply placed into a map and -// returned. -func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering KindSortOrder) ([]*release.Hook, []Manifest, error) { - result := &result{} - - var sortedFilePaths []string - for filePath := range files { - sortedFilePaths = append(sortedFilePaths, filePath) - } - sort.Strings(sortedFilePaths) - - for _, filePath := range sortedFilePaths { - content := files[filePath] - - // Skip partials. We could return these as a separate map, but there doesn't - // seem to be any need for that at this time. - if strings.HasPrefix(path.Base(filePath), "_") { - continue - } - // Skip empty files and log this. - if strings.TrimSpace(content) == "" { - continue - } - - manifestFile := &manifestFile{ - entries: SplitManifests(content), - path: filePath, - apis: apis, - } - - if err := manifestFile.sort(result); err != nil { - return result.hooks, result.generic, err - } - } - - return result.hooks, sortByKind(result.generic, ordering), nil -} - -// sort takes a manifestFile object which may contain multiple resource definition -// entries and sorts each entry by hook types, and saves the resulting hooks and -// generic manifests (or non-hooks) to the result struct. -// -// To determine hook type, it looks for a YAML structure like this: -// -// kind: SomeKind -// apiVersion: v1 -// metadata: -// annotations: -// helm.sh/hook: pre-install -// -// To determine the policy to delete the hook, it looks for a YAML structure like this: -// -// kind: SomeKind -// apiVersion: v1 -// metadata: -// annotations: -// helm.sh/hook-delete-policy: hook-succeeded -func (file *manifestFile) sort(result *result) error { - // Go through manifests in order found in file (function `SplitManifests` creates integer-sortable keys) - var sortedEntryKeys []string - for entryKey := range file.entries { - sortedEntryKeys = append(sortedEntryKeys, entryKey) - } - sort.Sort(BySplitManifestsOrder(sortedEntryKeys)) - - for _, entryKey := range sortedEntryKeys { - m := file.entries[entryKey] - - var entry SimpleHead - if err := yaml.Unmarshal([]byte(m), &entry); err != nil { - return errors.Wrapf(err, "YAML parse error on %s", file.path) - } - - if !hasAnyAnnotation(entry) { - result.generic = append(result.generic, Manifest{ - Name: file.path, - Content: m, - Head: &entry, - }) - continue - } - - hookTypes, ok := entry.Metadata.Annotations[release.HookAnnotation] - if !ok { - result.generic = append(result.generic, Manifest{ - Name: file.path, - Content: m, - Head: &entry, - }) - continue - } - - hw := calculateHookWeight(entry) - - h := &release.Hook{ - Name: entry.Metadata.Name, - Kind: entry.Kind, - Path: file.path, - Manifest: m, - Events: []release.HookEvent{}, - Weight: hw, - DeletePolicies: []release.HookDeletePolicy{}, - } - - isUnknownHook := false - for _, hookType := range strings.Split(hookTypes, ",") { - hookType = strings.ToLower(strings.TrimSpace(hookType)) - e, ok := events[hookType] - if !ok { - isUnknownHook = true - break - } - h.Events = append(h.Events, e) - } - - if isUnknownHook { - log.Printf("info: skipping unknown hook: %q", hookTypes) - continue - } - - result.hooks = append(result.hooks, h) - - operateAnnotationValues(entry, release.HookDeleteAnnotation, func(value string) { - h.DeletePolicies = append(h.DeletePolicies, release.HookDeletePolicy(value)) - }) - } - - return nil -} - -// hasAnyAnnotation returns true if the given entry has any annotations at all. -func hasAnyAnnotation(entry SimpleHead) bool { - return entry.Metadata != nil && - entry.Metadata.Annotations != nil && - len(entry.Metadata.Annotations) != 0 -} - -// calculateHookWeight finds the weight in the hook weight annotation. -// -// If no weight is found, the assigned weight is 0 -func calculateHookWeight(entry SimpleHead) int { - hws := entry.Metadata.Annotations[release.HookWeightAnnotation] - hw, err := strconv.Atoi(hws) - if err != nil { - hw = 0 - } - return hw -} - -// operateAnnotationValues finds the given annotation and runs the operate function with the value of that annotation -func operateAnnotationValues(entry SimpleHead, annotation string, operate func(p string)) { - if dps, ok := entry.Metadata.Annotations[annotation]; ok { - for _, dp := range strings.Split(dps, ",") { - dp = strings.ToLower(strings.TrimSpace(dp)) - operate(dp) - } - } -} diff --git a/pkg/releaseutil/manifest_sorter_test.go b/pkg/releaseutil/manifest_sorter_test.go deleted file mode 100644 index 0d2d666..0000000 --- a/pkg/releaseutil/manifest_sorter_test.go +++ /dev/null @@ -1,228 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil - -import ( - "reflect" - "testing" - - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chartutil" - "helm.sh/helm/v3/pkg/release" -) - -func TestSortManifests(t *testing.T) { - - data := []struct { - name []string - path string - kind []string - hooks map[string][]release.HookEvent - manifest string - }{ - { - name: []string{"first"}, - path: "one", - kind: []string{"Job"}, - hooks: map[string][]release.HookEvent{"first": {release.HookPreInstall}}, - manifest: `apiVersion: v1 -kind: Job -metadata: - name: first - labels: - doesnot: matter - annotations: - "helm.sh/hook": pre-install -`, - }, - { - name: []string{"second"}, - path: "two", - kind: []string{"ReplicaSet"}, - hooks: map[string][]release.HookEvent{"second": {release.HookPostInstall}}, - manifest: `kind: ReplicaSet -apiVersion: v1beta1 -metadata: - name: second - annotations: - "helm.sh/hook": post-install -`, - }, { - name: []string{"third"}, - path: "three", - kind: []string{"ReplicaSet"}, - hooks: map[string][]release.HookEvent{"third": nil}, - manifest: `kind: ReplicaSet -apiVersion: v1beta1 -metadata: - name: third - annotations: - "helm.sh/hook": no-such-hook -`, - }, { - name: []string{"fourth"}, - path: "four", - kind: []string{"Pod"}, - hooks: map[string][]release.HookEvent{"fourth": nil}, - manifest: `kind: Pod -apiVersion: v1 -metadata: - name: fourth - annotations: - nothing: here`, - }, { - name: []string{"fifth"}, - path: "five", - kind: []string{"ReplicaSet"}, - hooks: map[string][]release.HookEvent{"fifth": {release.HookPostDelete, release.HookPostInstall}}, - manifest: `kind: ReplicaSet -apiVersion: v1beta1 -metadata: - name: fifth - annotations: - "helm.sh/hook": post-delete, post-install -`, - }, { - // Regression test: files with an underscore in the base name should be skipped. - name: []string{"sixth"}, - path: "six/_six", - kind: []string{"ReplicaSet"}, - hooks: map[string][]release.HookEvent{"sixth": nil}, - manifest: `invalid manifest`, // This will fail if partial is not skipped. - }, { - // Regression test: files with no content should be skipped. - name: []string{"seventh"}, - path: "seven", - kind: []string{"ReplicaSet"}, - hooks: map[string][]release.HookEvent{"seventh": nil}, - manifest: "", - }, - { - name: []string{"eighth", "example-test"}, - path: "eight", - kind: []string{"ConfigMap", "Pod"}, - hooks: map[string][]release.HookEvent{"eighth": nil, "example-test": {release.HookTest}}, - manifest: `kind: ConfigMap -apiVersion: v1 -metadata: - name: eighth -data: - name: value ---- -apiVersion: v1 -kind: Pod -metadata: - name: example-test - annotations: - "helm.sh/hook": test -`, - }, - } - - manifests := make(map[string]string, len(data)) - for _, o := range data { - manifests[o.path] = o.manifest - } - - hs, generic, err := SortManifests(manifests, chartutil.VersionSet{"v1", "v1beta1"}, InstallOrder) - if err != nil { - t.Fatalf("Unexpected error: %s", err) - } - - // This test will fail if 'six' or 'seven' was added. - if len(generic) != 2 { - t.Errorf("Expected 2 generic manifests, got %d", len(generic)) - } - - if len(hs) != 4 { - t.Errorf("Expected 4 hooks, got %d", len(hs)) - } - - for _, out := range hs { - found := false - for _, expect := range data { - if out.Path == expect.path { - found = true - if out.Path != expect.path { - t.Errorf("Expected path %s, got %s", expect.path, out.Path) - } - nameFound := false - for _, expectedName := range expect.name { - if out.Name == expectedName { - nameFound = true - } - } - if !nameFound { - t.Errorf("Got unexpected name %s", out.Name) - } - kindFound := false - for _, expectedKind := range expect.kind { - if out.Kind == expectedKind { - kindFound = true - } - } - if !kindFound { - t.Errorf("Got unexpected kind %s", out.Kind) - } - - expectedHooks := expect.hooks[out.Name] - if !reflect.DeepEqual(expectedHooks, out.Events) { - t.Errorf("expected events: %v but got: %v", expectedHooks, out.Events) - } - - } - } - if !found { - t.Errorf("Result not found: %v", out) - } - } - - // Verify the sort order - sorted := []Manifest{} - for _, s := range data { - manifests := SplitManifests(s.manifest) - - for _, m := range manifests { - var sh SimpleHead - if err := yaml.Unmarshal([]byte(m), &sh); err != nil { - // This is expected for manifests that are corrupt or empty. - t.Log(err) - continue - } - - name := sh.Metadata.Name - - // only keep track of non-hook manifests - if s.hooks[name] == nil { - another := Manifest{ - Content: m, - Name: name, - Head: &sh, - } - sorted = append(sorted, another) - } - } - } - - sorted = sortByKind(sorted, InstallOrder) - for i, m := range generic { - if m.Content != sorted[i].Content { - t.Errorf("Expected %q, got %q", m.Content, sorted[i].Content) - } - } -} diff --git a/pkg/releaseutil/manifest_test.go b/pkg/releaseutil/manifest_test.go deleted file mode 100644 index 8664d20..0000000 --- a/pkg/releaseutil/manifest_test.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil // import "helm.sh/helm/v3/pkg/releaseutil" - -import ( - "reflect" - "testing" -) - -const mockManifestFile = ` - ---- -apiVersion: v1 -kind: Pod -metadata: - name: finding-nemo, - annotations: - "helm.sh/hook": test -spec: - containers: - - name: nemo-test - image: fake-image - cmd: fake-command -` - -const expectedManifest = `apiVersion: v1 -kind: Pod -metadata: - name: finding-nemo, - annotations: - "helm.sh/hook": test -spec: - containers: - - name: nemo-test - image: fake-image - cmd: fake-command` - -func TestSplitManifest(t *testing.T) { - manifests := SplitManifests(mockManifestFile) - if len(manifests) != 1 { - t.Errorf("Expected 1 manifest, got %v", len(manifests)) - } - expected := map[string]string{"manifest-0": expectedManifest} - if !reflect.DeepEqual(manifests, expected) { - t.Errorf("Expected %v, got %v", expected, manifests) - } -} diff --git a/pkg/releaseutil/sorter.go b/pkg/releaseutil/sorter.go deleted file mode 100644 index 1a8aa78..0000000 --- a/pkg/releaseutil/sorter.go +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil // import "helm.sh/helm/v3/pkg/releaseutil" - -import ( - "sort" - - rspb "helm.sh/helm/v3/pkg/release" -) - -type list []*rspb.Release - -func (s list) Len() int { return len(s) } -func (s list) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// ByName sorts releases by name -type ByName struct{ list } - -// Less compares to releases -func (s ByName) Less(i, j int) bool { return s.list[i].Name < s.list[j].Name } - -// ByDate sorts releases by date -type ByDate struct{ list } - -// Less compares to releases -func (s ByDate) Less(i, j int) bool { - ti := s.list[i].Info.LastDeployed.Unix() - tj := s.list[j].Info.LastDeployed.Unix() - return ti < tj -} - -// ByRevision sorts releases by revision number -type ByRevision struct{ list } - -// Less compares to releases -func (s ByRevision) Less(i, j int) bool { - return s.list[i].Version < s.list[j].Version -} - -// Reverse reverses the list of releases sorted by the sort func. -func Reverse(list []*rspb.Release, sortFn func([]*rspb.Release)) { - sortFn(list) - for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { - list[i], list[j] = list[j], list[i] - } -} - -// SortByName returns the list of releases sorted -// in lexicographical order. -func SortByName(list []*rspb.Release) { - sort.Sort(ByName{list}) -} - -// SortByDate returns the list of releases sorted by a -// release's last deployed time (in seconds). -func SortByDate(list []*rspb.Release) { - sort.Sort(ByDate{list}) -} - -// SortByRevision returns the list of releases sorted by a -// release's revision number (release.Version). -func SortByRevision(list []*rspb.Release) { - sort.Sort(ByRevision{list}) -} diff --git a/pkg/releaseutil/sorter_test.go b/pkg/releaseutil/sorter_test.go deleted file mode 100644 index 69a6543..0000000 --- a/pkg/releaseutil/sorter_test.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package releaseutil // import "helm.sh/helm/v3/pkg/releaseutil" - -import ( - "testing" - "time" - - rspb "helm.sh/helm/v3/pkg/release" - helmtime "helm.sh/helm/v3/pkg/time" -) - -// note: this test data is shared with filter_test.go. - -var releases = []*rspb.Release{ - tsRelease("quiet-bear", 2, 2000, rspb.StatusSuperseded), - tsRelease("angry-bird", 4, 3000, rspb.StatusDeployed), - tsRelease("happy-cats", 1, 4000, rspb.StatusUninstalled), - tsRelease("vocal-dogs", 3, 6000, rspb.StatusUninstalled), -} - -func tsRelease(name string, vers int, dur time.Duration, status rspb.Status) *rspb.Release { - info := &rspb.Info{Status: status, LastDeployed: helmtime.Now().Add(dur)} - return &rspb.Release{ - Name: name, - Version: vers, - Info: info, - } -} - -func check(t *testing.T, by string, fn func(int, int) bool) { - for i := len(releases) - 1; i > 0; i-- { - if fn(i, i-1) { - t.Errorf("release at positions '(%d,%d)' not sorted by %s", i-1, i, by) - } - } -} - -func TestSortByName(t *testing.T) { - SortByName(releases) - - check(t, "ByName", func(i, j int) bool { - ni := releases[i].Name - nj := releases[j].Name - return ni < nj - }) -} - -func TestSortByDate(t *testing.T) { - SortByDate(releases) - - check(t, "ByDate", func(i, j int) bool { - ti := releases[i].Info.LastDeployed.Second() - tj := releases[j].Info.LastDeployed.Second() - return ti < tj - }) -} - -func TestSortByRevision(t *testing.T) { - SortByRevision(releases) - - check(t, "ByRevision", func(i, j int) bool { - vi := releases[i].Version - vj := releases[j].Version - return vi < vj - }) -} diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go deleted file mode 100644 index 38b6b8f..0000000 --- a/pkg/repo/chartrepo.go +++ /dev/null @@ -1,273 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repo // import "helm.sh/helm/v3/pkg/repo" - -import ( - "crypto/rand" - "encoding/base64" - "fmt" - "io/ioutil" - "net/url" - "os" - "path" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/provenance" -) - -// Entry represents a collection of parameters for chart repository -type Entry struct { - Name string `json:"name"` - URL string `json:"url"` - Username string `json:"username"` - Password string `json:"password"` - CertFile string `json:"certFile"` - KeyFile string `json:"keyFile"` - CAFile string `json:"caFile"` -} - -// ChartRepository represents a chart repository -type ChartRepository struct { - Config *Entry - ChartPaths []string - IndexFile *IndexFile - Client getter.Getter - CachePath string -} - -// NewChartRepository constructs ChartRepository -func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository, error) { - u, err := url.Parse(cfg.URL) - if err != nil { - return nil, errors.Errorf("invalid chart URL format: %s", cfg.URL) - } - - client, err := getters.ByScheme(u.Scheme) - if err != nil { - return nil, errors.Errorf("could not find protocol handler for: %s", u.Scheme) - } - - return &ChartRepository{ - Config: cfg, - IndexFile: NewIndexFile(), - Client: client, - CachePath: helmpath.CachePath("repository"), - }, nil -} - -// Load loads a directory of charts as if it were a repository. -// -// It requires the presence of an index.yaml file in the directory. -func (r *ChartRepository) Load() error { - dirInfo, err := os.Stat(r.Config.Name) - if err != nil { - return err - } - if !dirInfo.IsDir() { - return errors.Errorf("%q is not a directory", r.Config.Name) - } - - // FIXME: Why are we recursively walking directories? - // FIXME: Why are we not reading the repositories.yaml to figure out - // what repos to use? - filepath.Walk(r.Config.Name, func(path string, f os.FileInfo, err error) error { - if !f.IsDir() { - if strings.Contains(f.Name(), "-index.yaml") { - i, err := LoadIndexFile(path) - if err != nil { - return nil - } - r.IndexFile = i - } else if strings.HasSuffix(f.Name(), ".tgz") { - r.ChartPaths = append(r.ChartPaths, path) - } - } - return nil - }) - return nil -} - -// DownloadIndexFile fetches the index from a repository. -func (r *ChartRepository) DownloadIndexFile() (string, error) { - parsedURL, err := url.Parse(r.Config.URL) - if err != nil { - return "", err - } - parsedURL.RawPath = path.Join(parsedURL.RawPath, "index.yaml") - parsedURL.Path = path.Join(parsedURL.Path, "index.yaml") - - indexURL := parsedURL.String() - // TODO add user-agent - resp, err := r.Client.Get(indexURL, - getter.WithURL(r.Config.URL), - getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile), - getter.WithBasicAuth(r.Config.Username, r.Config.Password), - ) - if err != nil { - return "", err - } - - index, err := ioutil.ReadAll(resp) - if err != nil { - return "", err - } - - indexFile, err := loadIndex(index) - if err != nil { - return "", err - } - - // Create the chart list file in the cache directory - var charts strings.Builder - for name := range indexFile.Entries { - fmt.Fprintln(&charts, name) - } - chartsFile := filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name)) - os.MkdirAll(filepath.Dir(chartsFile), 0755) - ioutil.WriteFile(chartsFile, []byte(charts.String()), 0644) - - // Create the index file in the cache directory - fname := filepath.Join(r.CachePath, helmpath.CacheIndexFile(r.Config.Name)) - os.MkdirAll(filepath.Dir(fname), 0755) - return fname, ioutil.WriteFile(fname, index, 0644) -} - -// Index generates an index for the chart repository and writes an index.yaml file. -func (r *ChartRepository) Index() error { - err := r.generateIndex() - if err != nil { - return err - } - return r.saveIndexFile() -} - -func (r *ChartRepository) saveIndexFile() error { - index, err := yaml.Marshal(r.IndexFile) - if err != nil { - return err - } - return ioutil.WriteFile(filepath.Join(r.Config.Name, indexPath), index, 0644) -} - -func (r *ChartRepository) generateIndex() error { - for _, path := range r.ChartPaths { - ch, err := loader.Load(path) - if err != nil { - return err - } - - digest, err := provenance.DigestFile(path) - if err != nil { - return err - } - - if !r.IndexFile.Has(ch.Name(), ch.Metadata.Version) { - r.IndexFile.Add(ch.Metadata, path, r.Config.URL, digest) - } - // TODO: If a chart exists, but has a different Digest, should we error? - } - r.IndexFile.SortEntries() - return nil -} - -// FindChartInRepoURL finds chart in chart repository pointed by repoURL -// without adding repo to repositories -func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { - return FindChartInAuthRepoURL(repoURL, "", "", chartName, chartVersion, certFile, keyFile, caFile, getters) -} - -// FindChartInAuthRepoURL finds chart in chart repository pointed by repoURL -// without adding repo to repositories, like FindChartInRepoURL, -// but it also receives credentials for the chart repository. -func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { - - // Download and write the index file to a temporary location - buf := make([]byte, 20) - rand.Read(buf) - name := strings.ReplaceAll(base64.StdEncoding.EncodeToString(buf), "/", "-") - - c := Entry{ - URL: repoURL, - Username: username, - Password: password, - CertFile: certFile, - KeyFile: keyFile, - CAFile: caFile, - Name: name, - } - r, err := NewChartRepository(&c, getters) - if err != nil { - return "", err - } - idx, err := r.DownloadIndexFile() - if err != nil { - return "", errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", repoURL) - } - - // Read the index file for the repository to get chart information and return chart URL - repoIndex, err := LoadIndexFile(idx) - if err != nil { - return "", err - } - - errMsg := fmt.Sprintf("chart %q", chartName) - if chartVersion != "" { - errMsg = fmt.Sprintf("%s version %q", errMsg, chartVersion) - } - cv, err := repoIndex.Get(chartName, chartVersion) - if err != nil { - return "", errors.Errorf("%s not found in %s repository", errMsg, repoURL) - } - - if len(cv.URLs) == 0 { - return "", errors.Errorf("%s has no downloadable URLs", errMsg) - } - - chartURL := cv.URLs[0] - - absoluteChartURL, err := ResolveReferenceURL(repoURL, chartURL) - if err != nil { - return "", errors.Wrap(err, "failed to make chart URL absolute") - } - - return absoluteChartURL, nil -} - -// ResolveReferenceURL resolves refURL relative to baseURL. -// If refURL is absolute, it simply returns refURL. -func ResolveReferenceURL(baseURL, refURL string) (string, error) { - parsedBaseURL, err := url.Parse(baseURL) - if err != nil { - return "", errors.Wrapf(err, "failed to parse %s as URL", baseURL) - } - - parsedRefURL, err := url.Parse(refURL) - if err != nil { - return "", errors.Wrapf(err, "failed to parse %s as URL", refURL) - } - - // We need a trailing slash for ResolveReference to work, but make sure there isn't already one - parsedBaseURL.Path = strings.TrimSuffix(parsedBaseURL.Path, "/") + "/" - return parsedBaseURL.ResolveReference(parsedRefURL).String(), nil -} diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go deleted file mode 100644 index c6b227a..0000000 --- a/pkg/repo/chartrepo_test.go +++ /dev/null @@ -1,364 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repo - -import ( - "bytes" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "reflect" - "strings" - "testing" - "time" - - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/getter" -) - -const ( - testRepository = "testdata/repository" - testURL = "http://example-charts.com" -) - -func TestLoadChartRepository(t *testing.T) { - r, err := NewChartRepository(&Entry{ - Name: testRepository, - URL: testURL, - }, getter.All(&cli.EnvSettings{})) - if err != nil { - t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) - } - - if err := r.Load(); err != nil { - t.Errorf("Problem loading chart repository from %s: %v", testRepository, err) - } - - paths := []string{ - filepath.Join(testRepository, "frobnitz-1.2.3.tgz"), - filepath.Join(testRepository, "sprocket-1.1.0.tgz"), - filepath.Join(testRepository, "sprocket-1.2.0.tgz"), - filepath.Join(testRepository, "universe/zarthal-1.0.0.tgz"), - } - - if r.Config.Name != testRepository { - t.Errorf("Expected %s as Name but got %s", testRepository, r.Config.Name) - } - - if !reflect.DeepEqual(r.ChartPaths, paths) { - t.Errorf("Expected %#v but got %#v\n", paths, r.ChartPaths) - } - - if r.Config.URL != testURL { - t.Errorf("Expected url for chart repository to be %s but got %s", testURL, r.Config.URL) - } -} - -func TestIndex(t *testing.T) { - r, err := NewChartRepository(&Entry{ - Name: testRepository, - URL: testURL, - }, getter.All(&cli.EnvSettings{})) - if err != nil { - t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) - } - - if err := r.Load(); err != nil { - t.Errorf("Problem loading chart repository from %s: %v", testRepository, err) - } - - err = r.Index() - if err != nil { - t.Errorf("Error performing index: %v\n", err) - } - - tempIndexPath := filepath.Join(testRepository, indexPath) - actual, err := LoadIndexFile(tempIndexPath) - defer os.Remove(tempIndexPath) // clean up - if err != nil { - t.Errorf("Error loading index file %v", err) - } - verifyIndex(t, actual) - - // Re-index and test again. - err = r.Index() - if err != nil { - t.Errorf("Error performing re-index: %s\n", err) - } - second, err := LoadIndexFile(tempIndexPath) - if err != nil { - t.Errorf("Error re-loading index file %v", err) - } - verifyIndex(t, second) -} - -type CustomGetter struct { - repoUrls []string -} - -func (g *CustomGetter) Get(href string, options ...getter.Option) (*bytes.Buffer, error) { - index := &IndexFile{ - APIVersion: "v1", - Generated: time.Now(), - } - indexBytes, err := yaml.Marshal(index) - if err != nil { - return nil, err - } - g.repoUrls = append(g.repoUrls, href) - return bytes.NewBuffer(indexBytes), nil -} - -func TestIndexCustomSchemeDownload(t *testing.T) { - repoName := "gcs-repo" - repoURL := "gs://some-gcs-bucket" - myCustomGetter := &CustomGetter{} - customGetterConstructor := func(options ...getter.Option) (getter.Getter, error) { - return myCustomGetter, nil - } - providers := getter.Providers{{ - Schemes: []string{"gs"}, - New: customGetterConstructor, - }} - repo, err := NewChartRepository(&Entry{ - Name: repoName, - URL: repoURL, - }, providers) - if err != nil { - t.Fatalf("Problem loading chart repository from %s: %v", repoURL, err) - } - repo.CachePath = ensure.TempDir(t) - - tempIndexFile, err := ioutil.TempFile("", "test-repo") - if err != nil { - t.Fatalf("Failed to create temp index file: %v", err) - } - defer os.Remove(tempIndexFile.Name()) - - idx, err := repo.DownloadIndexFile() - if err != nil { - t.Fatalf("Failed to download index file to %s: %v", idx, err) - } - - if len(myCustomGetter.repoUrls) != 1 { - t.Fatalf("Custom Getter.Get should be called once") - } - - expectedRepoIndexURL := repoURL + "/index.yaml" - if myCustomGetter.repoUrls[0] != expectedRepoIndexURL { - t.Fatalf("Custom Getter.Get should be called with %s", expectedRepoIndexURL) - } -} - -func verifyIndex(t *testing.T, actual *IndexFile) { - var empty time.Time - if actual.Generated == empty { - t.Errorf("Generated should be greater than 0: %s", actual.Generated) - } - - if actual.APIVersion != APIVersionV1 { - t.Error("Expected v1 API") - } - - entries := actual.Entries - if numEntries := len(entries); numEntries != 3 { - t.Errorf("Expected 3 charts to be listed in index file but got %v", numEntries) - } - - expects := map[string]ChartVersions{ - "frobnitz": { - { - Metadata: &chart.Metadata{ - Name: "frobnitz", - Version: "1.2.3", - }, - }, - }, - "sprocket": { - { - Metadata: &chart.Metadata{ - Name: "sprocket", - Version: "1.2.0", - }, - }, - { - Metadata: &chart.Metadata{ - Name: "sprocket", - Version: "1.1.0", - }, - }, - }, - "zarthal": { - { - Metadata: &chart.Metadata{ - Name: "zarthal", - Version: "1.0.0", - }, - }, - }, - } - - for name, versions := range expects { - got, ok := entries[name] - if !ok { - t.Errorf("Could not find %q entry", name) - continue - } - if len(versions) != len(got) { - t.Errorf("Expected %d versions, got %d", len(versions), len(got)) - continue - } - for i, e := range versions { - g := got[i] - if e.Name != g.Name { - t.Errorf("Expected %q, got %q", e.Name, g.Name) - } - if e.Version != g.Version { - t.Errorf("Expected %q, got %q", e.Version, g.Version) - } - if len(g.Keywords) != 3 { - t.Error("Expected 3 keywords.") - } - if len(g.Maintainers) != 2 { - t.Error("Expected 2 maintainers.") - } - if g.Created == empty { - t.Error("Expected created to be non-empty") - } - if g.Description == "" { - t.Error("Expected description to be non-empty") - } - if g.Home == "" { - t.Error("Expected home to be non-empty") - } - if g.Digest == "" { - t.Error("Expected digest to be non-empty") - } - if len(g.URLs) != 1 { - t.Error("Expected exactly 1 URL") - } - } - } -} - -// startLocalServerForTests Start the local helm server -func startLocalServerForTests(handler http.Handler) (*httptest.Server, error) { - if handler == nil { - fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml") - if err != nil { - return nil, err - } - handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write(fileBytes) - }) - } - - return httptest.NewServer(handler), nil -} - -func TestFindChartInRepoURL(t *testing.T) { - srv, err := startLocalServerForTests(nil) - if err != nil { - t.Fatal(err) - } - defer srv.Close() - - chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(&cli.EnvSettings{})) - if err != nil { - t.Fatalf("%v", err) - } - if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" { - t.Errorf("%s is not the valid URL", chartURL) - } - - chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(&cli.EnvSettings{})) - if err != nil { - t.Errorf("%s", err) - } - if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz" { - t.Errorf("%s is not the valid URL", chartURL) - } -} - -func TestErrorFindChartInRepoURL(t *testing.T) { - - g := getter.All(&cli.EnvSettings{ - RepositoryCache: ensure.TempDir(t), - }) - - if _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", g); err == nil { - t.Errorf("Expected error for bad chart URL, but did not get any errors") - } else if !strings.Contains(err.Error(), `looks like "http://someserver/something" is not a valid chart repository or cannot be reached: Get http://someserver/something/index.yaml`) { - t.Errorf("Expected error for bad chart URL, but got a different error (%v)", err) - } - - srv, err := startLocalServerForTests(nil) - if err != nil { - t.Fatal(err) - } - defer srv.Close() - - if _, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", g); err == nil { - t.Errorf("Expected error for chart not found, but did not get any errors") - } else if err.Error() != `chart "nginx1" not found in `+srv.URL+` repository` { - t.Errorf("Expected error for chart not found, but got a different error (%v)", err) - } - - if _, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", g); err == nil { - t.Errorf("Expected error for chart not found, but did not get any errors") - } else if err.Error() != `chart "nginx1" version "0.1.0" not found in `+srv.URL+` repository` { - t.Errorf("Expected error for chart not found, but got a different error (%v)", err) - } - - if _, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", g); err == nil { - t.Errorf("Expected error for no chart URLs available, but did not get any errors") - } else if err.Error() != `chart "chartWithNoURL" has no downloadable URLs` { - t.Errorf("Expected error for chart not found, but got a different error (%v)", err) - } -} - -func TestResolveReferenceURL(t *testing.T) { - chartURL, err := ResolveReferenceURL("http://localhost:8123/charts/", "nginx-0.2.0.tgz") - if err != nil { - t.Errorf("%s", err) - } - if chartURL != "http://localhost:8123/charts/nginx-0.2.0.tgz" { - t.Errorf("%s", chartURL) - } - - chartURL, err = ResolveReferenceURL("http://localhost:8123/charts-with-no-trailing-slash", "nginx-0.2.0.tgz") - if err != nil { - t.Errorf("%s", err) - } - if chartURL != "http://localhost:8123/charts-with-no-trailing-slash/nginx-0.2.0.tgz" { - t.Errorf("%s", chartURL) - } - - chartURL, err = ResolveReferenceURL("http://localhost:8123", "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz") - if err != nil { - t.Errorf("%s", err) - } - if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" { - t.Errorf("%s", chartURL) - } -} diff --git a/pkg/repo/doc.go b/pkg/repo/doc.go deleted file mode 100644 index 0565010..0000000 --- a/pkg/repo/doc.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package repo implements the Helm Chart Repository. - -A chart repository is an HTTP server that provides information on charts. A local -repository cache is an on-disk representation of a chart repository. - -There are two important file formats for chart repositories. - -The first is the 'index.yaml' format, which is expressed like this: - - apiVersion: v1 - entries: - frobnitz: - - created: 2016-09-29T12:14:34.830161306-06:00 - description: This is a frobnitz. - digest: 587bd19a9bd9d2bc4a6d25ab91c8c8e7042c47b4ac246e37bf8e1e74386190f4 - home: http://example.com - keywords: - - frobnitz - - sprocket - - dodad - maintainers: - - email: helm@example.com - name: The Helm Team - - email: nobody@example.com - name: Someone Else - name: frobnitz - urls: - - http://example-charts.com/testdata/repository/frobnitz-1.2.3.tgz - version: 1.2.3 - sprocket: - - created: 2016-09-29T12:14:34.830507606-06:00 - description: This is a sprocket" - digest: 8505ff813c39502cc849a38e1e4a8ac24b8e6e1dcea88f4c34ad9b7439685ae6 - home: http://example.com - keywords: - - frobnitz - - sprocket - - dodad - maintainers: - - email: helm@example.com - name: The Helm Team - - email: nobody@example.com - name: Someone Else - name: sprocket - urls: - - http://example-charts.com/testdata/repository/sprocket-1.2.0.tgz - version: 1.2.0 - generated: 2016-09-29T12:14:34.829721375-06:00 - -An index.yaml file contains the necessary descriptive information about what -charts are available in a repository, and how to get them. - -The second file format is the repositories.yaml file format. This file is for -facilitating local cached copies of one or more chart repositories. - -The format of a repository.yaml file is: - - apiVersion: v1 - generated: TIMESTAMP - repositories: - - name: stable - url: http://example.com/charts - cache: stable-index.yaml - - name: incubator - url: http://example.com/incubator - cache: incubator-index.yaml - -This file maps three bits of information about a repository: - - - The name the user uses to refer to it - - The fully qualified URL to the repository (index.yaml will be appended) - - The name of the local cachefile - -The format for both files was changed after Helm v2.0.0-Alpha.4. Helm is not -backwards compatible with those earlier versions. -*/ -package repo diff --git a/pkg/repo/index.go b/pkg/repo/index.go deleted file mode 100644 index 3638666..0000000 --- a/pkg/repo/index.go +++ /dev/null @@ -1,290 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repo - -import ( - "io/ioutil" - "os" - "path" - "path/filepath" - "sort" - "strings" - "time" - - "github.com/Masterminds/semver/v3" - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/internal/urlutil" - "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/chart/loader" - "helm.sh/helm/v3/pkg/provenance" -) - -var indexPath = "index.yaml" - -// APIVersionV1 is the v1 API version for index and repository files. -const APIVersionV1 = "v1" - -var ( - // ErrNoAPIVersion indicates that an API version was not specified. - ErrNoAPIVersion = errors.New("no API version specified") - // ErrNoChartVersion indicates that a chart with the given version is not found. - ErrNoChartVersion = errors.New("no chart version found") - // ErrNoChartName indicates that a chart with the given name is not found. - ErrNoChartName = errors.New("no chart name found") -) - -// ChartVersions is a list of versioned chart references. -// Implements a sorter on Version. -type ChartVersions []*ChartVersion - -// Len returns the length. -func (c ChartVersions) Len() int { return len(c) } - -// Swap swaps the position of two items in the versions slice. -func (c ChartVersions) Swap(i, j int) { c[i], c[j] = c[j], c[i] } - -// Less returns true if the version of entry a is less than the version of entry b. -func (c ChartVersions) Less(a, b int) bool { - // Failed parse pushes to the back. - i, err := semver.NewVersion(c[a].Version) - if err != nil { - return true - } - j, err := semver.NewVersion(c[b].Version) - if err != nil { - return false - } - return i.LessThan(j) -} - -// IndexFile represents the index file in a chart repository -type IndexFile struct { - APIVersion string `json:"apiVersion"` - Generated time.Time `json:"generated"` - Entries map[string]ChartVersions `json:"entries"` - PublicKeys []string `json:"publicKeys,omitempty"` -} - -// NewIndexFile initializes an index. -func NewIndexFile() *IndexFile { - return &IndexFile{ - APIVersion: APIVersionV1, - Generated: time.Now(), - Entries: map[string]ChartVersions{}, - PublicKeys: []string{}, - } -} - -// LoadIndexFile takes a file at the given path and returns an IndexFile object -func LoadIndexFile(path string) (*IndexFile, error) { - b, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - return loadIndex(b) -} - -// Add adds a file to the index -// This can leave the index in an unsorted state -func (i IndexFile) Add(md *chart.Metadata, filename, baseURL, digest string) { - u := filename - if baseURL != "" { - var err error - _, file := filepath.Split(filename) - u, err = urlutil.URLJoin(baseURL, file) - if err != nil { - u = path.Join(baseURL, file) - } - } - cr := &ChartVersion{ - URLs: []string{u}, - Metadata: md, - Digest: digest, - Created: time.Now(), - } - if ee, ok := i.Entries[md.Name]; !ok { - i.Entries[md.Name] = ChartVersions{cr} - } else { - i.Entries[md.Name] = append(ee, cr) - } -} - -// Has returns true if the index has an entry for a chart with the given name and exact version. -func (i IndexFile) Has(name, version string) bool { - _, err := i.Get(name, version) - return err == nil -} - -// SortEntries sorts the entries by version in descending order. -// -// In canonical form, the individual version records should be sorted so that -// the most recent release for every version is in the 0th slot in the -// Entries.ChartVersions array. That way, tooling can predict the newest -// version without needing to parse SemVers. -func (i IndexFile) SortEntries() { - for _, versions := range i.Entries { - sort.Sort(sort.Reverse(versions)) - } -} - -// Get returns the ChartVersion for the given name. -// -// If version is empty, this will return the chart with the latest stable version, -// prerelease versions will be skipped. -func (i IndexFile) Get(name, version string) (*ChartVersion, error) { - vs, ok := i.Entries[name] - if !ok { - return nil, ErrNoChartName - } - if len(vs) == 0 { - return nil, ErrNoChartVersion - } - - var constraint *semver.Constraints - if version == "" { - constraint, _ = semver.NewConstraint("*") - } else { - var err error - constraint, err = semver.NewConstraint(version) - if err != nil { - return nil, err - } - } - - // when customer input exact version, check whether have exact match one first - if len(version) != 0 { - for _, ver := range vs { - if version == ver.Version { - return ver, nil - } - } - } - - for _, ver := range vs { - test, err := semver.NewVersion(ver.Version) - if err != nil { - continue - } - - if constraint.Check(test) { - return ver, nil - } - } - return nil, errors.Errorf("no chart version found for %s-%s", name, version) -} - -// WriteFile writes an index file to the given destination path. -// -// The mode on the file is set to 'mode'. -func (i IndexFile) WriteFile(dest string, mode os.FileMode) error { - b, err := yaml.Marshal(i) - if err != nil { - return err - } - return ioutil.WriteFile(dest, b, mode) -} - -// Merge merges the given index file into this index. -// -// This merges by name and version. -// -// If one of the entries in the given index does _not_ already exist, it is added. -// In all other cases, the existing record is preserved. -// -// This can leave the index in an unsorted state -func (i *IndexFile) Merge(f *IndexFile) { - for _, cvs := range f.Entries { - for _, cv := range cvs { - if !i.Has(cv.Name, cv.Version) { - e := i.Entries[cv.Name] - i.Entries[cv.Name] = append(e, cv) - } - } - } -} - -// ChartVersion represents a chart entry in the IndexFile -type ChartVersion struct { - *chart.Metadata - URLs []string `json:"urls"` - Created time.Time `json:"created,omitempty"` - Removed bool `json:"removed,omitempty"` - Digest string `json:"digest,omitempty"` -} - -// IndexDirectory reads a (flat) directory and generates an index. -// -// It indexes only charts that have been packaged (*.tgz). -// -// The index returned will be in an unsorted state -func IndexDirectory(dir, baseURL string) (*IndexFile, error) { - archives, err := filepath.Glob(filepath.Join(dir, "*.tgz")) - if err != nil { - return nil, err - } - moreArchives, err := filepath.Glob(filepath.Join(dir, "**/*.tgz")) - if err != nil { - return nil, err - } - archives = append(archives, moreArchives...) - - index := NewIndexFile() - for _, arch := range archives { - fname, err := filepath.Rel(dir, arch) - if err != nil { - return index, err - } - - var parentDir string - parentDir, fname = filepath.Split(fname) - // filepath.Split appends an extra slash to the end of parentDir. We want to strip that out. - parentDir = strings.TrimSuffix(parentDir, string(os.PathSeparator)) - parentURL, err := urlutil.URLJoin(baseURL, parentDir) - if err != nil { - parentURL = path.Join(baseURL, parentDir) - } - - c, err := loader.Load(arch) - if err != nil { - // Assume this is not a chart. - continue - } - hash, err := provenance.DigestFile(arch) - if err != nil { - return index, err - } - index.Add(c.Metadata, fname, parentURL, hash) - } - return index, nil -} - -// loadIndex loads an index file and does minimal validity checking. -// -// This will fail if API Version is not set (ErrNoAPIVersion) or if the unmarshal fails. -func loadIndex(data []byte) (*IndexFile, error) { - i := &IndexFile{} - if err := yaml.Unmarshal(data, i); err != nil { - return i, err - } - i.SortEntries() - if i.APIVersion == "" { - return i, ErrNoAPIVersion - } - return i, nil -} diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go deleted file mode 100644 index 5dbd5e5..0000000 --- a/pkg/repo/index_test.go +++ /dev/null @@ -1,430 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repo - -import ( - "bufio" - "bytes" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "sort" - "strings" - "testing" - - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/getter" - "helm.sh/helm/v3/pkg/helmpath" - - "helm.sh/helm/v3/pkg/chart" -) - -const ( - testfile = "testdata/local-index.yaml" - unorderedTestfile = "testdata/local-index-unordered.yaml" - testRepo = "test-repo" -) - -func TestIndexFile(t *testing.T) { - i := NewIndexFile() - i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890") - i.Add(&chart.Metadata{Name: "cutter", Version: "0.1.1"}, "cutter-0.1.1.tgz", "http://example.com/charts", "sha256:1234567890abc") - i.Add(&chart.Metadata{Name: "cutter", Version: "0.1.0"}, "cutter-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890abc") - i.Add(&chart.Metadata{Name: "cutter", Version: "0.2.0"}, "cutter-0.2.0.tgz", "http://example.com/charts", "sha256:1234567890abc") - i.Add(&chart.Metadata{Name: "setter", Version: "0.1.9+alpha"}, "setter-0.1.9+alpha.tgz", "http://example.com/charts", "sha256:1234567890abc") - i.Add(&chart.Metadata{Name: "setter", Version: "0.1.9+beta"}, "setter-0.1.9+beta.tgz", "http://example.com/charts", "sha256:1234567890abc") - - i.SortEntries() - - if i.APIVersion != APIVersionV1 { - t.Error("Expected API version v1") - } - - if len(i.Entries) != 3 { - t.Errorf("Expected 3 charts. Got %d", len(i.Entries)) - } - - if i.Entries["clipper"][0].Name != "clipper" { - t.Errorf("Expected clipper, got %s", i.Entries["clipper"][0].Name) - } - - if len(i.Entries["cutter"]) != 3 { - t.Error("Expected three cutters.") - } - - // Test that the sort worked. 0.2 should be at the first index for Cutter. - if v := i.Entries["cutter"][0].Version; v != "0.2.0" { - t.Errorf("Unexpected first version: %s", v) - } - - cv, err := i.Get("setter", "0.1.9") - if err == nil && !strings.Contains(cv.Metadata.Version, "0.1.9") { - t.Errorf("Unexpected version: %s", cv.Metadata.Version) - } - - cv, err = i.Get("setter", "0.1.9+alpha") - if err != nil || cv.Metadata.Version != "0.1.9+alpha" { - t.Errorf("Expected version: 0.1.9+alpha") - } -} - -func TestLoadIndex(t *testing.T) { - b, err := ioutil.ReadFile(testfile) - if err != nil { - t.Fatal(err) - } - i, err := loadIndex(b) - if err != nil { - t.Fatal(err) - } - verifyLocalIndex(t, i) -} - -func TestLoadIndexFile(t *testing.T) { - i, err := LoadIndexFile(testfile) - if err != nil { - t.Fatal(err) - } - verifyLocalIndex(t, i) -} - -func TestLoadUnorderedIndex(t *testing.T) { - b, err := ioutil.ReadFile(unorderedTestfile) - if err != nil { - t.Fatal(err) - } - i, err := loadIndex(b) - if err != nil { - t.Fatal(err) - } - verifyLocalIndex(t, i) -} - -func TestMerge(t *testing.T) { - ind1 := NewIndexFile() - ind1.Add(&chart.Metadata{ - Name: "dreadnought", - Version: "0.1.0", - }, "dreadnought-0.1.0.tgz", "http://example.com", "aaaa") - - ind2 := NewIndexFile() - ind2.Add(&chart.Metadata{ - Name: "dreadnought", - Version: "0.2.0", - }, "dreadnought-0.2.0.tgz", "http://example.com", "aaaabbbb") - ind2.Add(&chart.Metadata{ - Name: "doughnut", - Version: "0.2.0", - }, "doughnut-0.2.0.tgz", "http://example.com", "ccccbbbb") - - ind1.Merge(ind2) - - if len(ind1.Entries) != 2 { - t.Errorf("Expected 2 entries, got %d", len(ind1.Entries)) - vs := ind1.Entries["dreadnought"] - if len(vs) != 2 { - t.Errorf("Expected 2 versions, got %d", len(vs)) - } - v := vs[0] - if v.Version != "0.2.0" { - t.Errorf("Expected %q version to be 0.2.0, got %s", v.Name, v.Version) - } - } - -} - -func TestDownloadIndexFile(t *testing.T) { - t.Run("should download index file", func(t *testing.T) { - srv, err := startLocalServerForTests(nil) - if err != nil { - t.Fatal(err) - } - defer srv.Close() - - r, err := NewChartRepository(&Entry{ - Name: testRepo, - URL: srv.URL, - }, getter.All(&cli.EnvSettings{})) - if err != nil { - t.Errorf("Problem creating chart repository from %s: %v", testRepo, err) - } - - idx, err := r.DownloadIndexFile() - if err != nil { - t.Fatalf("Failed to download index file to %s: %#v", idx, err) - } - - if _, err := os.Stat(idx); err != nil { - t.Fatalf("error finding created index file: %#v", err) - } - - b, err := ioutil.ReadFile(idx) - if err != nil { - t.Fatalf("error reading index file: %#v", err) - } - - i, err := loadIndex(b) - if err != nil { - t.Fatalf("Index %q failed to parse: %s", testfile, err) - } - verifyLocalIndex(t, i) - - // Check that charts file is also created - idx = filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name)) - if _, err := os.Stat(idx); err != nil { - t.Fatalf("error finding created charts file: %#v", err) - } - - b, err = ioutil.ReadFile(idx) - if err != nil { - t.Fatalf("error reading charts file: %#v", err) - } - verifyLocalChartsFile(t, b, i) - }) - - t.Run("should not decode the path in the repo url while downloading index", func(t *testing.T) { - chartRepoURLPath := "/some%2Fpath/test" - fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml") - if err != nil { - t.Fatal(err) - } - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.RawPath == chartRepoURLPath+"/index.yaml" { - w.Write(fileBytes) - } - }) - srv, err := startLocalServerForTests(handler) - if err != nil { - t.Fatal(err) - } - defer srv.Close() - - r, err := NewChartRepository(&Entry{ - Name: testRepo, - URL: srv.URL + chartRepoURLPath, - }, getter.All(&cli.EnvSettings{})) - if err != nil { - t.Errorf("Problem creating chart repository from %s: %v", testRepo, err) - } - - idx, err := r.DownloadIndexFile() - if err != nil { - t.Fatalf("Failed to download index file to %s: %#v", idx, err) - } - - if _, err := os.Stat(idx); err != nil { - t.Fatalf("error finding created index file: %#v", err) - } - - b, err := ioutil.ReadFile(idx) - if err != nil { - t.Fatalf("error reading index file: %#v", err) - } - - i, err := loadIndex(b) - if err != nil { - t.Fatalf("Index %q failed to parse: %s", testfile, err) - } - verifyLocalIndex(t, i) - - // Check that charts file is also created - idx = filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name)) - if _, err := os.Stat(idx); err != nil { - t.Fatalf("error finding created charts file: %#v", err) - } - - b, err = ioutil.ReadFile(idx) - if err != nil { - t.Fatalf("error reading charts file: %#v", err) - } - verifyLocalChartsFile(t, b, i) - }) -} - -func verifyLocalIndex(t *testing.T, i *IndexFile) { - numEntries := len(i.Entries) - if numEntries != 3 { - t.Errorf("Expected 3 entries in index file but got %d", numEntries) - } - - alpine, ok := i.Entries["alpine"] - if !ok { - t.Fatalf("'alpine' section not found.") - } - - if l := len(alpine); l != 1 { - t.Fatalf("'alpine' should have 1 chart, got %d", l) - } - - nginx, ok := i.Entries["nginx"] - if !ok || len(nginx) != 2 { - t.Fatalf("Expected 2 nginx entries") - } - - expects := []*ChartVersion{ - { - Metadata: &chart.Metadata{ - Name: "alpine", - Description: "string", - Version: "1.0.0", - Keywords: []string{"linux", "alpine", "small", "sumtin"}, - Home: "https://github.com/something", - }, - URLs: []string{ - "https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz", - "http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz", - }, - Digest: "sha256:1234567890abcdef", - }, - { - Metadata: &chart.Metadata{ - Name: "nginx", - Description: "string", - Version: "0.2.0", - Keywords: []string{"popular", "web server", "proxy"}, - Home: "https://github.com/something/else", - }, - URLs: []string{ - "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz", - }, - Digest: "sha256:1234567890abcdef", - }, - { - Metadata: &chart.Metadata{ - Name: "nginx", - Description: "string", - Version: "0.1.0", - Keywords: []string{"popular", "web server", "proxy"}, - Home: "https://github.com/something", - }, - URLs: []string{ - "https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz", - }, - Digest: "sha256:1234567890abcdef", - }, - } - tests := []*ChartVersion{alpine[0], nginx[0], nginx[1]} - - for i, tt := range tests { - expect := expects[i] - if tt.Name != expect.Name { - t.Errorf("Expected name %q, got %q", expect.Name, tt.Name) - } - if tt.Description != expect.Description { - t.Errorf("Expected description %q, got %q", expect.Description, tt.Description) - } - if tt.Version != expect.Version { - t.Errorf("Expected version %q, got %q", expect.Version, tt.Version) - } - if tt.Digest != expect.Digest { - t.Errorf("Expected digest %q, got %q", expect.Digest, tt.Digest) - } - if tt.Home != expect.Home { - t.Errorf("Expected home %q, got %q", expect.Home, tt.Home) - } - - for i, url := range tt.URLs { - if url != expect.URLs[i] { - t.Errorf("Expected URL %q, got %q", expect.URLs[i], url) - } - } - for i, kw := range tt.Keywords { - if kw != expect.Keywords[i] { - t.Errorf("Expected keywords %q, got %q", expect.Keywords[i], kw) - } - } - } -} - -func verifyLocalChartsFile(t *testing.T, chartsContent []byte, indexContent *IndexFile) { - var expected, real []string - for chart := range indexContent.Entries { - expected = append(expected, chart) - } - sort.Strings(expected) - - scanner := bufio.NewScanner(bytes.NewReader(chartsContent)) - for scanner.Scan() { - real = append(real, scanner.Text()) - } - sort.Strings(real) - - if strings.Join(expected, " ") != strings.Join(real, " ") { - t.Errorf("Cached charts file content unexpected. Expected:\n%s\ngot:\n%s", expected, real) - } -} - -func TestIndexDirectory(t *testing.T) { - dir := "testdata/repository" - index, err := IndexDirectory(dir, "http://localhost:8080") - if err != nil { - t.Fatal(err) - } - - if l := len(index.Entries); l != 3 { - t.Fatalf("Expected 3 entries, got %d", l) - } - - // Other things test the entry generation more thoroughly. We just test a - // few fields. - - corpus := []struct{ chartName, downloadLink string }{ - {"frobnitz", "http://localhost:8080/frobnitz-1.2.3.tgz"}, - {"zarthal", "http://localhost:8080/universe/zarthal-1.0.0.tgz"}, - } - - for _, test := range corpus { - cname := test.chartName - frobs, ok := index.Entries[cname] - if !ok { - t.Fatalf("Could not read chart %s", cname) - } - - frob := frobs[0] - if frob.Digest == "" { - t.Errorf("Missing digest of file %s.", frob.Name) - } - if frob.URLs[0] != test.downloadLink { - t.Errorf("Unexpected URLs: %v", frob.URLs) - } - if frob.Name != cname { - t.Errorf("Expected %q, got %q", cname, frob.Name) - } - } -} - -func TestIndexAdd(t *testing.T) { - i := NewIndexFile() - i.Add(&chart.Metadata{Name: "clipper", Version: "0.1.0"}, "clipper-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890") - - if i.Entries["clipper"][0].URLs[0] != "http://example.com/charts/clipper-0.1.0.tgz" { - t.Errorf("Expected http://example.com/charts/clipper-0.1.0.tgz, got %s", i.Entries["clipper"][0].URLs[0]) - } - - i.Add(&chart.Metadata{Name: "alpine", Version: "0.1.0"}, "/home/charts/alpine-0.1.0.tgz", "http://example.com/charts", "sha256:1234567890") - - if i.Entries["alpine"][0].URLs[0] != "http://example.com/charts/alpine-0.1.0.tgz" { - t.Errorf("Expected http://example.com/charts/alpine-0.1.0.tgz, got %s", i.Entries["alpine"][0].URLs[0]) - } - - i.Add(&chart.Metadata{Name: "deis", Version: "0.1.0"}, "/home/charts/deis-0.1.0.tgz", "http://example.com/charts/", "sha256:1234567890") - - if i.Entries["deis"][0].URLs[0] != "http://example.com/charts/deis-0.1.0.tgz" { - t.Errorf("Expected http://example.com/charts/deis-0.1.0.tgz, got %s", i.Entries["deis"][0].URLs[0]) - } -} diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go deleted file mode 100644 index 6f1e90d..0000000 --- a/pkg/repo/repo.go +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repo // import "helm.sh/helm/v3/pkg/repo" - -import ( - "io/ioutil" - "os" - "path/filepath" - "time" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" -) - -// File represents the repositories.yaml file -type File struct { - APIVersion string `json:"apiVersion"` - Generated time.Time `json:"generated"` - Repositories []*Entry `json:"repositories"` -} - -// NewFile generates an empty repositories file. -// -// Generated and APIVersion are automatically set. -func NewFile() *File { - return &File{ - APIVersion: APIVersionV1, - Generated: time.Now(), - Repositories: []*Entry{}, - } -} - -// LoadFile takes a file at the given path and returns a File object -func LoadFile(path string) (*File, error) { - r := new(File) - b, err := ioutil.ReadFile(path) - if err != nil { - return r, errors.Wrapf(err, "couldn't load repositories file (%s)", path) - } - - err = yaml.Unmarshal(b, r) - return r, err -} - -// Add adds one or more repo entries to a repo file. -func (r *File) Add(re ...*Entry) { - r.Repositories = append(r.Repositories, re...) -} - -// Update attempts to replace one or more repo entries in a repo file. If an -// entry with the same name doesn't exist in the repo file it will add it. -func (r *File) Update(re ...*Entry) { - for _, target := range re { - r.update(target) - } -} - -func (r *File) update(e *Entry) { - for j, repo := range r.Repositories { - if repo.Name == e.Name { - r.Repositories[j] = e - return - } - } - r.Add(e) -} - -// Has returns true if the given name is already a repository name. -func (r *File) Has(name string) bool { - entry := r.Get(name) - return entry != nil -} - -// Get returns an entry with the given name if it exists, otherwise returns nil -func (r *File) Get(name string) *Entry { - for _, entry := range r.Repositories { - if entry.Name == name { - return entry - } - } - return nil -} - -// Remove removes the entry from the list of repositories. -func (r *File) Remove(name string) bool { - cp := []*Entry{} - found := false - for _, rf := range r.Repositories { - if rf.Name == name { - found = true - continue - } - cp = append(cp, rf) - } - r.Repositories = cp - return found -} - -// WriteFile writes a repositories file to the given path. -func (r *File) WriteFile(path string, perm os.FileMode) error { - data, err := yaml.Marshal(r) - if err != nil { - return err - } - if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { - return err - } - return ioutil.WriteFile(path, data, perm) -} diff --git a/pkg/repo/repo_test.go b/pkg/repo/repo_test.go deleted file mode 100644 index f87d2c2..0000000 --- a/pkg/repo/repo_test.go +++ /dev/null @@ -1,227 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repo - -import ( - "io/ioutil" - "os" - "strings" - "testing" -) - -const testRepositoriesFile = "testdata/repositories.yaml" - -func TestFile(t *testing.T) { - rf := NewFile() - rf.Add( - &Entry{ - Name: "stable", - URL: "https://example.com/stable/charts", - }, - &Entry{ - Name: "incubator", - URL: "https://example.com/incubator", - }, - ) - - if len(rf.Repositories) != 2 { - t.Fatal("Expected 2 repositories") - } - - if rf.Has("nosuchrepo") { - t.Error("Found nonexistent repo") - } - if !rf.Has("incubator") { - t.Error("incubator repo is missing") - } - - stable := rf.Repositories[0] - if stable.Name != "stable" { - t.Error("stable is not named stable") - } - if stable.URL != "https://example.com/stable/charts" { - t.Error("Wrong URL for stable") - } -} - -func TestNewFile(t *testing.T) { - expects := NewFile() - expects.Add( - &Entry{ - Name: "stable", - URL: "https://example.com/stable/charts", - }, - &Entry{ - Name: "incubator", - URL: "https://example.com/incubator", - }, - ) - - file, err := LoadFile(testRepositoriesFile) - if err != nil { - t.Errorf("%q could not be loaded: %s", testRepositoriesFile, err) - } - - if len(expects.Repositories) != len(file.Repositories) { - t.Fatalf("Unexpected repo data: %#v", file.Repositories) - } - - for i, expect := range expects.Repositories { - got := file.Repositories[i] - if expect.Name != got.Name { - t.Errorf("Expected name %q, got %q", expect.Name, got.Name) - } - if expect.URL != got.URL { - t.Errorf("Expected url %q, got %q", expect.URL, got.URL) - } - } -} - -func TestRepoFile_Get(t *testing.T) { - repo := NewFile() - repo.Add( - &Entry{ - Name: "first", - URL: "https://example.com/first", - }, - &Entry{ - Name: "second", - URL: "https://example.com/second", - }, - &Entry{ - Name: "third", - URL: "https://example.com/third", - }, - &Entry{ - Name: "fourth", - URL: "https://example.com/fourth", - }, - ) - - name := "second" - - entry := repo.Get(name) - if entry == nil { - t.Fatalf("Expected repo entry %q to be found", name) - } - - if entry.URL != "https://example.com/second" { - t.Errorf("Expected repo URL to be %q but got %q", "https://example.com/second", entry.URL) - } - - entry = repo.Get("nonexistent") - if entry != nil { - t.Errorf("Got unexpected entry %+v", entry) - } -} - -func TestRemoveRepository(t *testing.T) { - sampleRepository := NewFile() - sampleRepository.Add( - &Entry{ - Name: "stable", - URL: "https://example.com/stable/charts", - }, - &Entry{ - Name: "incubator", - URL: "https://example.com/incubator", - }, - ) - - removeRepository := "stable" - found := sampleRepository.Remove(removeRepository) - if !found { - t.Errorf("expected repository %s not found", removeRepository) - } - - found = sampleRepository.Has(removeRepository) - if found { - t.Errorf("repository %s not deleted", removeRepository) - } -} - -func TestUpdateRepository(t *testing.T) { - sampleRepository := NewFile() - sampleRepository.Add( - &Entry{ - Name: "stable", - URL: "https://example.com/stable/charts", - }, - &Entry{ - Name: "incubator", - URL: "https://example.com/incubator", - }, - ) - newRepoName := "sample" - sampleRepository.Update(&Entry{Name: newRepoName, - URL: "https://example.com/sample", - }) - - if !sampleRepository.Has(newRepoName) { - t.Errorf("expected repository %s not found", newRepoName) - } - repoCount := len(sampleRepository.Repositories) - - sampleRepository.Update(&Entry{Name: newRepoName, - URL: "https://example.com/sample", - }) - - if repoCount != len(sampleRepository.Repositories) { - t.Errorf("invalid number of repositories found %d, expected number of repositories %d", len(sampleRepository.Repositories), repoCount) - } -} - -func TestWriteFile(t *testing.T) { - sampleRepository := NewFile() - sampleRepository.Add( - &Entry{ - Name: "stable", - URL: "https://example.com/stable/charts", - }, - &Entry{ - Name: "incubator", - URL: "https://example.com/incubator", - }, - ) - - file, err := ioutil.TempFile("", "helm-repo") - if err != nil { - t.Errorf("failed to create test-file (%v)", err) - } - defer os.Remove(file.Name()) - if err := sampleRepository.WriteFile(file.Name(), 0644); err != nil { - t.Errorf("failed to write file (%v)", err) - } - - repos, err := LoadFile(file.Name()) - if err != nil { - t.Errorf("failed to load file (%v)", err) - } - for _, repo := range sampleRepository.Repositories { - if !repos.Has(repo.Name) { - t.Errorf("expected repository %s not found", repo.Name) - } - } -} - -func TestRepoNotExists(t *testing.T) { - if _, err := LoadFile("/this/path/does/not/exist.yaml"); err == nil { - t.Errorf("expected err to be non-nil when path does not exist") - } else if !strings.Contains(err.Error(), "couldn't load repositories file") { - t.Errorf("expected prompt `couldn't load repositories file`") - } -} diff --git a/pkg/repo/repotest/doc.go b/pkg/repo/repotest/doc.go deleted file mode 100644 index 3bf98aa..0000000 --- a/pkg/repo/repotest/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package repotest provides utilities for testing. - -The server provides a testing server that can be set up and torn down quickly. -*/ -package repotest diff --git a/pkg/repo/repotest/server.go b/pkg/repo/repotest/server.go deleted file mode 100644 index 96a8bbf..0000000 --- a/pkg/repo/repotest/server.go +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repotest - -import ( - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/pkg/repo" -) - -// NewTempServer creates a server inside of a temp dir. -// -// If the passed in string is not "", it will be treated as a shell glob, and files -// will be copied from that path to the server's docroot. -// -// The caller is responsible for destroying the temp directory as well as stopping -// the server. -func NewTempServer(glob string) (*Server, error) { - tdir, err := ioutil.TempDir("", "helm-repotest-") - if err != nil { - return nil, err - } - srv := NewServer(tdir) - - if glob != "" { - if _, err := srv.CopyCharts(glob); err != nil { - srv.Stop() - return srv, err - } - } - - return srv, nil -} - -// NewServer creates a repository server for testing. -// -// docroot should be a temp dir managed by the caller. -// -// This will start the server, serving files off of the docroot. -// -// Use CopyCharts to move charts into the repository and then index them -// for service. -func NewServer(docroot string) *Server { - root, err := filepath.Abs(docroot) - if err != nil { - panic(err) - } - srv := &Server{ - docroot: root, - } - srv.Start() - // Add the testing repository as the only repo. - if err := setTestingRepository(srv.URL(), filepath.Join(root, "repositories.yaml")); err != nil { - panic(err) - } - return srv -} - -// Server is an implementation of a repository server for testing. -type Server struct { - docroot string - srv *httptest.Server - middleware http.HandlerFunc -} - -// WithMiddleware injects middleware in front of the server. This can be used to inject -// additional functionality like layering in an authentication frontend. -func (s *Server) WithMiddleware(middleware http.HandlerFunc) { - s.middleware = middleware -} - -// Root gets the docroot for the server. -func (s *Server) Root() string { - return s.docroot -} - -// CopyCharts takes a glob expression and copies those charts to the server root. -func (s *Server) CopyCharts(origin string) ([]string, error) { - files, err := filepath.Glob(origin) - if err != nil { - return []string{}, err - } - copied := make([]string, len(files)) - for i, f := range files { - base := filepath.Base(f) - newname := filepath.Join(s.docroot, base) - data, err := ioutil.ReadFile(f) - if err != nil { - return []string{}, err - } - if err := ioutil.WriteFile(newname, data, 0644); err != nil { - return []string{}, err - } - copied[i] = newname - } - - err = s.CreateIndex() - return copied, err -} - -// CreateIndex will read docroot and generate an index.yaml file. -func (s *Server) CreateIndex() error { - // generate the index - index, err := repo.IndexDirectory(s.docroot, s.URL()) - if err != nil { - return err - } - - d, err := yaml.Marshal(index) - if err != nil { - return err - } - - ifile := filepath.Join(s.docroot, "index.yaml") - return ioutil.WriteFile(ifile, d, 0644) -} - -func (s *Server) Start() { - s.srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if s.middleware != nil { - s.middleware.ServeHTTP(w, r) - } - http.FileServer(http.Dir(s.docroot)).ServeHTTP(w, r) - })) -} - -// Stop stops the server and closes all connections. -// -// It should be called explicitly. -func (s *Server) Stop() { - s.srv.Close() -} - -// URL returns the URL of the server. -// -// Example: -// http://localhost:1776 -func (s *Server) URL() string { - return s.srv.URL -} - -// LinkIndices links the index created with CreateIndex and makes a symbolic link to the cache index. -// -// This makes it possible to simulate a local cache of a repository. -func (s *Server) LinkIndices() error { - lstart := filepath.Join(s.docroot, "index.yaml") - ldest := filepath.Join(s.docroot, "test-index.yaml") - return os.Symlink(lstart, ldest) -} - -// setTestingRepository sets up a testing repository.yaml with only the given URL. -func setTestingRepository(url, fname string) error { - r := repo.NewFile() - r.Add(&repo.Entry{ - Name: "test", - URL: url, - }) - return r.WriteFile(fname, 0644) -} diff --git a/pkg/repo/repotest/server_test.go b/pkg/repo/repotest/server_test.go deleted file mode 100644 index ee62791..0000000 --- a/pkg/repo/repotest/server_test.go +++ /dev/null @@ -1,116 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package repotest - -import ( - "io/ioutil" - "net/http" - "path/filepath" - "testing" - - "sigs.k8s.io/yaml" - - "helm.sh/helm/v3/internal/test/ensure" - "helm.sh/helm/v3/pkg/repo" -) - -// Young'n, in these here parts, we test our tests. - -func TestServer(t *testing.T) { - defer ensure.HelmHome(t)() - - rootDir := ensure.TempDir(t) - - srv := NewServer(rootDir) - defer srv.Stop() - - c, err := srv.CopyCharts("testdata/*.tgz") - if err != nil { - // Some versions of Go don't correctly fire defer on Fatal. - t.Fatal(err) - } - - if len(c) != 1 { - t.Errorf("Unexpected chart count: %d", len(c)) - } - - if filepath.Base(c[0]) != "examplechart-0.1.0.tgz" { - t.Errorf("Unexpected chart: %s", c[0]) - } - - res, err := http.Get(srv.URL() + "/examplechart-0.1.0.tgz") - res.Body.Close() - if err != nil { - t.Fatal(err) - } - - if res.ContentLength < 500 { - t.Errorf("Expected at least 500 bytes of data, got %d", res.ContentLength) - } - - res, err = http.Get(srv.URL() + "/index.yaml") - if err != nil { - t.Fatal(err) - } - - data, err := ioutil.ReadAll(res.Body) - res.Body.Close() - if err != nil { - t.Fatal(err) - } - - m := repo.NewIndexFile() - if err := yaml.Unmarshal(data, m); err != nil { - t.Fatal(err) - } - - if l := len(m.Entries); l != 1 { - t.Fatalf("Expected 1 entry, got %d", l) - } - - expect := "examplechart" - if !m.Has(expect, "0.1.0") { - t.Errorf("missing %q", expect) - } - - res, err = http.Get(srv.URL() + "/index.yaml-nosuchthing") - res.Body.Close() - if err != nil { - t.Fatal(err) - } - if res.StatusCode != 404 { - t.Fatalf("Expected 404, got %d", res.StatusCode) - } -} - -func TestNewTempServer(t *testing.T) { - defer ensure.HelmHome(t)() - - srv, err := NewTempServer("testdata/examplechart-0.1.0.tgz") - if err != nil { - t.Fatal(err) - } - defer srv.Stop() - - res, err := http.Head(srv.URL() + "/examplechart-0.1.0.tgz") - res.Body.Close() - if err != nil { - t.Error(err) - } - if res.StatusCode != 200 { - t.Errorf("Expected 200, got %d", res.StatusCode) - } -} diff --git a/pkg/repo/repotest/testdata/examplechart-0.1.0.tgz b/pkg/repo/repotest/testdata/examplechart-0.1.0.tgz deleted file mode 100644 index c5ea741..0000000 Binary files a/pkg/repo/repotest/testdata/examplechart-0.1.0.tgz and /dev/null differ diff --git a/pkg/repo/repotest/testdata/examplechart/.helmignore b/pkg/repo/repotest/testdata/examplechart/.helmignore deleted file mode 100644 index f0c1319..0000000 --- a/pkg/repo/repotest/testdata/examplechart/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/pkg/repo/repotest/testdata/examplechart/Chart.yaml b/pkg/repo/repotest/testdata/examplechart/Chart.yaml deleted file mode 100644 index a7d2972..0000000 --- a/pkg/repo/repotest/testdata/examplechart/Chart.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -description: A Helm chart for Kubernetes -name: examplechart -version: 0.1.0 diff --git a/pkg/repo/repotest/testdata/examplechart/values.yaml b/pkg/repo/repotest/testdata/examplechart/values.yaml deleted file mode 100644 index 5170c61..0000000 --- a/pkg/repo/repotest/testdata/examplechart/values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Default values for examplechart. -# This is a YAML-formatted file. -# Declare name/value pairs to be passed into your templates. -# name: value diff --git a/pkg/repo/testdata/local-index-unordered.yaml b/pkg/repo/testdata/local-index-unordered.yaml deleted file mode 100644 index 7482baa..0000000 --- a/pkg/repo/testdata/local-index-unordered.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: v1 -entries: - nginx: - - urls: - - https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz - name: nginx - description: string - version: 0.1.0 - home: https://github.com/something - digest: "sha256:1234567890abcdef" - keywords: - - popular - - web server - - proxy - - urls: - - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz - name: nginx - description: string - version: 0.2.0 - home: https://github.com/something/else - digest: "sha256:1234567890abcdef" - keywords: - - popular - - web server - - proxy - alpine: - - urls: - - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz - - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz - name: alpine - description: string - version: 1.0.0 - home: https://github.com/something - keywords: - - linux - - alpine - - small - - sumtin - digest: "sha256:1234567890abcdef" - chartWithNoURL: - - name: chartWithNoURL - description: string - version: 1.0.0 - home: https://github.com/something - keywords: - - small - - sumtin - digest: "sha256:1234567890abcdef" diff --git a/pkg/repo/testdata/local-index.yaml b/pkg/repo/testdata/local-index.yaml deleted file mode 100644 index e680d2a..0000000 --- a/pkg/repo/testdata/local-index.yaml +++ /dev/null @@ -1,48 +0,0 @@ -apiVersion: v1 -entries: - nginx: - - urls: - - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz - name: nginx - description: string - version: 0.2.0 - home: https://github.com/something/else - digest: "sha256:1234567890abcdef" - keywords: - - popular - - web server - - proxy - - urls: - - https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz - name: nginx - description: string - version: 0.1.0 - home: https://github.com/something - digest: "sha256:1234567890abcdef" - keywords: - - popular - - web server - - proxy - alpine: - - urls: - - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz - - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz - name: alpine - description: string - version: 1.0.0 - home: https://github.com/something - keywords: - - linux - - alpine - - small - - sumtin - digest: "sha256:1234567890abcdef" - chartWithNoURL: - - name: chartWithNoURL - description: string - version: 1.0.0 - home: https://github.com/something - keywords: - - small - - sumtin - digest: "sha256:1234567890abcdef" diff --git a/pkg/repo/testdata/old-repositories.yaml b/pkg/repo/testdata/old-repositories.yaml deleted file mode 100644 index 3fb55b0..0000000 --- a/pkg/repo/testdata/old-repositories.yaml +++ /dev/null @@ -1,3 +0,0 @@ -best-charts-ever: http://best-charts-ever.com -okay-charts: http://okay-charts.org -example123: http://examplecharts.net/charts/123 diff --git a/pkg/repo/testdata/repositories.yaml b/pkg/repo/testdata/repositories.yaml deleted file mode 100644 index a28c48e..0000000 --- a/pkg/repo/testdata/repositories.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -repositories: - - name: stable - url: https://example.com/stable/charts - cache: stable-index.yaml - - name: incubator - url: https://example.com/incubator - cache: incubator-index.yaml diff --git a/pkg/repo/testdata/repository/frobnitz-1.2.3.tgz b/pkg/repo/testdata/repository/frobnitz-1.2.3.tgz deleted file mode 100644 index 8731dce..0000000 Binary files a/pkg/repo/testdata/repository/frobnitz-1.2.3.tgz and /dev/null differ diff --git a/pkg/repo/testdata/repository/sprocket-1.1.0.tgz b/pkg/repo/testdata/repository/sprocket-1.1.0.tgz deleted file mode 100644 index 48d65f4..0000000 Binary files a/pkg/repo/testdata/repository/sprocket-1.1.0.tgz and /dev/null differ diff --git a/pkg/repo/testdata/repository/sprocket-1.2.0.tgz b/pkg/repo/testdata/repository/sprocket-1.2.0.tgz deleted file mode 100644 index 6fdc73c..0000000 Binary files a/pkg/repo/testdata/repository/sprocket-1.2.0.tgz and /dev/null differ diff --git a/pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz b/pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz deleted file mode 100644 index 6f1e856..0000000 Binary files a/pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz and /dev/null differ diff --git a/pkg/repo/testdata/server/index.yaml b/pkg/repo/testdata/server/index.yaml deleted file mode 100644 index ec529f1..0000000 --- a/pkg/repo/testdata/server/index.yaml +++ /dev/null @@ -1,39 +0,0 @@ -apiVersion: v1 -entries: - nginx: - - urls: - - https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz - name: nginx - description: string - version: 0.1.0 - home: https://github.com/something - digest: "sha256:1234567890abcdef" - keywords: - - popular - - web server - - proxy - - urls: - - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz - name: nginx - description: string - version: 0.2.0 - home: https://github.com/something/else - digest: "sha256:1234567890abcdef" - keywords: - - popular - - web server - - proxy - alpine: - - urls: - - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz - - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz - name: alpine - description: string - version: 1.0.0 - home: https://github.com/something - keywords: - - linux - - alpine - - small - - sumtin - digest: "sha256:1234567890abcdef" diff --git a/pkg/repo/testdata/server/test.txt b/pkg/repo/testdata/server/test.txt deleted file mode 100644 index 557db03..0000000 --- a/pkg/repo/testdata/server/test.txt +++ /dev/null @@ -1 +0,0 @@ -Hello World diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go deleted file mode 100644 index cc2e241..0000000 --- a/pkg/storage/driver/cfgmaps.go +++ /dev/null @@ -1,258 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver // import "helm.sh/helm/v3/pkg/storage/driver" - -import ( - "strconv" - "strings" - "time" - - "github.com/pkg/errors" - v1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kblabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/validation" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - - rspb "helm.sh/helm/v3/pkg/release" -) - -var _ Driver = (*ConfigMaps)(nil) - -// ConfigMapsDriverName is the string name of the driver. -const ConfigMapsDriverName = "ConfigMap" - -// ConfigMaps is a wrapper around an implementation of a kubernetes -// ConfigMapsInterface. -type ConfigMaps struct { - impl corev1.ConfigMapInterface - Log func(string, ...interface{}) -} - -// NewConfigMaps initializes a new ConfigMaps wrapping an implementation of -// the kubernetes ConfigMapsInterface. -func NewConfigMaps(impl corev1.ConfigMapInterface) *ConfigMaps { - return &ConfigMaps{ - impl: impl, - Log: func(_ string, _ ...interface{}) {}, - } -} - -// Name returns the name of the driver. -func (cfgmaps *ConfigMaps) Name() string { - return ConfigMapsDriverName -} - -// Get fetches the release named by key. The corresponding release is returned -// or error if not found. -func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) { - // fetch the configmap holding the release named by key - obj, err := cfgmaps.impl.Get(key, metav1.GetOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - return nil, ErrReleaseNotFound - } - - cfgmaps.Log("get: failed to get %q: %s", key, err) - return nil, err - } - // found the configmap, decode the base64 data string - r, err := decodeRelease(obj.Data["release"]) - if err != nil { - cfgmaps.Log("get: failed to decode data %q: %s", key, err) - return nil, err - } - // return the release object - return r, nil -} - -// List fetches all releases and returns the list releases such -// that filter(release) == true. An error is returned if the -// configmap fails to retrieve the releases. -func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { - lsel := kblabels.Set{"owner": "helm"}.AsSelector() - opts := metav1.ListOptions{LabelSelector: lsel.String()} - - list, err := cfgmaps.impl.List(opts) - if err != nil { - cfgmaps.Log("list: failed to list: %s", err) - return nil, err - } - - var results []*rspb.Release - - // iterate over the configmaps object list - // and decode each release - for _, item := range list.Items { - rls, err := decodeRelease(item.Data["release"]) - if err != nil { - cfgmaps.Log("list: failed to decode release: %v: %s", item, err) - continue - } - if filter(rls) { - results = append(results, rls) - } - } - return results, nil -} - -// Query fetches all releases that match the provided map of labels. -// An error is returned if the configmap fails to retrieve the releases. -func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, error) { - ls := kblabels.Set{} - for k, v := range labels { - if errs := validation.IsValidLabelValue(v); len(errs) != 0 { - return nil, errors.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; ")) - } - ls[k] = v - } - - opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()} - - list, err := cfgmaps.impl.List(opts) - if err != nil { - cfgmaps.Log("query: failed to query with labels: %s", err) - return nil, err - } - - if len(list.Items) == 0 { - return nil, ErrReleaseNotFound - } - - var results []*rspb.Release - for _, item := range list.Items { - rls, err := decodeRelease(item.Data["release"]) - if err != nil { - cfgmaps.Log("query: failed to decode release: %s", err) - continue - } - results = append(results, rls) - } - return results, nil -} - -// Create creates a new ConfigMap holding the release. If the -// ConfigMap already exists, ErrReleaseExists is returned. -func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error { - // set labels for configmaps object meta data - var lbs labels - - lbs.init() - lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix()))) - - // create a new configmap to hold the release - obj, err := newConfigMapsObject(key, rls, lbs) - if err != nil { - cfgmaps.Log("create: failed to encode release %q: %s", rls.Name, err) - return err - } - // push the configmap object out into the kubiverse - if _, err := cfgmaps.impl.Create(obj); err != nil { - if apierrors.IsAlreadyExists(err) { - return ErrReleaseExists - } - - cfgmaps.Log("create: failed to create: %s", err) - return err - } - return nil -} - -// Update updates the ConfigMap holding the release. If not found -// the ConfigMap is created to hold the release. -func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error { - // set labels for configmaps object meta data - var lbs labels - - lbs.init() - lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix()))) - - // create a new configmap object to hold the release - obj, err := newConfigMapsObject(key, rls, lbs) - if err != nil { - cfgmaps.Log("update: failed to encode release %q: %s", rls.Name, err) - return err - } - // push the configmap object out into the kubiverse - _, err = cfgmaps.impl.Update(obj) - if err != nil { - cfgmaps.Log("update: failed to update: %s", err) - return err - } - return nil -} - -// Delete deletes the ConfigMap holding the release named by key. -func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) { - // fetch the release to check existence - if rls, err = cfgmaps.Get(key); err != nil { - if apierrors.IsNotFound(err) { - return nil, ErrReleaseExists - } - - cfgmaps.Log("delete: failed to get release %q: %s", key, err) - return nil, err - } - // delete the release - if err = cfgmaps.impl.Delete(key, &metav1.DeleteOptions{}); err != nil { - return rls, err - } - return rls, nil -} - -// newConfigMapsObject constructs a kubernetes ConfigMap object -// to store a release. Each configmap data entry is the base64 -// encoded gzipped string of a release. -// -// The following labels are used within each configmap: -// -// "modifiedAt" - timestamp indicating when this configmap was last modified. (set in Update) -// "createdAt" - timestamp indicating when this configmap was created. (set in Create) -// "version" - version of the release. -// "status" - status of the release (see pkg/release/status.go for variants) -// "owner" - owner of the configmap, currently "helm". -// "name" - name of the release. -// -func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*v1.ConfigMap, error) { - const owner = "helm" - - // encode the release - s, err := encodeRelease(rls) - if err != nil { - return nil, err - } - - if lbs == nil { - lbs.init() - } - - // apply labels - lbs.set("name", rls.Name) - lbs.set("owner", owner) - lbs.set("status", rls.Info.Status.String()) - lbs.set("version", strconv.Itoa(rls.Version)) - - // create and return configmap object - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: key, - Labels: lbs.toMap(), - }, - Data: map[string]string{"release": s}, - }, nil -} diff --git a/pkg/storage/driver/cfgmaps_test.go b/pkg/storage/driver/cfgmaps_test.go deleted file mode 100644 index 2aa38f2..0000000 --- a/pkg/storage/driver/cfgmaps_test.go +++ /dev/null @@ -1,186 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver - -import ( - "encoding/base64" - "encoding/json" - "reflect" - "testing" - - v1 "k8s.io/api/core/v1" - - rspb "helm.sh/helm/v3/pkg/release" -) - -func TestConfigMapName(t *testing.T) { - c := newTestFixtureCfgMaps(t) - if c.Name() != ConfigMapsDriverName { - t.Errorf("Expected name to be %q, got %q", ConfigMapsDriverName, c.Name()) - } -} - -func TestConfigMapGet(t *testing.T) { - vers := 1 - name := "smug-pigeon" - namespace := "default" - key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) - - cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{rel}...) - - // get release with key - got, err := cfgmaps.Get(key) - if err != nil { - t.Fatalf("Failed to get release: %s", err) - } - // compare fetched release with original - if !reflect.DeepEqual(rel, got) { - t.Errorf("Expected {%v}, got {%v}", rel, got) - } -} - -func TestUncompressedConfigMapGet(t *testing.T) { - vers := 1 - name := "smug-pigeon" - namespace := "default" - key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) - - // Create a test fixture which contains an uncompressed release - cfgmap, err := newConfigMapsObject(key, rel, nil) - if err != nil { - t.Fatalf("Failed to create configmap: %s", err) - } - b, err := json.Marshal(rel) - if err != nil { - t.Fatalf("Failed to marshal release: %s", err) - } - cfgmap.Data["release"] = base64.StdEncoding.EncodeToString(b) - var mock MockConfigMapsInterface - mock.objects = map[string]*v1.ConfigMap{key: cfgmap} - cfgmaps := NewConfigMaps(&mock) - - // get release with key - got, err := cfgmaps.Get(key) - if err != nil { - t.Fatalf("Failed to get release: %s", err) - } - // compare fetched release with original - if !reflect.DeepEqual(rel, got) { - t.Errorf("Expected {%v}, got {%v}", rel, got) - } -} - -func TestConfigMapList(t *testing.T) { - cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{ - releaseStub("key-1", 1, "default", rspb.StatusUninstalled), - releaseStub("key-2", 1, "default", rspb.StatusUninstalled), - releaseStub("key-3", 1, "default", rspb.StatusDeployed), - releaseStub("key-4", 1, "default", rspb.StatusDeployed), - releaseStub("key-5", 1, "default", rspb.StatusSuperseded), - releaseStub("key-6", 1, "default", rspb.StatusSuperseded), - }...) - - // list all deleted releases - del, err := cfgmaps.List(func(rel *rspb.Release) bool { - return rel.Info.Status == rspb.StatusUninstalled - }) - // check - if err != nil { - t.Errorf("Failed to list deleted: %s", err) - } - if len(del) != 2 { - t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del) - } - - // list all deployed releases - dpl, err := cfgmaps.List(func(rel *rspb.Release) bool { - return rel.Info.Status == rspb.StatusDeployed - }) - // check - if err != nil { - t.Errorf("Failed to list deployed: %s", err) - } - if len(dpl) != 2 { - t.Errorf("Expected 2 deployed, got %d", len(dpl)) - } - - // list all superseded releases - ssd, err := cfgmaps.List(func(rel *rspb.Release) bool { - return rel.Info.Status == rspb.StatusSuperseded - }) - // check - if err != nil { - t.Errorf("Failed to list superseded: %s", err) - } - if len(ssd) != 2 { - t.Errorf("Expected 2 superseded, got %d", len(ssd)) - } -} - -func TestConfigMapCreate(t *testing.T) { - cfgmaps := newTestFixtureCfgMaps(t) - - vers := 1 - name := "smug-pigeon" - namespace := "default" - key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) - - // store the release in a configmap - if err := cfgmaps.Create(key, rel); err != nil { - t.Fatalf("Failed to create release with key %q: %s", key, err) - } - - // get the release back - got, err := cfgmaps.Get(key) - if err != nil { - t.Fatalf("Failed to get release with key %q: %s", key, err) - } - - // compare created release with original - if !reflect.DeepEqual(rel, got) { - t.Errorf("Expected {%v}, got {%v}", rel, got) - } -} - -func TestConfigMapUpdate(t *testing.T) { - vers := 1 - name := "smug-pigeon" - namespace := "default" - key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) - - cfgmaps := newTestFixtureCfgMaps(t, []*rspb.Release{rel}...) - - // modify release status code - rel.Info.Status = rspb.StatusSuperseded - - // perform the update - if err := cfgmaps.Update(key, rel); err != nil { - t.Fatalf("Failed to update release: %s", err) - } - - // fetch the updated release - got, err := cfgmaps.Get(key) - if err != nil { - t.Fatalf("Failed to get release with key %q: %s", key, err) - } - - // check release has actually been updated by comparing modified fields - if rel.Info.Status != got.Info.Status { - t.Errorf("Expected status %s, got status %s", rel.Info.Status.String(), got.Info.Status.String()) - } -} diff --git a/pkg/storage/driver/driver.go b/pkg/storage/driver/driver.go deleted file mode 100644 index 9a1fbc5..0000000 --- a/pkg/storage/driver/driver.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver // import "helm.sh/helm/v3/pkg/storage/driver" - -import ( - "github.com/pkg/errors" - - rspb "helm.sh/helm/v3/pkg/release" -) - -var ( - // ErrReleaseNotFound indicates that a release is not found. - ErrReleaseNotFound = errors.New("release: not found") - // ErrReleaseExists indicates that a release already exists. - ErrReleaseExists = errors.New("release: already exists") - // ErrInvalidKey indicates that a release key could not be parsed. - ErrInvalidKey = errors.Errorf("release: invalid key") -) - -// Creator is the interface that wraps the Create method. -// -// Create stores the release or returns ErrReleaseExists -// if an identical release already exists. -type Creator interface { - Create(key string, rls *rspb.Release) error -} - -// Updator is the interface that wraps the Update method. -// -// Update updates an existing release or returns -// ErrReleaseNotFound if the release does not exist. -type Updator interface { - Update(key string, rls *rspb.Release) error -} - -// Deletor is the interface that wraps the Delete method. -// -// Delete deletes the release named by key or returns -// ErrReleaseNotFound if the release does not exist. -type Deletor interface { - Delete(key string) (*rspb.Release, error) -} - -// Queryor is the interface that wraps the Get and List methods. -// -// Get returns the release named by key or returns ErrReleaseNotFound -// if the release does not exist. -// -// List returns the set of all releases that satisfy the filter predicate. -// -// Query returns the set of all releases that match the provided label set. -type Queryor interface { - Get(key string) (*rspb.Release, error) - List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) - Query(labels map[string]string) ([]*rspb.Release, error) -} - -// Driver is the interface composed of Creator, Updator, Deletor, and Queryor -// interfaces. It defines the behavior for storing, updating, deleted, -// and retrieving Helm releases from some underlying storage mechanism, -// e.g. memory, configmaps. -type Driver interface { - Creator - Updator - Deletor - Queryor - Name() string -} diff --git a/pkg/storage/driver/labels.go b/pkg/storage/driver/labels.go deleted file mode 100644 index eb7118f..0000000 --- a/pkg/storage/driver/labels.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver - -// labels is a map of key value pairs to be included as metadata in a configmap object. -type labels map[string]string - -func (lbs *labels) init() { *lbs = labels(make(map[string]string)) } -func (lbs labels) get(key string) string { return lbs[key] } -func (lbs labels) set(key, val string) { lbs[key] = val } - -func (lbs labels) keys() (ls []string) { - for key := range lbs { - ls = append(ls, key) - } - return -} - -func (lbs labels) match(set labels) bool { - for _, key := range set.keys() { - if lbs.get(key) != set.get(key) { - return false - } - } - return true -} - -func (lbs labels) toMap() map[string]string { return lbs } - -func (lbs *labels) fromMap(kvs map[string]string) { - for k, v := range kvs { - lbs.set(k, v) - } -} diff --git a/pkg/storage/driver/labels_test.go b/pkg/storage/driver/labels_test.go deleted file mode 100644 index bfd8091..0000000 --- a/pkg/storage/driver/labels_test.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver // import "helm.sh/helm/v3/pkg/storage/driver" - -import ( - "testing" -) - -func TestLabelsMatch(t *testing.T) { - var tests = []struct { - desc string - set1 labels - set2 labels - expect bool - }{ - { - "equal labels sets", - labels(map[string]string{"KEY_A": "VAL_A", "KEY_B": "VAL_B"}), - labels(map[string]string{"KEY_A": "VAL_A", "KEY_B": "VAL_B"}), - true, - }, - { - "disjoint label sets", - labels(map[string]string{"KEY_C": "VAL_C", "KEY_D": "VAL_D"}), - labels(map[string]string{"KEY_A": "VAL_A", "KEY_B": "VAL_B"}), - false, - }, - } - - for _, tt := range tests { - if !tt.set1.match(tt.set2) && tt.expect { - t.Fatalf("Expected match '%s'\n", tt.desc) - } - } -} diff --git a/pkg/storage/driver/memory.go b/pkg/storage/driver/memory.go deleted file mode 100644 index ca8756c..0000000 --- a/pkg/storage/driver/memory.go +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver - -import ( - "strconv" - "strings" - "sync" - - rspb "helm.sh/helm/v3/pkg/release" -) - -var _ Driver = (*Memory)(nil) - -// MemoryDriverName is the string name of this driver. -const MemoryDriverName = "Memory" - -// Memory is the in-memory storage driver implementation. -type Memory struct { - sync.RWMutex - cache map[string]records -} - -// NewMemory initializes a new memory driver. -func NewMemory() *Memory { - return &Memory{cache: map[string]records{}} -} - -// Name returns the name of the driver. -func (mem *Memory) Name() string { - return MemoryDriverName -} - -// Get returns the release named by key or returns ErrReleaseNotFound. -func (mem *Memory) Get(key string) (*rspb.Release, error) { - defer unlock(mem.rlock()) - - keyWithoutPrefix := strings.TrimPrefix(key, "sh.helm.release.v1.") - switch elems := strings.Split(keyWithoutPrefix, ".v"); len(elems) { - case 2: - name, ver := elems[0], elems[1] - if _, err := strconv.Atoi(ver); err != nil { - return nil, ErrInvalidKey - } - if recs, ok := mem.cache[name]; ok { - if r := recs.Get(key); r != nil { - return r.rls, nil - } - } - return nil, ErrReleaseNotFound - default: - return nil, ErrInvalidKey - } -} - -// List returns the list of all releases such that filter(release) == true -func (mem *Memory) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { - defer unlock(mem.rlock()) - - var ls []*rspb.Release - for _, recs := range mem.cache { - recs.Iter(func(_ int, rec *record) bool { - if filter(rec.rls) { - ls = append(ls, rec.rls) - } - return true - }) - } - return ls, nil -} - -// Query returns the set of releases that match the provided set of labels -func (mem *Memory) Query(keyvals map[string]string) ([]*rspb.Release, error) { - defer unlock(mem.rlock()) - - var lbs labels - - lbs.init() - lbs.fromMap(keyvals) - - var ls []*rspb.Release - for _, recs := range mem.cache { - recs.Iter(func(_ int, rec *record) bool { - // A query for a release name that doesn't exist (has been deleted) - // can cause rec to be nil. - if rec == nil { - return false - } - if rec.lbs.match(lbs) { - ls = append(ls, rec.rls) - } - return true - }) - } - return ls, nil -} - -// Create creates a new release or returns ErrReleaseExists. -func (mem *Memory) Create(key string, rls *rspb.Release) error { - defer unlock(mem.wlock()) - - if recs, ok := mem.cache[rls.Name]; ok { - if err := recs.Add(newRecord(key, rls)); err != nil { - return err - } - mem.cache[rls.Name] = recs - return nil - } - mem.cache[rls.Name] = records{newRecord(key, rls)} - return nil -} - -// Update updates a release or returns ErrReleaseNotFound. -func (mem *Memory) Update(key string, rls *rspb.Release) error { - defer unlock(mem.wlock()) - - if rs, ok := mem.cache[rls.Name]; ok && rs.Exists(key) { - rs.Replace(key, newRecord(key, rls)) - return nil - } - return ErrReleaseNotFound -} - -// Delete deletes a release or returns ErrReleaseNotFound. -func (mem *Memory) Delete(key string) (*rspb.Release, error) { - defer unlock(mem.wlock()) - - keyWithoutPrefix := strings.TrimPrefix(key, "sh.helm.release.v1.") - elems := strings.Split(keyWithoutPrefix, ".v") - - if len(elems) != 2 { - return nil, ErrInvalidKey - } - - name, ver := elems[0], elems[1] - if _, err := strconv.Atoi(ver); err != nil { - return nil, ErrInvalidKey - } - if recs, ok := mem.cache[name]; ok { - if r := recs.Remove(key); r != nil { - // recs.Remove changes the slice reference, so we have to re-assign it. - mem.cache[name] = recs - return r.rls, nil - } - } - return nil, ErrReleaseNotFound -} - -// wlock locks mem for writing -func (mem *Memory) wlock() func() { - mem.Lock() - return func() { mem.Unlock() } -} - -// rlock locks mem for reading -func (mem *Memory) rlock() func() { - mem.RLock() - return func() { mem.RUnlock() } -} - -// unlock calls fn which reverses a mem.rlock or mem.wlock. e.g: -// ```defer unlock(mem.rlock())```, locks mem for reading at the -// call point of defer and unlocks upon exiting the block. -func unlock(fn func()) { fn() } diff --git a/pkg/storage/driver/memory_test.go b/pkg/storage/driver/memory_test.go deleted file mode 100644 index c74df94..0000000 --- a/pkg/storage/driver/memory_test.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver - -import ( - "fmt" - "reflect" - "testing" - - rspb "helm.sh/helm/v3/pkg/release" -) - -func TestMemoryName(t *testing.T) { - if mem := NewMemory(); mem.Name() != MemoryDriverName { - t.Errorf("Expected name to be %q, got %q", MemoryDriverName, mem.Name()) - } -} - -func TestMemoryCreate(t *testing.T) { - var tests = []struct { - desc string - rls *rspb.Release - err bool - }{ - { - "create should success", - releaseStub("rls-c", 1, "default", rspb.StatusDeployed), - false, - }, - { - "create should fail (release already exists)", - releaseStub("rls-a", 1, "default", rspb.StatusDeployed), - true, - }, - } - - ts := tsFixtureMemory(t) - for _, tt := range tests { - key := testKey(tt.rls.Name, tt.rls.Version) - rls := tt.rls - - if err := ts.Create(key, rls); err != nil { - if !tt.err { - t.Fatalf("failed to create %q: %s", tt.desc, err) - } - } - } -} - -func TestMemoryGet(t *testing.T) { - var tests = []struct { - desc string - key string - err bool - }{ - {"release key should exist", "rls-a.v1", false}, - {"release key should not exist", "rls-a.v5", true}, - } - - ts := tsFixtureMemory(t) - for _, tt := range tests { - if _, err := ts.Get(tt.key); err != nil { - if !tt.err { - t.Fatalf("Failed %q to get '%s': %q\n", tt.desc, tt.key, err) - } - } - } -} - -func TestMemoryQuery(t *testing.T) { - var tests = []struct { - desc string - xlen int - lbs map[string]string - }{ - { - "should be 2 query results", - 2, - map[string]string{"status": "deployed"}, - }, - } - - ts := tsFixtureMemory(t) - for _, tt := range tests { - l, err := ts.Query(tt.lbs) - if err != nil { - t.Fatalf("Failed to query: %s\n", err) - } - - if tt.xlen != len(l) { - t.Fatalf("Expected %d results, actual %d\n", tt.xlen, len(l)) - } - } -} - -func TestMemoryUpdate(t *testing.T) { - var tests = []struct { - desc string - key string - rls *rspb.Release - err bool - }{ - { - "update release status", - "rls-a.v4", - releaseStub("rls-a", 4, "default", rspb.StatusSuperseded), - false, - }, - { - "update release does not exist", - "rls-z.v1", - releaseStub("rls-z", 1, "default", rspb.StatusUninstalled), - true, - }, - } - - ts := tsFixtureMemory(t) - for _, tt := range tests { - if err := ts.Update(tt.key, tt.rls); err != nil { - if !tt.err { - t.Fatalf("Failed %q: %s\n", tt.desc, err) - } - continue - } - - r, err := ts.Get(tt.key) - if err != nil { - t.Fatalf("Failed to get: %s\n", err) - } - - if !reflect.DeepEqual(r, tt.rls) { - t.Fatalf("Expected %v, actual %v\n", tt.rls, r) - } - } -} - -func TestMemoryDelete(t *testing.T) { - var tests = []struct { - desc string - key string - err bool - }{ - {"release key should exist", "rls-a.v1", false}, - {"release key should not exist", "rls-a.v5", true}, - } - - ts := tsFixtureMemory(t) - start, err := ts.Query(map[string]string{"name": "rls-a"}) - if err != nil { - t.Errorf("Query failed: %s", err) - } - startLen := len(start) - for _, tt := range tests { - if rel, err := ts.Delete(tt.key); err != nil { - if !tt.err { - t.Fatalf("Failed %q to get '%s': %q\n", tt.desc, tt.key, err) - } - continue - } else if fmt.Sprintf("%s.v%d", rel.Name, rel.Version) != tt.key { - t.Fatalf("Asked for delete on %s, but deleted %d", tt.key, rel.Version) - } - _, err := ts.Get(tt.key) - if err == nil { - t.Errorf("Expected an error when asking for a deleted key") - } - } - - // Make sure that the deleted records are gone. - end, err := ts.Query(map[string]string{"name": "rls-a"}) - if err != nil { - t.Errorf("Query failed: %s", err) - } - endLen := len(end) - - if startLen <= endLen { - t.Errorf("expected start %d to be less than end %d", startLen, endLen) - for _, ee := range end { - t.Logf("Name: %s, Version: %d", ee.Name, ee.Version) - } - } - -} diff --git a/pkg/storage/driver/mock_test.go b/pkg/storage/driver/mock_test.go deleted file mode 100644 index 4c9b4ef..0000000 --- a/pkg/storage/driver/mock_test.go +++ /dev/null @@ -1,222 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver // import "helm.sh/helm/v3/pkg/storage/driver" - -import ( - "fmt" - "testing" - - v1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - - rspb "helm.sh/helm/v3/pkg/release" -) - -func releaseStub(name string, vers int, namespace string, status rspb.Status) *rspb.Release { - return &rspb.Release{ - Name: name, - Version: vers, - Namespace: namespace, - Info: &rspb.Info{Status: status}, - } -} - -func testKey(name string, vers int) string { - return fmt.Sprintf("%s.v%d", name, vers) -} - -func tsFixtureMemory(t *testing.T) *Memory { - hs := []*rspb.Release{ - // rls-a - releaseStub("rls-a", 4, "default", rspb.StatusDeployed), - releaseStub("rls-a", 1, "default", rspb.StatusSuperseded), - releaseStub("rls-a", 3, "default", rspb.StatusSuperseded), - releaseStub("rls-a", 2, "default", rspb.StatusSuperseded), - // rls-b - releaseStub("rls-b", 4, "default", rspb.StatusDeployed), - releaseStub("rls-b", 1, "default", rspb.StatusSuperseded), - releaseStub("rls-b", 3, "default", rspb.StatusSuperseded), - releaseStub("rls-b", 2, "default", rspb.StatusSuperseded), - } - - mem := NewMemory() - for _, tt := range hs { - err := mem.Create(testKey(tt.Name, tt.Version), tt) - if err != nil { - t.Fatalf("Test setup failed to create: %s\n", err) - } - } - return mem -} - -// newTestFixture initializes a MockConfigMapsInterface. -// ConfigMaps are created for each release provided. -func newTestFixtureCfgMaps(t *testing.T, releases ...*rspb.Release) *ConfigMaps { - var mock MockConfigMapsInterface - mock.Init(t, releases...) - - return NewConfigMaps(&mock) -} - -// MockConfigMapsInterface mocks a kubernetes ConfigMapsInterface -type MockConfigMapsInterface struct { - corev1.ConfigMapInterface - - objects map[string]*v1.ConfigMap -} - -// Init initializes the MockConfigMapsInterface with the set of releases. -func (mock *MockConfigMapsInterface) Init(t *testing.T, releases ...*rspb.Release) { - mock.objects = map[string]*v1.ConfigMap{} - - for _, rls := range releases { - objkey := testKey(rls.Name, rls.Version) - - cfgmap, err := newConfigMapsObject(objkey, rls, nil) - if err != nil { - t.Fatalf("Failed to create configmap: %s", err) - } - mock.objects[objkey] = cfgmap - } -} - -// Get returns the ConfigMap by name. -func (mock *MockConfigMapsInterface) Get(name string, options metav1.GetOptions) (*v1.ConfigMap, error) { - object, ok := mock.objects[name] - if !ok { - return nil, apierrors.NewNotFound(v1.Resource("tests"), name) - } - return object, nil -} - -// List returns the a of ConfigMaps. -func (mock *MockConfigMapsInterface) List(opts metav1.ListOptions) (*v1.ConfigMapList, error) { - var list v1.ConfigMapList - for _, cfgmap := range mock.objects { - list.Items = append(list.Items, *cfgmap) - } - return &list, nil -} - -// Create creates a new ConfigMap. -func (mock *MockConfigMapsInterface) Create(cfgmap *v1.ConfigMap) (*v1.ConfigMap, error) { - name := cfgmap.ObjectMeta.Name - if object, ok := mock.objects[name]; ok { - return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name) - } - mock.objects[name] = cfgmap - return cfgmap, nil -} - -// Update updates a ConfigMap. -func (mock *MockConfigMapsInterface) Update(cfgmap *v1.ConfigMap) (*v1.ConfigMap, error) { - name := cfgmap.ObjectMeta.Name - if _, ok := mock.objects[name]; !ok { - return nil, apierrors.NewNotFound(v1.Resource("tests"), name) - } - mock.objects[name] = cfgmap - return cfgmap, nil -} - -// Delete deletes a ConfigMap by name. -func (mock *MockConfigMapsInterface) Delete(name string, opts *metav1.DeleteOptions) error { - if _, ok := mock.objects[name]; !ok { - return apierrors.NewNotFound(v1.Resource("tests"), name) - } - delete(mock.objects, name) - return nil -} - -// newTestFixture initializes a MockSecretsInterface. -// Secrets are created for each release provided. -func newTestFixtureSecrets(t *testing.T, releases ...*rspb.Release) *Secrets { - var mock MockSecretsInterface - mock.Init(t, releases...) - - return NewSecrets(&mock) -} - -// MockSecretsInterface mocks a kubernetes SecretsInterface -type MockSecretsInterface struct { - corev1.SecretInterface - - objects map[string]*v1.Secret -} - -// Init initializes the MockSecretsInterface with the set of releases. -func (mock *MockSecretsInterface) Init(t *testing.T, releases ...*rspb.Release) { - mock.objects = map[string]*v1.Secret{} - - for _, rls := range releases { - objkey := testKey(rls.Name, rls.Version) - - secret, err := newSecretsObject(objkey, rls, nil) - if err != nil { - t.Fatalf("Failed to create secret: %s", err) - } - mock.objects[objkey] = secret - } -} - -// Get returns the Secret by name. -func (mock *MockSecretsInterface) Get(name string, options metav1.GetOptions) (*v1.Secret, error) { - object, ok := mock.objects[name] - if !ok { - return nil, apierrors.NewNotFound(v1.Resource("tests"), name) - } - return object, nil -} - -// List returns the a of Secret. -func (mock *MockSecretsInterface) List(opts metav1.ListOptions) (*v1.SecretList, error) { - var list v1.SecretList - for _, secret := range mock.objects { - list.Items = append(list.Items, *secret) - } - return &list, nil -} - -// Create creates a new Secret. -func (mock *MockSecretsInterface) Create(secret *v1.Secret) (*v1.Secret, error) { - name := secret.ObjectMeta.Name - if object, ok := mock.objects[name]; ok { - return object, apierrors.NewAlreadyExists(v1.Resource("tests"), name) - } - mock.objects[name] = secret - return secret, nil -} - -// Update updates a Secret. -func (mock *MockSecretsInterface) Update(secret *v1.Secret) (*v1.Secret, error) { - name := secret.ObjectMeta.Name - if _, ok := mock.objects[name]; !ok { - return nil, apierrors.NewNotFound(v1.Resource("tests"), name) - } - mock.objects[name] = secret - return secret, nil -} - -// Delete deletes a Secret by name. -func (mock *MockSecretsInterface) Delete(name string, opts *metav1.DeleteOptions) error { - if _, ok := mock.objects[name]; !ok { - return apierrors.NewNotFound(v1.Resource("tests"), name) - } - delete(mock.objects, name) - return nil -} diff --git a/pkg/storage/driver/records.go b/pkg/storage/driver/records.go deleted file mode 100644 index 9df1733..0000000 --- a/pkg/storage/driver/records.go +++ /dev/null @@ -1,124 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver // import "helm.sh/helm/v3/pkg/storage/driver" - -import ( - "sort" - "strconv" - - rspb "helm.sh/helm/v3/pkg/release" -) - -// records holds a list of in-memory release records -type records []*record - -func (rs records) Len() int { return len(rs) } -func (rs records) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] } -func (rs records) Less(i, j int) bool { return rs[i].rls.Version < rs[j].rls.Version } - -func (rs *records) Add(r *record) error { - if r == nil { - return nil - } - - if rs.Exists(r.key) { - return ErrReleaseExists - } - - *rs = append(*rs, r) - sort.Sort(*rs) - - return nil -} - -func (rs records) Get(key string) *record { - if i, ok := rs.Index(key); ok { - return rs[i] - } - return nil -} - -func (rs *records) Iter(fn func(int, *record) bool) { - cp := make([]*record, len(*rs)) - copy(cp, *rs) - - for i, r := range cp { - if !fn(i, r) { - return - } - } -} - -func (rs *records) Index(key string) (int, bool) { - for i, r := range *rs { - if r.key == key { - return i, true - } - } - return -1, false -} - -func (rs records) Exists(key string) bool { - _, ok := rs.Index(key) - return ok -} - -func (rs *records) Remove(key string) (r *record) { - if i, ok := rs.Index(key); ok { - return rs.removeAt(i) - } - return nil -} - -func (rs *records) Replace(key string, rec *record) *record { - if i, ok := rs.Index(key); ok { - old := (*rs)[i] - (*rs)[i] = rec - return old - } - return nil -} - -func (rs *records) removeAt(index int) *record { - r := (*rs)[index] - (*rs)[index] = nil - copy((*rs)[index:], (*rs)[index+1:]) - *rs = (*rs)[:len(*rs)-1] - return r -} - -// record is the data structure used to cache releases -// for the in-memory storage driver -type record struct { - key string - lbs labels - rls *rspb.Release -} - -// newRecord creates a new in-memory release record -func newRecord(key string, rls *rspb.Release) *record { - var lbs labels - - lbs.init() - lbs.set("name", rls.Name) - lbs.set("owner", "helm") - lbs.set("status", rls.Info.Status.String()) - lbs.set("version", strconv.Itoa(rls.Version)) - - // return &record{key: key, lbs: lbs, rls: proto.Clone(rls).(*rspb.Release)} - return &record{key: key, lbs: lbs, rls: rls} -} diff --git a/pkg/storage/driver/records_test.go b/pkg/storage/driver/records_test.go deleted file mode 100644 index 79b6004..0000000 --- a/pkg/storage/driver/records_test.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver // import "helm.sh/helm/v3/pkg/storage/driver" - -import ( - "testing" - - rspb "helm.sh/helm/v3/pkg/release" -) - -func TestRecordsAdd(t *testing.T) { - rs := records([]*record{ - newRecord("rls-a.v1", releaseStub("rls-a", 1, "default", rspb.StatusSuperseded)), - newRecord("rls-a.v2", releaseStub("rls-a", 2, "default", rspb.StatusDeployed)), - }) - - var tests = []struct { - desc string - key string - ok bool - rec *record - }{ - { - "add valid key", - "rls-a.v3", - false, - newRecord("rls-a.v3", releaseStub("rls-a", 3, "default", rspb.StatusSuperseded)), - }, - { - "add already existing key", - "rls-a.v1", - true, - newRecord("rls-a.v1", releaseStub("rls-a", 1, "default", rspb.StatusDeployed)), - }, - } - - for _, tt := range tests { - if err := rs.Add(tt.rec); err != nil { - if !tt.ok { - t.Fatalf("failed: %q: %s\n", tt.desc, err) - } - } - } -} - -func TestRecordsRemove(t *testing.T) { - var tests = []struct { - desc string - key string - ok bool - }{ - {"remove valid key", "rls-a.v1", false}, - {"remove invalid key", "rls-a.v", true}, - {"remove non-existent key", "rls-z.v1", true}, - } - - rs := records([]*record{ - newRecord("rls-a.v1", releaseStub("rls-a", 1, "default", rspb.StatusSuperseded)), - newRecord("rls-a.v2", releaseStub("rls-a", 2, "default", rspb.StatusDeployed)), - }) - - startLen := rs.Len() - - for _, tt := range tests { - if r := rs.Remove(tt.key); r == nil { - if !tt.ok { - t.Fatalf("Failed to %q (key = %s). Expected nil, got %v", - tt.desc, - tt.key, - r, - ) - } - } - } - - // We expect the total number of records will be less now than there were - // when we started. - endLen := rs.Len() - if endLen >= startLen { - t.Errorf("expected ending length %d to be less than starting length %d", endLen, startLen) - } -} - -func TestRecordsRemoveAt(t *testing.T) { - rs := records([]*record{ - newRecord("rls-a.v1", releaseStub("rls-a", 1, "default", rspb.StatusSuperseded)), - newRecord("rls-a.v2", releaseStub("rls-a", 2, "default", rspb.StatusDeployed)), - }) - - if len(rs) != 2 { - t.Fatal("Expected len=2 for mock") - } - - rs.Remove("rls-a.v1") - if len(rs) != 1 { - t.Fatalf("Expected length of rs to be 1, got %d", len(rs)) - } -} diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go deleted file mode 100644 index dcb2ecf..0000000 --- a/pkg/storage/driver/secrets.go +++ /dev/null @@ -1,250 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver // import "helm.sh/helm/v3/pkg/storage/driver" - -import ( - "strconv" - "strings" - "time" - - "github.com/pkg/errors" - v1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kblabels "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/util/validation" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - - rspb "helm.sh/helm/v3/pkg/release" -) - -var _ Driver = (*Secrets)(nil) - -// SecretsDriverName is the string name of the driver. -const SecretsDriverName = "Secret" - -// Secrets is a wrapper around an implementation of a kubernetes -// SecretsInterface. -type Secrets struct { - impl corev1.SecretInterface - Log func(string, ...interface{}) -} - -// NewSecrets initializes a new Secrets wrapping an implementation of -// the kubernetes SecretsInterface. -func NewSecrets(impl corev1.SecretInterface) *Secrets { - return &Secrets{ - impl: impl, - Log: func(_ string, _ ...interface{}) {}, - } -} - -// Name returns the name of the driver. -func (secrets *Secrets) Name() string { - return SecretsDriverName -} - -// Get fetches the release named by key. The corresponding release is returned -// or error if not found. -func (secrets *Secrets) Get(key string) (*rspb.Release, error) { - // fetch the secret holding the release named by key - obj, err := secrets.impl.Get(key, metav1.GetOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - return nil, ErrReleaseNotFound - } - return nil, errors.Wrapf(err, "get: failed to get %q", key) - } - // found the secret, decode the base64 data string - r, err := decodeRelease(string(obj.Data["release"])) - return r, errors.Wrapf(err, "get: failed to decode data %q", key) -} - -// List fetches all releases and returns the list releases such -// that filter(release) == true. An error is returned if the -// secret fails to retrieve the releases. -func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { - lsel := kblabels.Set{"owner": "helm"}.AsSelector() - opts := metav1.ListOptions{LabelSelector: lsel.String()} - - list, err := secrets.impl.List(opts) - if err != nil { - return nil, errors.Wrap(err, "list: failed to list") - } - - var results []*rspb.Release - - // iterate over the secrets object list - // and decode each release - for _, item := range list.Items { - rls, err := decodeRelease(string(item.Data["release"])) - if err != nil { - secrets.Log("list: failed to decode release: %v: %s", item, err) - continue - } - if filter(rls) { - results = append(results, rls) - } - } - return results, nil -} - -// Query fetches all releases that match the provided map of labels. -// An error is returned if the secret fails to retrieve the releases. -func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error) { - ls := kblabels.Set{} - for k, v := range labels { - if errs := validation.IsValidLabelValue(v); len(errs) != 0 { - return nil, errors.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; ")) - } - ls[k] = v - } - - opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()} - - list, err := secrets.impl.List(opts) - if err != nil { - return nil, errors.Wrap(err, "query: failed to query with labels") - } - - if len(list.Items) == 0 { - return nil, ErrReleaseNotFound - } - - var results []*rspb.Release - for _, item := range list.Items { - rls, err := decodeRelease(string(item.Data["release"])) - if err != nil { - secrets.Log("query: failed to decode release: %s", err) - continue - } - results = append(results, rls) - } - return results, nil -} - -// Create creates a new Secret holding the release. If the -// Secret already exists, ErrReleaseExists is returned. -func (secrets *Secrets) Create(key string, rls *rspb.Release) error { - // set labels for secrets object meta data - var lbs labels - - lbs.init() - lbs.set("createdAt", strconv.Itoa(int(time.Now().Unix()))) - - // create a new secret to hold the release - obj, err := newSecretsObject(key, rls, lbs) - if err != nil { - return errors.Wrapf(err, "create: failed to encode release %q", rls.Name) - } - // push the secret object out into the kubiverse - if _, err := secrets.impl.Create(obj); err != nil { - if apierrors.IsAlreadyExists(err) { - return ErrReleaseExists - } - - return errors.Wrap(err, "create: failed to create") - } - return nil -} - -// Update updates the Secret holding the release. If not found -// the Secret is created to hold the release. -func (secrets *Secrets) Update(key string, rls *rspb.Release) error { - // set labels for secrets object meta data - var lbs labels - - lbs.init() - lbs.set("modifiedAt", strconv.Itoa(int(time.Now().Unix()))) - - // create a new secret object to hold the release - obj, err := newSecretsObject(key, rls, lbs) - if err != nil { - return errors.Wrapf(err, "update: failed to encode release %q", rls.Name) - } - // push the secret object out into the kubiverse - _, err = secrets.impl.Update(obj) - return errors.Wrap(err, "update: failed to update") -} - -// Delete deletes the Secret holding the release named by key. -func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) { - // fetch the release to check existence - if rls, err = secrets.Get(key); err != nil { - if apierrors.IsNotFound(err) { - return nil, ErrReleaseExists - } - - return nil, errors.Wrapf(err, "delete: failed to get release %q", key) - } - // delete the release - err = secrets.impl.Delete(key, &metav1.DeleteOptions{}) - return rls, err -} - -// newSecretsObject constructs a kubernetes Secret object -// to store a release. Each secret data entry is the base64 -// encoded gzipped string of a release. -// -// The following labels are used within each secret: -// -// "modifiedAt" - timestamp indicating when this secret was last modified. (set in Update) -// "createdAt" - timestamp indicating when this secret was created. (set in Create) -// "version" - version of the release. -// "status" - status of the release (see pkg/release/status.go for variants) -// "owner" - owner of the secret, currently "helm". -// "name" - name of the release. -// -func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, error) { - const owner = "helm" - - // encode the release - s, err := encodeRelease(rls) - if err != nil { - return nil, err - } - - if lbs == nil { - lbs.init() - } - - // apply labels - lbs.set("name", rls.Name) - lbs.set("owner", owner) - lbs.set("status", rls.Info.Status.String()) - lbs.set("version", strconv.Itoa(rls.Version)) - - // create and return secret object. - // Helm 3 introduced setting the 'Type' field - // in the Kubernetes storage object. - // Helm defines the field content as follows: - // /.v - // Type field for Helm 3: helm.sh/release.v1 - // Note: Version starts at 'v1' for Helm 3 and - // should be incremented if the release object - // metadata is modified. - // This would potentially be a breaking change - // and should only happen between major versions. - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: key, - Labels: lbs.toMap(), - }, - Type: "helm.sh/release.v1", - Data: map[string][]byte{"release": []byte(s)}, - }, nil -} diff --git a/pkg/storage/driver/secrets_test.go b/pkg/storage/driver/secrets_test.go deleted file mode 100644 index 892482e..0000000 --- a/pkg/storage/driver/secrets_test.go +++ /dev/null @@ -1,186 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver - -import ( - "encoding/base64" - "encoding/json" - "reflect" - "testing" - - v1 "k8s.io/api/core/v1" - - rspb "helm.sh/helm/v3/pkg/release" -) - -func TestSecretName(t *testing.T) { - c := newTestFixtureSecrets(t) - if c.Name() != SecretsDriverName { - t.Errorf("Expected name to be %q, got %q", SecretsDriverName, c.Name()) - } -} - -func TestSecretGet(t *testing.T) { - vers := 1 - name := "smug-pigeon" - namespace := "default" - key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) - - secrets := newTestFixtureSecrets(t, []*rspb.Release{rel}...) - - // get release with key - got, err := secrets.Get(key) - if err != nil { - t.Fatalf("Failed to get release: %s", err) - } - // compare fetched release with original - if !reflect.DeepEqual(rel, got) { - t.Errorf("Expected {%v}, got {%v}", rel, got) - } -} - -func TestUNcompressedSecretGet(t *testing.T) { - vers := 1 - name := "smug-pigeon" - namespace := "default" - key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) - - // Create a test fixture which contains an uncompressed release - secret, err := newSecretsObject(key, rel, nil) - if err != nil { - t.Fatalf("Failed to create secret: %s", err) - } - b, err := json.Marshal(rel) - if err != nil { - t.Fatalf("Failed to marshal release: %s", err) - } - secret.Data["release"] = []byte(base64.StdEncoding.EncodeToString(b)) - var mock MockSecretsInterface - mock.objects = map[string]*v1.Secret{key: secret} - secrets := NewSecrets(&mock) - - // get release with key - got, err := secrets.Get(key) - if err != nil { - t.Fatalf("Failed to get release: %s", err) - } - // compare fetched release with original - if !reflect.DeepEqual(rel, got) { - t.Errorf("Expected {%v}, got {%v}", rel, got) - } -} - -func TestSecretList(t *testing.T) { - secrets := newTestFixtureSecrets(t, []*rspb.Release{ - releaseStub("key-1", 1, "default", rspb.StatusUninstalled), - releaseStub("key-2", 1, "default", rspb.StatusUninstalled), - releaseStub("key-3", 1, "default", rspb.StatusDeployed), - releaseStub("key-4", 1, "default", rspb.StatusDeployed), - releaseStub("key-5", 1, "default", rspb.StatusSuperseded), - releaseStub("key-6", 1, "default", rspb.StatusSuperseded), - }...) - - // list all deleted releases - del, err := secrets.List(func(rel *rspb.Release) bool { - return rel.Info.Status == rspb.StatusUninstalled - }) - // check - if err != nil { - t.Errorf("Failed to list deleted: %s", err) - } - if len(del) != 2 { - t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del) - } - - // list all deployed releases - dpl, err := secrets.List(func(rel *rspb.Release) bool { - return rel.Info.Status == rspb.StatusDeployed - }) - // check - if err != nil { - t.Errorf("Failed to list deployed: %s", err) - } - if len(dpl) != 2 { - t.Errorf("Expected 2 deployed, got %d", len(dpl)) - } - - // list all superseded releases - ssd, err := secrets.List(func(rel *rspb.Release) bool { - return rel.Info.Status == rspb.StatusSuperseded - }) - // check - if err != nil { - t.Errorf("Failed to list superseded: %s", err) - } - if len(ssd) != 2 { - t.Errorf("Expected 2 superseded, got %d", len(ssd)) - } -} - -func TestSecretCreate(t *testing.T) { - secrets := newTestFixtureSecrets(t) - - vers := 1 - name := "smug-pigeon" - namespace := "default" - key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) - - // store the release in a secret - if err := secrets.Create(key, rel); err != nil { - t.Fatalf("Failed to create release with key %q: %s", key, err) - } - - // get the release back - got, err := secrets.Get(key) - if err != nil { - t.Fatalf("Failed to get release with key %q: %s", key, err) - } - - // compare created release with original - if !reflect.DeepEqual(rel, got) { - t.Errorf("Expected {%v}, got {%v}", rel, got) - } -} - -func TestSecretUpdate(t *testing.T) { - vers := 1 - name := "smug-pigeon" - namespace := "default" - key := testKey(name, vers) - rel := releaseStub(name, vers, namespace, rspb.StatusDeployed) - - secrets := newTestFixtureSecrets(t, []*rspb.Release{rel}...) - - // modify release status code - rel.Info.Status = rspb.StatusSuperseded - - // perform the update - if err := secrets.Update(key, rel); err != nil { - t.Fatalf("Failed to update release: %s", err) - } - - // fetch the updated release - got, err := secrets.Get(key) - if err != nil { - t.Fatalf("Failed to get release with key %q: %s", key, err) - } - - // check release has actually been updated by comparing modified fields - if rel.Info.Status != got.Info.Status { - t.Errorf("Expected status %s, got status %s", rel.Info.Status.String(), got.Info.Status.String()) - } -} diff --git a/pkg/storage/driver/util.go b/pkg/storage/driver/util.go deleted file mode 100644 index a87002a..0000000 --- a/pkg/storage/driver/util.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package driver // import "helm.sh/helm/v3/pkg/storage/driver" - -import ( - "bytes" - "compress/gzip" - "encoding/base64" - "encoding/json" - "io/ioutil" - - rspb "helm.sh/helm/v3/pkg/release" -) - -var b64 = base64.StdEncoding - -var magicGzip = []byte{0x1f, 0x8b, 0x08} - -// encodeRelease encodes a release returning a base64 encoded -// gzipped string representation, or error. -func encodeRelease(rls *rspb.Release) (string, error) { - b, err := json.Marshal(rls) - if err != nil { - return "", err - } - var buf bytes.Buffer - w, err := gzip.NewWriterLevel(&buf, gzip.BestCompression) - if err != nil { - return "", err - } - if _, err = w.Write(b); err != nil { - return "", err - } - w.Close() - - return b64.EncodeToString(buf.Bytes()), nil -} - -// decodeRelease decodes the bytes of data into a release -// type. Data must contain a base64 encoded gzipped string of a -// valid release, otherwise an error is returned. -func decodeRelease(data string) (*rspb.Release, error) { - // base64 decode string - b, err := b64.DecodeString(data) - if err != nil { - return nil, err - } - - // For backwards compatibility with releases that were stored before - // compression was introduced we skip decompression if the - // gzip magic header is not found - if bytes.Equal(b[0:3], magicGzip) { - r, err := gzip.NewReader(bytes.NewReader(b)) - if err != nil { - return nil, err - } - b2, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - b = b2 - } - - var rls rspb.Release - // unmarshal release object bytes - if err := json.Unmarshal(b, &rls); err != nil { - return nil, err - } - return &rls, nil -} diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go deleted file mode 100644 index 8918335..0000000 --- a/pkg/storage/storage.go +++ /dev/null @@ -1,237 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package storage // import "helm.sh/helm/v3/pkg/storage" - -import ( - "fmt" - "strings" - - "github.com/pkg/errors" - - rspb "helm.sh/helm/v3/pkg/release" - relutil "helm.sh/helm/v3/pkg/releaseutil" - "helm.sh/helm/v3/pkg/storage/driver" -) - -// The type field of the Kubernetes storage object which stores the Helm release -// version. It is modified slightly replacing the '/': sh.helm/release.v1 -// Note: The version 'v1' is incremented if the release object metadata is -// modified between major releases. -// This constant is used as a prefix for the Kubernetes storage object name. -const HelmStorageType = "sh.helm.release.v1" - -// Storage represents a storage engine for a Release. -type Storage struct { - driver.Driver - - // MaxHistory specifies the maximum number of historical releases that will - // be retained, including the most recent release. Values of 0 or less are - // ignored (meaning no limits are imposed). - MaxHistory int - - Log func(string, ...interface{}) -} - -// Get retrieves the release from storage. An error is returned -// if the storage driver failed to fetch the release, or the -// release identified by the key, version pair does not exist. -func (s *Storage) Get(name string, version int) (*rspb.Release, error) { - s.Log("getting release %q", makeKey(name, version)) - return s.Driver.Get(makeKey(name, version)) -} - -// Create creates a new storage entry holding the release. An -// error is returned if the storage driver failed to store the -// release, or a release with identical an key already exists. -func (s *Storage) Create(rls *rspb.Release) error { - s.Log("creating release %q", makeKey(rls.Name, rls.Version)) - if s.MaxHistory > 0 { - // Want to make space for one more release. - s.removeLeastRecent(rls.Name, s.MaxHistory-1) - } - return s.Driver.Create(makeKey(rls.Name, rls.Version), rls) -} - -// Update updates the release in storage. An error is returned if the -// storage backend fails to update the release or if the release -// does not exist. -func (s *Storage) Update(rls *rspb.Release) error { - s.Log("updating release %q", makeKey(rls.Name, rls.Version)) - return s.Driver.Update(makeKey(rls.Name, rls.Version), rls) -} - -// Delete deletes the release from storage. An error is returned if -// the storage backend fails to delete the release or if the release -// does not exist. -func (s *Storage) Delete(name string, version int) (*rspb.Release, error) { - s.Log("deleting release %q", makeKey(name, version)) - return s.Driver.Delete(makeKey(name, version)) -} - -// ListReleases returns all releases from storage. An error is returned if the -// storage backend fails to retrieve the releases. -func (s *Storage) ListReleases() ([]*rspb.Release, error) { - s.Log("listing all releases in storage") - return s.Driver.List(func(_ *rspb.Release) bool { return true }) -} - -// ListUninstalled returns all releases with Status == UNINSTALLED. An error is returned -// if the storage backend fails to retrieve the releases. -func (s *Storage) ListUninstalled() ([]*rspb.Release, error) { - s.Log("listing uninstalled releases in storage") - return s.Driver.List(func(rls *rspb.Release) bool { - return relutil.StatusFilter(rspb.StatusUninstalled).Check(rls) - }) -} - -// ListDeployed returns all releases with Status == DEPLOYED. An error is returned -// if the storage backend fails to retrieve the releases. -func (s *Storage) ListDeployed() ([]*rspb.Release, error) { - s.Log("listing all deployed releases in storage") - return s.Driver.List(func(rls *rspb.Release) bool { - return relutil.StatusFilter(rspb.StatusDeployed).Check(rls) - }) -} - -// Deployed returns the last deployed release with the provided release name, or -// returns ErrReleaseNotFound if not found. -func (s *Storage) Deployed(name string) (*rspb.Release, error) { - ls, err := s.DeployedAll(name) - if err != nil { - return nil, err - } - - if len(ls) == 0 { - return nil, errors.Errorf("%q has no deployed releases", name) - } - - // If executed concurrently, Helm's database gets corrupted - // and multiple releases are DEPLOYED. Take the latest. - relutil.Reverse(ls, relutil.SortByRevision) - - return ls[0], nil -} - -// DeployedAll returns all deployed releases with the provided name, or -// returns ErrReleaseNotFound if not found. -func (s *Storage) DeployedAll(name string) ([]*rspb.Release, error) { - s.Log("getting deployed releases from %q history", name) - - ls, err := s.Driver.Query(map[string]string{ - "name": name, - "owner": "helm", - "status": "deployed", - }) - if err == nil { - return ls, nil - } - if strings.Contains(err.Error(), "not found") { - return nil, errors.Errorf("%q has no deployed releases", name) - } - return nil, err -} - -// History returns the revision history for the release with the provided name, or -// returns ErrReleaseNotFound if no such release name exists. -func (s *Storage) History(name string) ([]*rspb.Release, error) { - s.Log("getting release history for %q", name) - - return s.Driver.Query(map[string]string{"name": name, "owner": "helm"}) -} - -// removeLeastRecent removes items from history until the lengh number of releases -// does not exceed max. -// -// We allow max to be set explicitly so that calling functions can "make space" -// for the new records they are going to write. -func (s *Storage) removeLeastRecent(name string, max int) error { - if max < 0 { - return nil - } - h, err := s.History(name) - if err != nil { - return err - } - if len(h) <= max { - return nil - } - overage := len(h) - max - - // We want oldest to newest - relutil.SortByRevision(h) - - // Delete as many as possible. In the case of API throughput limitations, - // multiple invocations of this function will eventually delete them all. - toDelete := h[0:overage] - errs := []error{} - for _, rel := range toDelete { - key := makeKey(name, rel.Version) - _, innerErr := s.Delete(name, rel.Version) - if innerErr != nil { - s.Log("error pruning %s from release history: %s", key, innerErr) - errs = append(errs, innerErr) - } - } - - s.Log("Pruned %d record(s) from %s with %d error(s)", len(toDelete), name, len(errs)) - switch c := len(errs); c { - case 0: - return nil - case 1: - return errs[0] - default: - return errors.Errorf("encountered %d deletion errors. First is: %s", c, errs[0]) - } -} - -// Last fetches the last revision of the named release. -func (s *Storage) Last(name string) (*rspb.Release, error) { - s.Log("getting last revision of %q", name) - h, err := s.History(name) - if err != nil { - return nil, err - } - if len(h) == 0 { - return nil, errors.Errorf("no revision for release %q", name) - } - - relutil.Reverse(h, relutil.SortByRevision) - return h[0], nil -} - -// makeKey concatenates the Kubernetes storage object type, a release name and version -// into a string with format:```..v```. -// The storage type is prepended to keep name uniqueness between different -// release storage types. An example of clash when not using the type: -// https://github.com/helm/helm/issues/6435. -// This key is used to uniquely identify storage objects. -func makeKey(rlsname string, version int) string { - return fmt.Sprintf("%s.%s.v%d", HelmStorageType, rlsname, version) -} - -// Init initializes a new storage backend with the driver d. -// If d is nil, the default in-memory driver is used. -func Init(d driver.Driver) *Storage { - // default driver is in memory - if d == nil { - d = driver.NewMemory() - } - return &Storage{ - Driver: d, - Log: func(_ string, _ ...interface{}) {}, - } -} diff --git a/pkg/storage/storage_test.go b/pkg/storage/storage_test.go deleted file mode 100644 index ee9c68b..0000000 --- a/pkg/storage/storage_test.go +++ /dev/null @@ -1,390 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package storage // import "helm.sh/helm/v3/pkg/storage" - -import ( - "fmt" - "reflect" - "testing" - - rspb "helm.sh/helm/v3/pkg/release" - "helm.sh/helm/v3/pkg/storage/driver" -) - -func TestStorageCreate(t *testing.T) { - // initialize storage - storage := Init(driver.NewMemory()) - - // create fake release - rls := ReleaseTestData{ - Name: "angry-beaver", - Version: 1, - }.ToRelease() - - assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") - - // fetch the release - res, err := storage.Get(rls.Name, rls.Version) - assertErrNil(t.Fatal, err, "QueryRelease") - - // verify the fetched and created release are the same - if !reflect.DeepEqual(rls, res) { - t.Fatalf("Expected %v, got %v", rls, res) - } -} - -func TestStorageUpdate(t *testing.T) { - // initialize storage - storage := Init(driver.NewMemory()) - - // create fake release - rls := ReleaseTestData{ - Name: "angry-beaver", - Version: 1, - Status: rspb.StatusDeployed, - }.ToRelease() - - assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") - - // modify the release - rls.Info.Status = rspb.StatusUninstalled - assertErrNil(t.Fatal, storage.Update(rls), "UpdateRelease") - - // retrieve the updated release - res, err := storage.Get(rls.Name, rls.Version) - assertErrNil(t.Fatal, err, "QueryRelease") - - // verify updated and fetched releases are the same. - if !reflect.DeepEqual(rls, res) { - t.Fatalf("Expected %v, got %v", rls, res) - } -} - -func TestStorageDelete(t *testing.T) { - // initialize storage - storage := Init(driver.NewMemory()) - - // create fake release - rls := ReleaseTestData{ - Name: "angry-beaver", - Version: 1, - }.ToRelease() - rls2 := ReleaseTestData{ - Name: "angry-beaver", - Version: 2, - }.ToRelease() - - assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") - assertErrNil(t.Fatal, storage.Create(rls2), "StoreRelease") - - // delete the release - res, err := storage.Delete(rls.Name, rls.Version) - assertErrNil(t.Fatal, err, "DeleteRelease") - - // verify updated and fetched releases are the same. - if !reflect.DeepEqual(rls, res) { - t.Fatalf("Expected %v, got %v", rls, res) - } - - hist, err := storage.History(rls.Name) - if err != nil { - t.Errorf("unexpected error: %s", err) - } - - // We have now deleted one of the two records. - if len(hist) != 1 { - t.Errorf("expected 1 record for deleted release version, got %d", len(hist)) - } - - if hist[0].Version != 2 { - t.Errorf("Expected version to be 2, got %d", hist[0].Version) - } -} - -func TestStorageList(t *testing.T) { - // initialize storage - storage := Init(driver.NewMemory()) - - // setup storage with test releases - setup := func() { - // release records - rls0 := ReleaseTestData{Name: "happy-catdog", Status: rspb.StatusSuperseded}.ToRelease() - rls1 := ReleaseTestData{Name: "livid-human", Status: rspb.StatusSuperseded}.ToRelease() - rls2 := ReleaseTestData{Name: "relaxed-cat", Status: rspb.StatusSuperseded}.ToRelease() - rls3 := ReleaseTestData{Name: "hungry-hippo", Status: rspb.StatusDeployed}.ToRelease() - rls4 := ReleaseTestData{Name: "angry-beaver", Status: rspb.StatusDeployed}.ToRelease() - rls5 := ReleaseTestData{Name: "opulent-frog", Status: rspb.StatusUninstalled}.ToRelease() - rls6 := ReleaseTestData{Name: "happy-liger", Status: rspb.StatusUninstalled}.ToRelease() - - // create the release records in the storage - assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'rls0'") - assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'rls1'") - assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'rls2'") - assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'rls3'") - assertErrNil(t.Fatal, storage.Create(rls4), "Storing release 'rls4'") - assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'rls5'") - assertErrNil(t.Fatal, storage.Create(rls6), "Storing release 'rls6'") - } - - var listTests = []struct { - Description string - NumExpected int - ListFunc func() ([]*rspb.Release, error) - }{ - {"ListDeployed", 2, storage.ListDeployed}, - {"ListReleases", 7, storage.ListReleases}, - {"ListUninstalled", 2, storage.ListUninstalled}, - } - - setup() - - for _, tt := range listTests { - list, err := tt.ListFunc() - assertErrNil(t.Fatal, err, tt.Description) - // verify the count of releases returned - if len(list) != tt.NumExpected { - t.Errorf("ListReleases(%s): expected %d, actual %d", - tt.Description, - tt.NumExpected, - len(list)) - } - } -} - -func TestStorageDeployed(t *testing.T) { - storage := Init(driver.NewMemory()) - - const name = "angry-bird" - const vers = 4 - - // setup storage with test releases - setup := func() { - // release records - rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() - rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease() - rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() - rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusDeployed}.ToRelease() - - // create the release records in the storage - assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") - assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") - assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") - assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") - } - - setup() - - rls, err := storage.Last(name) - if err != nil { - t.Fatalf("Failed to query for deployed release: %s\n", err) - } - - switch { - case rls == nil: - t.Fatalf("Release is nil") - case rls.Name != name: - t.Fatalf("Expected release name %q, actual %q\n", name, rls.Name) - case rls.Version != vers: - t.Fatalf("Expected release version %d, actual %d\n", vers, rls.Version) - case rls.Info.Status != rspb.StatusDeployed: - t.Fatalf("Expected release status 'DEPLOYED', actual %s\n", rls.Info.Status.String()) - } -} - -func TestStorageDeployedWithCorruption(t *testing.T) { - storage := Init(driver.NewMemory()) - - const name = "angry-bird" - const vers = int(4) - - // setup storage with test releases - setup := func() { - // release records (notice odd order and corruption) - rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() - rls1 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusDeployed}.ToRelease() - rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() - rls3 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusDeployed}.ToRelease() - - // create the release records in the storage - assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") - assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") - assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") - assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") - } - - setup() - - rls, err := storage.Deployed(name) - if err != nil { - t.Fatalf("Failed to query for deployed release: %s\n", err) - } - - switch { - case rls == nil: - t.Fatalf("Release is nil") - case rls.Name != name: - t.Fatalf("Expected release name %q, actual %q\n", name, rls.Name) - case rls.Version != vers: - t.Fatalf("Expected release version %d, actual %d\n", vers, rls.Version) - case rls.Info.Status != rspb.StatusDeployed: - t.Fatalf("Expected release status 'DEPLOYED', actual %s\n", rls.Info.Status.String()) - } -} - -func TestStorageHistory(t *testing.T) { - storage := Init(driver.NewMemory()) - - const name = "angry-bird" - - // setup storage with test releases - setup := func() { - // release records - rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() - rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease() - rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() - rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusDeployed}.ToRelease() - - // create the release records in the storage - assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") - assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") - assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") - assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") - } - - setup() - - h, err := storage.History(name) - if err != nil { - t.Fatalf("Failed to query for release history (%q): %s\n", name, err) - } - if len(h) != 4 { - t.Fatalf("Release history (%q) is empty\n", name) - } -} - -func TestStorageRemoveLeastRecent(t *testing.T) { - storage := Init(driver.NewMemory()) - storage.Log = t.Logf - - // Make sure that specifying this at the outset doesn't cause any bugs. - storage.MaxHistory = 10 - - const name = "angry-bird" - - // setup storage with test releases - setup := func() { - // release records - rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() - rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease() - rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() - rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusDeployed}.ToRelease() - - // create the release records in the storage - assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") - assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") - assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") - assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") - } - setup() - - // Because we have not set a limit, we expect 4. - expect := 4 - if hist, err := storage.History(name); err != nil { - t.Fatal(err) - } else if len(hist) != expect { - t.Fatalf("expected %d items in history, got %d", expect, len(hist)) - } - - storage.MaxHistory = 3 - rls5 := ReleaseTestData{Name: name, Version: 5, Status: rspb.StatusDeployed}.ToRelease() - assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'angry-bird' (v5)") - - // On inserting the 5th record, we expect two records to be pruned from history. - hist, err := storage.History(name) - if err != nil { - t.Fatal(err) - } else if len(hist) != storage.MaxHistory { - for _, item := range hist { - t.Logf("%s %v", item.Name, item.Version) - } - t.Fatalf("expected %d items in history, got %d", storage.MaxHistory, len(hist)) - } - - // We expect the existing records to be 3, 4, and 5. - for i, item := range hist { - v := item.Version - if expect := i + 3; v != expect { - t.Errorf("Expected release %d, got %d", expect, v) - } - } -} - -func TestStorageLast(t *testing.T) { - storage := Init(driver.NewMemory()) - - const name = "angry-bird" - - // Set up storage with test releases. - setup := func() { - // release records - rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() - rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease() - rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() - rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusFailed}.ToRelease() - - // create the release records in the storage - assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") - assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") - assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") - assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") - } - - setup() - - h, err := storage.Last(name) - if err != nil { - t.Fatalf("Failed to query for release history (%q): %s\n", name, err) - } - - if h.Version != 4 { - t.Errorf("Expected revision 4, got %d", h.Version) - } -} - -type ReleaseTestData struct { - Name string - Version int - Manifest string - Namespace string - Status rspb.Status -} - -func (test ReleaseTestData) ToRelease() *rspb.Release { - return &rspb.Release{ - Name: test.Name, - Version: test.Version, - Manifest: test.Manifest, - Namespace: test.Namespace, - Info: &rspb.Info{Status: test.Status}, - } -} - -func assertErrNil(eh func(args ...interface{}), err error, message string) { - if err != nil { - eh(fmt.Sprintf("%s: %q", message, err)) - } -} diff --git a/pkg/strvals/doc.go b/pkg/strvals/doc.go deleted file mode 100644 index f172905..0000000 --- a/pkg/strvals/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*Package strvals provides tools for working with strval lines. - -Helm supports a compressed format for YAML settings which we call strvals. -The format is roughly like this: - - name=value,topname.subname=value - -The above is equivalent to the YAML document - - name: value - topname: - subname: value - -This package provides a parser and utilities for converting the strvals format -to other formats. -*/ -package strvals diff --git a/pkg/strvals/parser.go b/pkg/strvals/parser.go deleted file mode 100644 index 03adbd3..0000000 --- a/pkg/strvals/parser.go +++ /dev/null @@ -1,408 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package strvals - -import ( - "bytes" - "io" - "strconv" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" -) - -// ErrNotList indicates that a non-list was treated as a list. -var ErrNotList = errors.New("not a list") - -// ToYAML takes a string of arguments and converts to a YAML document. -func ToYAML(s string) (string, error) { - m, err := Parse(s) - if err != nil { - return "", err - } - d, err := yaml.Marshal(m) - return strings.TrimSuffix(string(d), "\n"), err -} - -// Parse parses a set line. -// -// A set line is of the form name1=value1,name2=value2 -func Parse(s string) (map[string]interface{}, error) { - vals := map[string]interface{}{} - scanner := bytes.NewBufferString(s) - t := newParser(scanner, vals, false) - err := t.parse() - return vals, err -} - -// ParseString parses a set line and forces a string value. -// -// A set line is of the form name1=value1,name2=value2 -func ParseString(s string) (map[string]interface{}, error) { - vals := map[string]interface{}{} - scanner := bytes.NewBufferString(s) - t := newParser(scanner, vals, true) - err := t.parse() - return vals, err -} - -// ParseInto parses a strvals line and merges the result into dest. -// -// If the strval string has a key that exists in dest, it overwrites the -// dest version. -func ParseInto(s string, dest map[string]interface{}) error { - scanner := bytes.NewBufferString(s) - t := newParser(scanner, dest, false) - return t.parse() -} - -// ParseFile parses a set line, but its final value is loaded from the file at the path specified by the original value. -// -// A set line is of the form name1=path1,name2=path2 -// -// When the files at path1 and path2 contained "val1" and "val2" respectively, the set line is consumed as -// name1=val1,name2=val2 -func ParseFile(s string, reader RunesValueReader) (map[string]interface{}, error) { - vals := map[string]interface{}{} - scanner := bytes.NewBufferString(s) - t := newFileParser(scanner, vals, reader) - err := t.parse() - return vals, err -} - -// ParseIntoString parses a strvals line and merges the result into dest. -// -// This method always returns a string as the value. -func ParseIntoString(s string, dest map[string]interface{}) error { - scanner := bytes.NewBufferString(s) - t := newParser(scanner, dest, true) - return t.parse() -} - -// ParseIntoFile parses a filevals line and merges the result into dest. -// -// This method always returns a string as the value. -func ParseIntoFile(s string, dest map[string]interface{}, reader RunesValueReader) error { - scanner := bytes.NewBufferString(s) - t := newFileParser(scanner, dest, reader) - return t.parse() -} - -// RunesValueReader is a function that takes the given value (a slice of runes) -// and returns the parsed value -type RunesValueReader func([]rune) (interface{}, error) - -// parser is a simple parser that takes a strvals line and parses it into a -// map representation. -// -// where sc is the source of the original data being parsed -// where data is the final parsed data from the parses with correct types -type parser struct { - sc *bytes.Buffer - data map[string]interface{} - reader RunesValueReader -} - -func newParser(sc *bytes.Buffer, data map[string]interface{}, stringBool bool) *parser { - stringConverter := func(rs []rune) (interface{}, error) { - return typedVal(rs, stringBool), nil - } - return &parser{sc: sc, data: data, reader: stringConverter} -} - -func newFileParser(sc *bytes.Buffer, data map[string]interface{}, reader RunesValueReader) *parser { - return &parser{sc: sc, data: data, reader: reader} -} - -func (t *parser) parse() error { - for { - err := t.key(t.data) - if err == nil { - continue - } - if err == io.EOF { - return nil - } - return err - } -} - -func runeSet(r []rune) map[rune]bool { - s := make(map[rune]bool, len(r)) - for _, rr := range r { - s[rr] = true - } - return s -} - -func (t *parser) key(data map[string]interface{}) error { - stop := runeSet([]rune{'=', '[', ',', '.'}) - for { - switch k, last, err := runesUntil(t.sc, stop); { - case err != nil: - if len(k) == 0 { - return err - } - return errors.Errorf("key %q has no value", string(k)) - //set(data, string(k), "") - //return err - case last == '[': - // We are in a list index context, so we need to set an index. - i, err := t.keyIndex() - if err != nil { - return errors.Wrap(err, "error parsing index") - } - kk := string(k) - // Find or create target list - list := []interface{}{} - if _, ok := data[kk]; ok { - list = data[kk].([]interface{}) - } - - // Now we need to get the value after the ]. - list, err = t.listItem(list, i) - set(data, kk, list) - return err - case last == '=': - //End of key. Consume =, Get value. - // FIXME: Get value list first - vl, e := t.valList() - switch e { - case nil: - set(data, string(k), vl) - return nil - case io.EOF: - set(data, string(k), "") - return e - case ErrNotList: - rs, e := t.val() - if e != nil && e != io.EOF { - return e - } - v, e := t.reader(rs) - set(data, string(k), v) - return e - default: - return e - } - - case last == ',': - // No value given. Set the value to empty string. Return error. - set(data, string(k), "") - return errors.Errorf("key %q has no value (cannot end with ,)", string(k)) - case last == '.': - // First, create or find the target map. - inner := map[string]interface{}{} - if _, ok := data[string(k)]; ok { - inner = data[string(k)].(map[string]interface{}) - } - - // Recurse - e := t.key(inner) - if len(inner) == 0 { - return errors.Errorf("key map %q has no value", string(k)) - } - set(data, string(k), inner) - return e - } - } -} - -func set(data map[string]interface{}, key string, val interface{}) { - // If key is empty, don't set it. - if len(key) == 0 { - return - } - data[key] = val -} - -func setIndex(list []interface{}, index int, val interface{}) []interface{} { - if len(list) <= index { - newlist := make([]interface{}, index+1) - copy(newlist, list) - list = newlist - } - list[index] = val - return list -} - -func (t *parser) keyIndex() (int, error) { - // First, get the key. - stop := runeSet([]rune{']'}) - v, _, err := runesUntil(t.sc, stop) - if err != nil { - return 0, err - } - // v should be the index - return strconv.Atoi(string(v)) - -} -func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) { - stop := runeSet([]rune{'[', '.', '='}) - switch k, last, err := runesUntil(t.sc, stop); { - case len(k) > 0: - return list, errors.Errorf("unexpected data at end of array index: %q", k) - case err != nil: - return list, err - case last == '=': - vl, e := t.valList() - switch e { - case nil: - return setIndex(list, i, vl), nil - case io.EOF: - return setIndex(list, i, ""), err - case ErrNotList: - rs, e := t.val() - if e != nil && e != io.EOF { - return list, e - } - v, e := t.reader(rs) - return setIndex(list, i, v), e - default: - return list, e - } - case last == '[': - // now we have a nested list. Read the index and handle. - i, err := t.keyIndex() - if err != nil { - return list, errors.Wrap(err, "error parsing index") - } - // Now we need to get the value after the ]. - list2, err := t.listItem(list, i) - return setIndex(list, i, list2), err - case last == '.': - // We have a nested object. Send to t.key - inner := map[string]interface{}{} - if len(list) > i { - var ok bool - inner, ok = list[i].(map[string]interface{}) - if !ok { - // We have indices out of order. Initialize empty value. - list[i] = map[string]interface{}{} - inner = list[i].(map[string]interface{}) - } - } - - // Recurse - e := t.key(inner) - return setIndex(list, i, inner), e - default: - return nil, errors.Errorf("parse error: unexpected token %v", last) - } -} - -func (t *parser) val() ([]rune, error) { - stop := runeSet([]rune{','}) - v, _, err := runesUntil(t.sc, stop) - return v, err -} - -func (t *parser) valList() ([]interface{}, error) { - r, _, e := t.sc.ReadRune() - if e != nil { - return []interface{}{}, e - } - - if r != '{' { - t.sc.UnreadRune() - return []interface{}{}, ErrNotList - } - - list := []interface{}{} - stop := runeSet([]rune{',', '}'}) - for { - switch rs, last, err := runesUntil(t.sc, stop); { - case err != nil: - if err == io.EOF { - err = errors.New("list must terminate with '}'") - } - return list, err - case last == '}': - // If this is followed by ',', consume it. - if r, _, e := t.sc.ReadRune(); e == nil && r != ',' { - t.sc.UnreadRune() - } - v, e := t.reader(rs) - list = append(list, v) - return list, e - case last == ',': - v, e := t.reader(rs) - if e != nil { - return list, e - } - list = append(list, v) - } - } -} - -func runesUntil(in io.RuneReader, stop map[rune]bool) ([]rune, rune, error) { - v := []rune{} - for { - switch r, _, e := in.ReadRune(); { - case e != nil: - return v, r, e - case inMap(r, stop): - return v, r, nil - case r == '\\': - next, _, e := in.ReadRune() - if e != nil { - return v, next, e - } - v = append(v, next) - default: - v = append(v, r) - } - } -} - -func inMap(k rune, m map[rune]bool) bool { - _, ok := m[k] - return ok -} - -func typedVal(v []rune, st bool) interface{} { - val := string(v) - - if st { - return val - } - - if strings.EqualFold(val, "true") { - return true - } - - if strings.EqualFold(val, "false") { - return false - } - - if strings.EqualFold(val, "null") { - return nil - } - - if strings.EqualFold(val, "0") { - return int64(0) - } - - // If this value does not start with zero, try parsing it to an int - if len(val) != 0 && val[0] != '0' { - if iv, err := strconv.ParseInt(val, 10, 64); err == nil { - return iv - } - } - - return val -} diff --git a/pkg/strvals/parser_test.go b/pkg/strvals/parser_test.go deleted file mode 100644 index 7f38efa..0000000 --- a/pkg/strvals/parser_test.go +++ /dev/null @@ -1,496 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package strvals - -import ( - "testing" - - "sigs.k8s.io/yaml" -) - -func TestSetIndex(t *testing.T) { - tests := []struct { - name string - initial []interface{} - expect []interface{} - add int - val int - }{ - { - name: "short", - initial: []interface{}{0, 1}, - expect: []interface{}{0, 1, 2}, - add: 2, - val: 2, - }, - { - name: "equal", - initial: []interface{}{0, 1}, - expect: []interface{}{0, 2}, - add: 1, - val: 2, - }, - { - name: "long", - initial: []interface{}{0, 1, 2, 3, 4, 5}, - expect: []interface{}{0, 1, 2, 4, 4, 5}, - add: 3, - val: 4, - }, - } - - for _, tt := range tests { - got := setIndex(tt.initial, tt.add, tt.val) - if len(got) != len(tt.expect) { - t.Fatalf("%s: Expected length %d, got %d", tt.name, len(tt.expect), len(got)) - } - - if gg := got[tt.add].(int); gg != tt.val { - t.Errorf("%s, Expected value %d, got %d", tt.name, tt.val, gg) - } - } -} - -func TestParseSet(t *testing.T) { - testsString := []struct { - str string - expect map[string]interface{} - err bool - }{ - { - str: "long_int_string=1234567890", - expect: map[string]interface{}{"long_int_string": "1234567890"}, - err: false, - }, - { - str: "boolean=true", - expect: map[string]interface{}{"boolean": "true"}, - err: false, - }, - { - str: "is_null=null", - expect: map[string]interface{}{"is_null": "null"}, - err: false, - }, - { - str: "zero=0", - expect: map[string]interface{}{"zero": "0"}, - err: false, - }, - } - tests := []struct { - str string - expect map[string]interface{} - err bool - }{ - { - "name1=null,f=false,t=true", - map[string]interface{}{"name1": nil, "f": false, "t": true}, - false, - }, - { - "name1=value1", - map[string]interface{}{"name1": "value1"}, - false, - }, - { - "name1=value1,name2=value2", - map[string]interface{}{"name1": "value1", "name2": "value2"}, - false, - }, - { - "name1=value1,name2=value2,", - map[string]interface{}{"name1": "value1", "name2": "value2"}, - false, - }, - { - str: "name1=value1,,,,name2=value2,", - err: true, - }, - { - str: "name1=,name2=value2", - expect: map[string]interface{}{"name1": "", "name2": "value2"}, - }, - { - str: "leading_zeros=00009", - expect: map[string]interface{}{"leading_zeros": "00009"}, - }, - { - str: "zero_int=0", - expect: map[string]interface{}{"zero_int": 0}, - }, - { - str: "long_int=1234567890", - expect: map[string]interface{}{"long_int": 1234567890}, - }, - { - str: "boolean=true", - expect: map[string]interface{}{"boolean": true}, - }, - { - str: "is_null=null", - expect: map[string]interface{}{"is_null": nil}, - err: false, - }, - { - str: "name1,name2=", - err: true, - }, - { - str: "name1,name2=value2", - err: true, - }, - { - str: "name1,name2=value2\\", - err: true, - }, - { - str: "name1,name2", - err: true, - }, - { - "name1=one\\,two,name2=three\\,four", - map[string]interface{}{"name1": "one,two", "name2": "three,four"}, - false, - }, - { - "name1=one\\=two,name2=three\\=four", - map[string]interface{}{"name1": "one=two", "name2": "three=four"}, - false, - }, - { - "name1=one two three,name2=three two one", - map[string]interface{}{"name1": "one two three", "name2": "three two one"}, - false, - }, - { - "outer.inner=value", - map[string]interface{}{"outer": map[string]interface{}{"inner": "value"}}, - false, - }, - { - "outer.middle.inner=value", - map[string]interface{}{"outer": map[string]interface{}{"middle": map[string]interface{}{"inner": "value"}}}, - false, - }, - { - "outer.inner1=value,outer.inner2=value2", - map[string]interface{}{"outer": map[string]interface{}{"inner1": "value", "inner2": "value2"}}, - false, - }, - { - "outer.inner1=value,outer.middle.inner=value", - map[string]interface{}{ - "outer": map[string]interface{}{ - "inner1": "value", - "middle": map[string]interface{}{ - "inner": "value", - }, - }, - }, - false, - }, - { - str: "name1.name2", - err: true, - }, - { - str: "name1.name2,name1.name3", - err: true, - }, - { - str: "name1.name2=", - expect: map[string]interface{}{"name1": map[string]interface{}{"name2": ""}}, - }, - { - str: "name1.=name2", - err: true, - }, - { - str: "name1.,name2", - err: true, - }, - { - "name1={value1,value2}", - map[string]interface{}{"name1": []string{"value1", "value2"}}, - false, - }, - { - "name1={value1,value2},name2={value1,value2}", - map[string]interface{}{ - "name1": []string{"value1", "value2"}, - "name2": []string{"value1", "value2"}, - }, - false, - }, - { - "name1={1021,902}", - map[string]interface{}{"name1": []int{1021, 902}}, - false, - }, - { - "name1.name2={value1,value2}", - map[string]interface{}{"name1": map[string]interface{}{"name2": []string{"value1", "value2"}}}, - false, - }, - { - str: "name1={1021,902", - err: true, - }, - // List support - { - str: "list[0]=foo", - expect: map[string]interface{}{"list": []string{"foo"}}, - }, - { - str: "list[0].foo=bar", - expect: map[string]interface{}{ - "list": []interface{}{ - map[string]interface{}{"foo": "bar"}, - }, - }, - }, - { - str: "list[0].foo=bar,list[0].hello=world", - expect: map[string]interface{}{ - "list": []interface{}{ - map[string]interface{}{"foo": "bar", "hello": "world"}, - }, - }, - }, - { - str: "list[0]=foo,list[1]=bar", - expect: map[string]interface{}{"list": []string{"foo", "bar"}}, - }, - { - str: "list[0]=foo,list[1]=bar,", - expect: map[string]interface{}{"list": []string{"foo", "bar"}}, - }, - { - str: "list[0]=foo,list[3]=bar", - expect: map[string]interface{}{"list": []interface{}{"foo", nil, nil, "bar"}}, - }, - { - str: "illegal[0]name.foo=bar", - err: true, - }, - { - str: "noval[0]", - expect: map[string]interface{}{"noval": []interface{}{}}, - }, - { - str: "noval[0]=", - expect: map[string]interface{}{"noval": []interface{}{""}}, - }, - { - str: "nested[0][0]=1", - expect: map[string]interface{}{"nested": []interface{}{[]interface{}{1}}}, - }, - { - str: "nested[1][1]=1", - expect: map[string]interface{}{"nested": []interface{}{nil, []interface{}{nil, 1}}}, - }, - { - str: "name1.name2[0].foo=bar,name1.name2[1].foo=bar", - expect: map[string]interface{}{ - "name1": map[string]interface{}{ - "name2": []map[string]interface{}{{"foo": "bar"}, {"foo": "bar"}}, - }, - }, - }, - { - str: "name1.name2[1].foo=bar,name1.name2[0].foo=bar", - expect: map[string]interface{}{ - "name1": map[string]interface{}{ - "name2": []map[string]interface{}{{"foo": "bar"}, {"foo": "bar"}}, - }, - }, - }, - { - str: "name1.name2[1].foo=bar", - expect: map[string]interface{}{ - "name1": map[string]interface{}{ - "name2": []map[string]interface{}{nil, {"foo": "bar"}}, - }, - }, - }, - } - - for _, tt := range tests { - got, err := Parse(tt.str) - if err != nil { - if tt.err { - continue - } - t.Fatalf("%s: %s", tt.str, err) - } - if tt.err { - t.Errorf("%s: Expected error. Got nil", tt.str) - } - - y1, err := yaml.Marshal(tt.expect) - if err != nil { - t.Fatal(err) - } - y2, err := yaml.Marshal(got) - if err != nil { - t.Fatalf("Error serializing parsed value: %s", err) - } - - if string(y1) != string(y2) { - t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.str, y1, y2) - } - } - for _, tt := range testsString { - got, err := ParseString(tt.str) - if err != nil { - if tt.err { - continue - } - t.Fatalf("%s: %s", tt.str, err) - } - if tt.err { - t.Errorf("%s: Expected error. Got nil", tt.str) - } - - y1, err := yaml.Marshal(tt.expect) - if err != nil { - t.Fatal(err) - } - y2, err := yaml.Marshal(got) - if err != nil { - t.Fatalf("Error serializing parsed value: %s", err) - } - - if string(y1) != string(y2) { - t.Errorf("%s: Expected:\n%s\nGot:\n%s", tt.str, y1, y2) - } - } -} - -func TestParseInto(t *testing.T) { - got := map[string]interface{}{ - "outer": map[string]interface{}{ - "inner1": "overwrite", - "inner2": "value2", - }, - } - input := "outer.inner1=value1,outer.inner3=value3,outer.inner4=4" - expect := map[string]interface{}{ - "outer": map[string]interface{}{ - "inner1": "value1", - "inner2": "value2", - "inner3": "value3", - "inner4": 4, - }, - } - - if err := ParseInto(input, got); err != nil { - t.Fatal(err) - } - - y1, err := yaml.Marshal(expect) - if err != nil { - t.Fatal(err) - } - y2, err := yaml.Marshal(got) - if err != nil { - t.Fatalf("Error serializing parsed value: %s", err) - } - - if string(y1) != string(y2) { - t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2) - } -} -func TestParseIntoString(t *testing.T) { - got := map[string]interface{}{ - "outer": map[string]interface{}{ - "inner1": "overwrite", - "inner2": "value2", - }, - } - input := "outer.inner1=1,outer.inner3=3" - expect := map[string]interface{}{ - "outer": map[string]interface{}{ - "inner1": "1", - "inner2": "value2", - "inner3": "3", - }, - } - - if err := ParseIntoString(input, got); err != nil { - t.Fatal(err) - } - - y1, err := yaml.Marshal(expect) - if err != nil { - t.Fatal(err) - } - y2, err := yaml.Marshal(got) - if err != nil { - t.Fatalf("Error serializing parsed value: %s", err) - } - - if string(y1) != string(y2) { - t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2) - } -} - -func TestParseIntoFile(t *testing.T) { - got := map[string]interface{}{} - input := "name1=path1" - expect := map[string]interface{}{ - "name1": "value1", - } - rs2v := func(rs []rune) (interface{}, error) { - v := string(rs) - if v != "path1" { - t.Errorf("%s: runesToVal: Expected value path1, got %s", input, v) - return "", nil - } - return "value1", nil - } - - if err := ParseIntoFile(input, got, rs2v); err != nil { - t.Fatal(err) - } - - y1, err := yaml.Marshal(expect) - if err != nil { - t.Fatal(err) - } - y2, err := yaml.Marshal(got) - if err != nil { - t.Fatalf("Error serializing parsed value: %s", err) - } - - if string(y1) != string(y2) { - t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2) - } -} - -func TestToYAML(t *testing.T) { - // The TestParse does the hard part. We just verify that YAML formatting is - // happening. - o, err := ToYAML("name=value") - if err != nil { - t.Fatal(err) - } - expect := "name: value" - if o != expect { - t.Errorf("Expected %q, got %q", expect, o) - } -} diff --git a/pkg/time/time.go b/pkg/time/time.go deleted file mode 100644 index 44f3fed..0000000 --- a/pkg/time/time.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package time contains a wrapper for time.Time in the standard library and -// associated methods. This package mainly exists to workaround an issue in Go -// where the serializer doesn't omit an empty value for time: -// https://github.com/golang/go/issues/11939. As such, this can be removed if a -// proposal is ever accepted for Go -package time - -import ( - "bytes" - "time" -) - -// emptyString contains an empty JSON string value to be used as output -var emptyString = `""` - -// Time is a convenience wrapper around stdlib time, but with different -// marshalling and unmarshaling for zero values -type Time struct { - time.Time -} - -// Now returns the current time. It is a convenience wrapper around time.Now() -func Now() Time { - return Time{time.Now()} -} - -func (t Time) MarshalJSON() ([]byte, error) { - if t.Time.IsZero() { - return []byte(emptyString), nil - } - - return t.Time.MarshalJSON() -} - -func (t *Time) UnmarshalJSON(b []byte) error { - if bytes.Equal(b, []byte("null")) { - return nil - } - // If it is empty, we don't have to set anything since time.Time is not a - // pointer and will be set to the zero value - if bytes.Equal([]byte(emptyString), b) { - return nil - } - - return t.Time.UnmarshalJSON(b) -} - -func Parse(layout, value string) (Time, error) { - t, err := time.Parse(layout, value) - return Time{Time: t}, err -} -func ParseInLocation(layout, value string, loc *time.Location) (Time, error) { - t, err := time.ParseInLocation(layout, value, loc) - return Time{Time: t}, err -} - -func Date(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) Time { - return Time{Time: time.Date(year, month, day, hour, min, sec, nsec, loc)} -} - -func Unix(sec int64, nsec int64) Time { return Time{Time: time.Unix(sec, nsec)} } - -func (t Time) Add(d time.Duration) Time { return Time{Time: t.Time.Add(d)} } -func (t Time) AddDate(years int, months int, days int) Time { - return Time{Time: t.Time.AddDate(years, months, days)} -} -func (t Time) After(u Time) bool { return t.Time.After(u.Time) } -func (t Time) Before(u Time) bool { return t.Time.Before(u.Time) } -func (t Time) Equal(u Time) bool { return t.Time.Equal(u.Time) } -func (t Time) In(loc *time.Location) Time { return Time{Time: t.Time.In(loc)} } -func (t Time) Local() Time { return Time{Time: t.Time.Local()} } -func (t Time) Round(d time.Duration) Time { return Time{Time: t.Time.Round(d)} } -func (t Time) Sub(u Time) time.Duration { return t.Time.Sub(u.Time) } -func (t Time) Truncate(d time.Duration) Time { return Time{Time: t.Time.Truncate(d)} } -func (t Time) UTC() Time { return Time{Time: t.Time.UTC()} } diff --git a/pkg/time/time_test.go b/pkg/time/time_test.go deleted file mode 100644 index 20f0f8e..0000000 --- a/pkg/time/time_test.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package time - -import ( - "encoding/json" - "testing" - "time" -) - -var ( - testingTime, _ = Parse(time.RFC3339, "1977-09-02T22:04:05Z") - testingTimeString = `"1977-09-02T22:04:05Z"` -) - -func TestNonZeroValueMarshal(t *testing.T) { - res, err := json.Marshal(testingTime) - if err != nil { - t.Fatal(err) - } - if testingTimeString != string(res) { - t.Errorf("expected a marshaled value of %s, got %s", testingTimeString, res) - } -} - -func TestZeroValueMarshal(t *testing.T) { - res, err := json.Marshal(Time{}) - if err != nil { - t.Fatal(err) - } - if string(res) != emptyString { - t.Errorf("expected zero value to marshal to empty string, got %s", res) - } -} - -func TestNonZeroValueUnmarshal(t *testing.T) { - var myTime Time - err := json.Unmarshal([]byte(testingTimeString), &myTime) - if err != nil { - t.Fatal(err) - } - if !myTime.Equal(testingTime) { - t.Errorf("expected time to be equal to %v, got %v", testingTime, myTime) - } -} - -func TestEmptyStringUnmarshal(t *testing.T) { - var myTime Time - err := json.Unmarshal([]byte(emptyString), &myTime) - if err != nil { - t.Fatal(err) - } - if !myTime.IsZero() { - t.Errorf("expected time to be equal to zero value, got %v", myTime) - } -} - -func TestZeroValueUnmarshal(t *testing.T) { - // This test ensures that we can unmarshal any time value that was output - // with the current go default value of "0001-01-01T00:00:00Z" - var myTime Time - err := json.Unmarshal([]byte(`"0001-01-01T00:00:00Z"`), &myTime) - if err != nil { - t.Fatal(err) - } - if !myTime.IsZero() { - t.Errorf("expected time to be equal to zero value, got %v", myTime) - } -} diff --git a/scripts/completions.bash b/scripts/completions.bash deleted file mode 100644 index 027cc33..0000000 --- a/scripts/completions.bash +++ /dev/null @@ -1,1632 +0,0 @@ -# bash completion for helm -*- shell-script -*- - -__debug() -{ - if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then - echo "$*" >> "${BASH_COMP_DEBUG_FILE}" - fi -} - -# Homebrew on Macs have version 1.3 of bash-completion which doesn't include -# _init_completion. This is a very minimal version of that function. -__my_init_completion() -{ - COMPREPLY=() - _get_comp_words_by_ref "$@" cur prev words cword -} - -__index_of_word() -{ - local w word=$1 - shift - index=0 - for w in "$@"; do - [[ $w = "$word" ]] && return - index=$((index+1)) - done - index=-1 -} - -__contains_word() -{ - local w word=$1; shift - for w in "$@"; do - [[ $w = "$word" ]] && return - done - return 1 -} - -__handle_reply() -{ - __debug "${FUNCNAME[0]}" - case $cur in - -*) - if [[ $(type -t compopt) = "builtin" ]]; then - compopt -o nospace - fi - local allflags - if [ ${#must_have_one_flag[@]} -ne 0 ]; then - allflags=("${must_have_one_flag[@]}") - else - allflags=("${flags[*]} ${two_word_flags[*]}") - fi - COMPREPLY=( $(compgen -W "${allflags[*]}" -- "$cur") ) - if [[ $(type -t compopt) = "builtin" ]]; then - [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace - fi - - # complete after --flag=abc - if [[ $cur == *=* ]]; then - if [[ $(type -t compopt) = "builtin" ]]; then - compopt +o nospace - fi - - local index flag - flag="${cur%%=*}" - __index_of_word "${flag}" "${flags_with_completion[@]}" - if [[ ${index} -ge 0 ]]; then - COMPREPLY=() - PREFIX="" - cur="${cur#*=}" - ${flags_completion[${index}]} - if [ -n "${ZSH_VERSION}" ]; then - # zfs completion needs --flag= prefix - eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" - fi - fi - fi - return 0; - ;; - esac - - # check if we are handling a flag with special work handling - local index - __index_of_word "${prev}" "${flags_with_completion[@]}" - if [[ ${index} -ge 0 ]]; then - ${flags_completion[${index}]} - return - fi - - # we are parsing a flag and don't have a special handler, no completion - if [[ ${cur} != "${words[cword]}" ]]; then - return - fi - - local completions - completions=("${commands[@]}") - if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then - completions=("${must_have_one_noun[@]}") - fi - if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then - completions+=("${must_have_one_flag[@]}") - fi - COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) - - if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then - COMPREPLY=( $(compgen -W "${noun_aliases[*]}" -- "$cur") ) - fi - - if [[ ${#COMPREPLY[@]} -eq 0 ]]; then - declare -F __custom_func >/dev/null && __custom_func - fi - - __ltrim_colon_completions "$cur" -} - -# The arguments should be in the form "ext1|ext2|extn" -__handle_filename_extension_flag() -{ - local ext="$1" - _filedir "@(${ext})" -} - -__handle_subdirs_in_dir_flag() -{ - local dir="$1" - pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 -} - -__handle_flag() -{ - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - - # if a command required a flag, and we found it, unset must_have_one_flag() - local flagname=${words[c]} - local flagvalue - # if the word contained an = - if [[ ${words[c]} == *"="* ]]; then - flagvalue=${flagname#*=} # take in as flagvalue after the = - flagname=${flagname%%=*} # strip everything after the = - flagname="${flagname}=" # but put the = back - fi - __debug "${FUNCNAME[0]}: looking for ${flagname}" - if __contains_word "${flagname}" "${must_have_one_flag[@]}"; then - must_have_one_flag=() - fi - - # if you set a flag which only applies to this command, don't show subcommands - if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then - commands=() - fi - - # keep flag value with flagname as flaghash - if [ -n "${flagvalue}" ] ; then - flaghash[${flagname}]=${flagvalue} - elif [ -n "${words[ $((c+1)) ]}" ] ; then - flaghash[${flagname}]=${words[ $((c+1)) ]} - else - flaghash[${flagname}]="true" # pad "true" for bool flag - fi - - # skip the argument to a two word flag - if __contains_word "${words[c]}" "${two_word_flags[@]}"; then - c=$((c+1)) - # if we are looking for a flags value, don't show commands - if [[ $c -eq $cword ]]; then - commands=() - fi - fi - - c=$((c+1)) - -} - -__handle_noun() -{ - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - - if __contains_word "${words[c]}" "${must_have_one_noun[@]}"; then - must_have_one_noun=() - elif __contains_word "${words[c]}" "${noun_aliases[@]}"; then - must_have_one_noun=() - fi - - nouns+=("${words[c]}") - c=$((c+1)) -} - -__handle_command() -{ - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - - local next_command - if [[ -n ${last_command} ]]; then - next_command="_${last_command}_${words[c]//:/__}" - else - if [[ $c -eq 0 ]]; then - next_command="_$(basename "${words[c]//:/__}")" - else - next_command="_${words[c]//:/__}" - fi - fi - c=$((c+1)) - __debug "${FUNCNAME[0]}: looking for ${next_command}" - declare -F $next_command >/dev/null && $next_command -} - -__handle_word() -{ - if [[ $c -ge $cword ]]; then - __handle_reply - return - fi - __debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" - if [[ "${words[c]}" == -* ]]; then - __handle_flag - elif __contains_word "${words[c]}" "${commands[@]}"; then - __handle_command - elif [[ $c -eq 0 ]] && __contains_word "$(basename "${words[c]}")" "${commands[@]}"; then - __handle_command - else - __handle_noun - fi - __handle_word -} - -_helm_completion() -{ - last_command="helm_completion" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - must_have_one_noun+=("bash") - must_have_one_noun+=("zsh") - noun_aliases=() -} - -_helm_create() -{ - last_command="helm_create" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--starter=") - two_word_flags+=("-p") - local_nonpersistent_flags+=("--starter=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_delete() -{ - last_command="helm_delete" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--no-hooks") - local_nonpersistent_flags+=("--no-hooks") - flags+=("--keep-history") - local_nonpersistent_flags+=("--keep-history") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_dependency_build() -{ - last_command="helm_dependency_build" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_dependency_list() -{ - last_command="helm_dependency_list" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_dependency_update() -{ - last_command="helm_dependency_update" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--skip-refresh") - local_nonpersistent_flags+=("--skip-refresh") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_dependency() -{ - last_command="helm_dependency" - commands=() - commands+=("build") - commands+=("list") - commands+=("update") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_fetch() -{ - last_command="helm_fetch" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--destination=") - two_word_flags+=("-d") - local_nonpersistent_flags+=("--destination=") - flags+=("--devel") - local_nonpersistent_flags+=("--devel") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--prov") - local_nonpersistent_flags+=("--prov") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--untar") - local_nonpersistent_flags+=("--untar") - flags+=("--untardir=") - local_nonpersistent_flags+=("--untardir=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_get_hooks() -{ - last_command="helm_get_hooks" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--revision=") - local_nonpersistent_flags+=("--revision=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_get_manifest() -{ - last_command="helm_get_manifest" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--revision=") - local_nonpersistent_flags+=("--revision=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_get_values() -{ - last_command="helm_get_values" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--all") - flags+=("-a") - local_nonpersistent_flags+=("--all") - flags+=("--revision=") - local_nonpersistent_flags+=("--revision=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_get() -{ - last_command="helm_get" - commands=() - commands+=("hooks") - commands+=("manifest") - commands+=("values") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--revision=") - local_nonpersistent_flags+=("--revision=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_history() -{ - last_command="helm_history" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--max=") - local_nonpersistent_flags+=("--max=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_home() -{ - last_command="helm_home" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_init() -{ - last_command="helm_init" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--canary-image") - local_nonpersistent_flags+=("--canary-image") - flags+=("--client-only") - flags+=("-c") - local_nonpersistent_flags+=("--client-only") - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--local-repo-url=") - local_nonpersistent_flags+=("--local-repo-url=") - flags+=("--net-host") - local_nonpersistent_flags+=("--net-host") - flags+=("--service-account=") - local_nonpersistent_flags+=("--service-account=") - flags+=("--skip-refresh") - local_nonpersistent_flags+=("--skip-refresh") - flags+=("--tiller-image=") - two_word_flags+=("-i") - local_nonpersistent_flags+=("--tiller-image=") - flags+=("--tiller-tls") - local_nonpersistent_flags+=("--tiller-tls") - flags+=("--tiller-tls-cert=") - local_nonpersistent_flags+=("--tiller-tls-cert=") - flags+=("--tiller-tls-key=") - local_nonpersistent_flags+=("--tiller-tls-key=") - flags+=("--tiller-tls-verify") - local_nonpersistent_flags+=("--tiller-tls-verify") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--upgrade") - local_nonpersistent_flags+=("--upgrade") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_inspect_chart() -{ - last_command="helm_inspect_chart" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_inspect_values() -{ - last_command="helm_inspect_values" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_inspect() -{ - last_command="helm_inspect" - commands=() - commands+=("chart") - commands+=("values") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_install() -{ - last_command="helm_install" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--devel") - local_nonpersistent_flags+=("--devel") - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--name=") - two_word_flags+=("-n") - local_nonpersistent_flags+=("--name=") - flags+=("--name-template=") - local_nonpersistent_flags+=("--name-template=") - flags+=("--namespace=") - local_nonpersistent_flags+=("--namespace=") - flags+=("--no-hooks") - local_nonpersistent_flags+=("--no-hooks") - flags+=("--replace") - local_nonpersistent_flags+=("--replace") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--set=") - local_nonpersistent_flags+=("--set=") - flags+=("--set-string=") - local_nonpersistent_flags+=("--set-string=") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--values=") - two_word_flags+=("-f") - local_nonpersistent_flags+=("--values=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--wait") - local_nonpersistent_flags+=("--wait") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_lint() -{ - last_command="helm_lint" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--strict") - local_nonpersistent_flags+=("--strict") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_list() -{ - last_command="helm_list" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--all") - local_nonpersistent_flags+=("--all") - flags+=("--date") - flags+=("-d") - local_nonpersistent_flags+=("--date") - flags+=("--uninstalled") - local_nonpersistent_flags+=("--uninstalled") - flags+=("--uninstalling") - local_nonpersistent_flags+=("--uninstalling") - flags+=("--deployed") - local_nonpersistent_flags+=("--deployed") - flags+=("--failed") - local_nonpersistent_flags+=("--failed") - flags+=("--max=") - two_word_flags+=("-m") - local_nonpersistent_flags+=("--max=") - flags+=("--namespace=") - local_nonpersistent_flags+=("--namespace=") - flags+=("--offset=") - two_word_flags+=("-o") - local_nonpersistent_flags+=("--offset=") - flags+=("--reverse") - flags+=("-r") - local_nonpersistent_flags+=("--reverse") - flags+=("--short") - flags+=("-q") - local_nonpersistent_flags+=("--short") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_package() -{ - last_command="helm_package" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--destination=") - two_word_flags+=("-d") - local_nonpersistent_flags+=("--destination=") - flags+=("--key=") - local_nonpersistent_flags+=("--key=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--save") - local_nonpersistent_flags+=("--save") - flags+=("--sign") - local_nonpersistent_flags+=("--sign") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin_install() -{ - last_command="helm_plugin_install" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin_list() -{ - last_command="helm_plugin_list" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin_remove() -{ - last_command="helm_plugin_remove" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin_update() -{ - last_command="helm_plugin_update" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_plugin() -{ - last_command="helm_plugin" - commands=() - commands+=("install") - commands+=("list") - commands+=("remove") - commands+=("update") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_add() -{ - last_command="helm_repo_add" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--no-update") - local_nonpersistent_flags+=("--no-update") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_index() -{ - last_command="helm_repo_index" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--merge=") - local_nonpersistent_flags+=("--merge=") - flags+=("--url=") - local_nonpersistent_flags+=("--url=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_list() -{ - last_command="helm_repo_list" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_remove() -{ - last_command="helm_repo_remove" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo_update() -{ - last_command="helm_repo_update" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_repo() -{ - last_command="helm_repo" - commands=() - commands+=("add") - commands+=("index") - commands+=("list") - commands+=("remove") - commands+=("update") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_reset() -{ - last_command="helm_reset" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--force") - flags+=("-f") - local_nonpersistent_flags+=("--force") - flags+=("--remove-helm-home") - local_nonpersistent_flags+=("--remove-helm-home") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_rollback() -{ - last_command="helm_rollback" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--force") - local_nonpersistent_flags+=("--force") - flags+=("--no-hooks") - local_nonpersistent_flags+=("--no-hooks") - flags+=("--recreate-pods") - local_nonpersistent_flags+=("--recreate-pods") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--wait") - local_nonpersistent_flags+=("--wait") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_search() -{ - last_command="helm_search" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--regexp") - flags+=("-r") - local_nonpersistent_flags+=("--regexp") - flags+=("--version=") - two_word_flags+=("-v") - local_nonpersistent_flags+=("--version=") - flags+=("--versions") - flags+=("-l") - local_nonpersistent_flags+=("--versions") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_serve() -{ - last_command="helm_serve" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--address=") - local_nonpersistent_flags+=("--address=") - flags+=("--repo-path=") - local_nonpersistent_flags+=("--repo-path=") - flags+=("--url=") - local_nonpersistent_flags+=("--url=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_status() -{ - last_command="helm_status" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--revision=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_test() -{ - last_command="helm_test" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--cleanup") - local_nonpersistent_flags+=("--cleanup") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_upgrade() -{ - last_command="helm_upgrade" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--ca-file=") - local_nonpersistent_flags+=("--ca-file=") - flags+=("--cert-file=") - local_nonpersistent_flags+=("--cert-file=") - flags+=("--devel") - local_nonpersistent_flags+=("--devel") - flags+=("--disable-hooks") - local_nonpersistent_flags+=("--disable-hooks") - flags+=("--dry-run") - local_nonpersistent_flags+=("--dry-run") - flags+=("--force") - local_nonpersistent_flags+=("--force") - flags+=("--install") - flags+=("-i") - local_nonpersistent_flags+=("--install") - flags+=("--key-file=") - local_nonpersistent_flags+=("--key-file=") - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--namespace=") - local_nonpersistent_flags+=("--namespace=") - flags+=("--no-hooks") - local_nonpersistent_flags+=("--no-hooks") - flags+=("--recreate-pods") - local_nonpersistent_flags+=("--recreate-pods") - flags+=("--repo=") - local_nonpersistent_flags+=("--repo=") - flags+=("--reset-values") - local_nonpersistent_flags+=("--reset-values") - flags+=("--reuse-values") - local_nonpersistent_flags+=("--reuse-values") - flags+=("--set=") - local_nonpersistent_flags+=("--set=") - flags+=("--timeout=") - local_nonpersistent_flags+=("--timeout=") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--values=") - two_word_flags+=("-f") - local_nonpersistent_flags+=("--values=") - flags+=("--verify") - local_nonpersistent_flags+=("--verify") - flags+=("--version=") - local_nonpersistent_flags+=("--version=") - flags+=("--wait") - local_nonpersistent_flags+=("--wait") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_verify() -{ - last_command="helm_verify" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--keyring=") - local_nonpersistent_flags+=("--keyring=") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm_version() -{ - last_command="helm_version" - commands=() - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--client") - flags+=("-c") - local_nonpersistent_flags+=("--client") - flags+=("--server") - flags+=("-s") - local_nonpersistent_flags+=("--server") - flags+=("--short") - local_nonpersistent_flags+=("--short") - flags+=("--tls") - local_nonpersistent_flags+=("--tls") - flags+=("--tls-ca-cert=") - local_nonpersistent_flags+=("--tls-ca-cert=") - flags+=("--tls-cert=") - local_nonpersistent_flags+=("--tls-cert=") - flags+=("--tls-key=") - local_nonpersistent_flags+=("--tls-key=") - flags+=("--tls-verify") - local_nonpersistent_flags+=("--tls-verify") - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -_helm() -{ - last_command="helm" - commands=() - commands+=("completion") - commands+=("create") - commands+=("delete") - commands+=("dependency") - commands+=("fetch") - commands+=("get") - commands+=("history") - commands+=("home") - commands+=("init") - commands+=("inspect") - commands+=("install") - commands+=("lint") - commands+=("list") - commands+=("package") - commands+=("plugin") - commands+=("repo") - commands+=("reset") - commands+=("rollback") - commands+=("search") - commands+=("serve") - commands+=("status") - commands+=("test") - commands+=("upgrade") - commands+=("verify") - commands+=("version") - - flags=() - two_word_flags=() - local_nonpersistent_flags=() - flags_with_completion=() - flags_completion=() - - flags+=("--debug") - flags+=("--host=") - flags+=("--kube-context=") - flags+=("--tiller-namespace=") - - must_have_one_flag=() - must_have_one_noun=() - noun_aliases=() -} - -__start_helm() -{ - local cur prev words cword - declare -A flaghash 2>/dev/null || : - if declare -F _init_completion >/dev/null 2>&1; then - _init_completion -s || return - else - __my_init_completion -n "=" || return - fi - - local c=0 - local flags=() - local two_word_flags=() - local local_nonpersistent_flags=() - local flags_with_completion=() - local flags_completion=() - local commands=("helm") - local must_have_one_flag=() - local must_have_one_noun=() - local last_command - local nouns=() - - __handle_word -} - -if [[ $(type -t compopt) = "builtin" ]]; then - complete -o default -F __start_helm helm -else - complete -o default -o nospace -F __start_helm helm -fi - -# ex: ts=4 sw=4 et filetype=sh diff --git a/scripts/coverage.sh b/scripts/coverage.sh deleted file mode 100755 index bdbfaa9..0000000 --- a/scripts/coverage.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The Helm Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -euo pipefail - -covermode=${COVERMODE:-atomic} -coverdir=$(mktemp -d /tmp/coverage.XXXXXXXXXX) -profile="${coverdir}/cover.out" - -pushd / -hash goveralls 2>/dev/null || go get github.com/mattn/goveralls -popd - -generate_cover_data() { - for d in $(go list ./...) ; do - ( - local output="${coverdir}/${d//\//-}.cover" - go test -coverprofile="${output}" -covermode="$covermode" "$d" - ) - done - - echo "mode: $covermode" >"$profile" - grep -h -v "^mode:" "$coverdir"/*.cover >>"$profile" -} - -push_to_coveralls() { - goveralls -coverprofile="${profile}" -service=circle-ci -} - -generate_cover_data -go tool cover -func "${profile}" - -case "${1-}" in - --html) - go tool cover -html "${profile}" - ;; - --coveralls) - push_to_coveralls - ;; -esac - diff --git a/scripts/get b/scripts/get deleted file mode 100755 index 711635e..0000000 --- a/scripts/get +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The Helm Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# The install script is based off of the MIT-licensed script from glide, -# the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get - -PROJECT_NAME="helm" -TILLER_NAME="tiller" - -: ${USE_SUDO:="true"} -: ${HELM_INSTALL_DIR:="/usr/local/bin"} - -# initArch discovers the architecture for this system. -initArch() { - ARCH=$(uname -m) - case $ARCH in - armv5*) ARCH="armv5";; - armv6*) ARCH="armv6";; - armv7*) ARCH="arm";; - aarch64) ARCH="arm64";; - x86) ARCH="386";; - x86_64) ARCH="amd64";; - i686) ARCH="386";; - i386) ARCH="386";; - esac -} - -# initOS discovers the operating system for this system. -initOS() { - OS=$(echo `uname`|tr '[:upper:]' '[:lower:]') - - case "$OS" in - # Minimalist GNU for Windows - mingw*) OS='windows';; - esac -} - -# runs the given command as root (detects if we are root already) -runAsRoot() { - local CMD="$*" - - if [ $EUID -ne 0 -a $USE_SUDO = "true" ]; then - CMD="sudo $CMD" - fi - - $CMD -} - -# verifySupported checks that the os/arch combination is supported for -# binary builds. -verifySupported() { - local supported="darwin-386\ndarwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nwindows-386\nwindows-amd64" - if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then - echo "No prebuilt binary for ${OS}-${ARCH}." - echo "To build from source, go to https://github.com/helm/helm" - exit 1 - fi - - if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then - echo "Either curl or wget is required" - exit 1 - fi -} - -# checkDesiredVersion checks if the desired version is available. -checkDesiredVersion() { - if [ "x$DESIRED_VERSION" == "x" ]; then - # FIXME(bacongobbler): hard code the desired version for the time being. - # A better fix would be to filter for Helm 2 release pages. - TAG="v2.16.1" - # Get tag from release URL - # local latest_release_url="https://github.com/helm/helm/releases/latest" - # if type "curl" > /dev/null; then - # TAG=$(curl -Ls -o /dev/null -w %{url_effective} $latest_release_url | grep -oE "[^/]+$" ) - # elif type "wget" > /dev/null; then - # TAG=$(wget $latest_release_url --server-response -O /dev/null 2>&1 | awk '/^ Location: /{DEST=$2} END{ print DEST}' | grep -oE "[^/]+$") - # fi - else - TAG=$DESIRED_VERSION - fi -} - -# checkHelmInstalledVersion checks which version of helm is installed and -# if it needs to be changed. -checkHelmInstalledVersion() { - if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then - local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version -c | grep '^Client' | cut -d'"' -f2) - if [[ "$version" == "$TAG" ]]; then - echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" - return 0 - else - echo "Helm ${TAG} is available. Changing from version ${version}." - return 1 - fi - else - return 1 - fi -} - -# downloadFile downloads the latest binary package and also the checksum -# for that binary. -downloadFile() { - HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz" - DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST" - CHECKSUM_URL="$DOWNLOAD_URL.sha256" - HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)" - HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST" - HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256" - echo "Downloading $DOWNLOAD_URL" - if type "curl" > /dev/null; then - curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE" - elif type "wget" > /dev/null; then - wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL" - fi - if type "curl" > /dev/null; then - curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE" - elif type "wget" > /dev/null; then - wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL" - fi -} - -# installFile verifies the SHA256 for the file, then unpacks and -# installs it. -installFile() { - HELM_TMP="$HELM_TMP_ROOT/$PROJECT_NAME" - local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}') - local expected_sum=$(cat ${HELM_SUM_FILE}) - if [ "$sum" != "$expected_sum" ]; then - echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting." - exit 1 - fi - - mkdir -p "$HELM_TMP" - tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" - HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME" - TILLER_TMP_BIN="$HELM_TMP/$OS-$ARCH/$TILLER_NAME" - echo "Preparing to install $PROJECT_NAME and $TILLER_NAME into ${HELM_INSTALL_DIR}" - runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR" - echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME" - if [ -x "$TILLER_TMP_BIN" ]; then - runAsRoot cp "$TILLER_TMP_BIN" "$HELM_INSTALL_DIR" - echo "$TILLER_NAME installed into $HELM_INSTALL_DIR/$TILLER_NAME" - else - echo "info: $TILLER_NAME binary was not found in this release; skipping $TILLER_NAME installation" - fi -} - -# fail_trap is executed if an error occurs. -fail_trap() { - result=$? - if [ "$result" != "0" ]; then - if [[ -n "$INPUT_ARGUMENTS" ]]; then - echo "Failed to install $PROJECT_NAME with the arguments provided: $INPUT_ARGUMENTS" - help - else - echo "Failed to install $PROJECT_NAME" - fi - echo -e "\tFor support, go to https://github.com/helm/helm." - fi - cleanup - exit $result -} - -# testVersion tests the installed client to make sure it is working. -testVersion() { - set +e - HELM="$(which $PROJECT_NAME)" - if [ "$?" = "1" ]; then - echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' - exit 1 - fi - set -e - echo "Run '$PROJECT_NAME init' to configure $PROJECT_NAME." -} - -# help provides possible cli installation arguments -help () { - echo "Accepted cli arguments are:" - echo -e "\t[--help|-h ] ->> prints this help" - echo -e "\t[--version|-v ]" - echo -e "\te.g. --version v2.4.0 or -v latest" - echo -e "\t[--no-sudo] ->> install without sudo" -} - -# cleanup temporary files to avoid https://github.com/helm/helm/issues/2977 -cleanup() { - if [[ -d "${HELM_TMP_ROOT:-}" ]]; then - rm -rf "$HELM_TMP_ROOT" - fi -} - -# Execution - -#Stop execution on any error -trap "fail_trap" EXIT -set -e - -# Parsing input arguments (if any) -export INPUT_ARGUMENTS="${@}" -set -u -while [[ $# -gt 0 ]]; do - case $1 in - '--version'|-v) - shift - if [[ $# -ne 0 ]]; then - export DESIRED_VERSION="${1}" - else - echo -e "Please provide the desired version. e.g. --version v2.4.0 or -v latest" - exit 0 - fi - ;; - '--no-sudo') - USE_SUDO="false" - ;; - '--help'|-h) - help - exit 0 - ;; - *) exit 1 - ;; - esac - shift -done -set +u - -initArch -initOS -verifySupported -checkDesiredVersion -if ! checkHelmInstalledVersion; then - downloadFile - installFile -fi -testVersion -cleanup diff --git a/scripts/get-helm-3 b/scripts/get-helm-3 deleted file mode 100755 index c1655a6..0000000 --- a/scripts/get-helm-3 +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The Helm Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# The install script is based off of the MIT-licensed script from glide, -# the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get - -PROJECT_NAME="helm" - -: ${USE_SUDO:="true"} -: ${HELM_INSTALL_DIR:="/usr/local/bin"} - -# initArch discovers the architecture for this system. -initArch() { - ARCH=$(uname -m) - case $ARCH in - armv5*) ARCH="armv5";; - armv6*) ARCH="armv6";; - armv7*) ARCH="arm";; - aarch64) ARCH="arm64";; - x86) ARCH="386";; - x86_64) ARCH="amd64";; - i686) ARCH="386";; - i386) ARCH="386";; - esac -} - -# initOS discovers the operating system for this system. -initOS() { - OS=$(echo `uname`|tr '[:upper:]' '[:lower:]') - - case "$OS" in - # Minimalist GNU for Windows - mingw*) OS='windows';; - esac -} - -# runs the given command as root (detects if we are root already) -runAsRoot() { - local CMD="$*" - - if [ $EUID -ne 0 -a $USE_SUDO = "true" ]; then - CMD="sudo $CMD" - fi - - $CMD -} - -# verifySupported checks that the os/arch combination is supported for -# binary builds. -verifySupported() { - local supported="darwin-386\ndarwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nwindows-386\nwindows-amd64" - if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then - echo "No prebuilt binary for ${OS}-${ARCH}." - echo "To build from source, go to https://github.com/helm/helm" - exit 1 - fi - - if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then - echo "Either curl or wget is required" - exit 1 - fi -} - -# checkDesiredVersion checks if the desired version is available. -checkDesiredVersion() { - if [ "x$DESIRED_VERSION" == "x" ]; then - # Get tag from release URL - local latest_release_url="https://github.com/helm/helm/releases/latest" - if type "curl" > /dev/null; then - TAG=$(curl -Ls -o /dev/null -w %{url_effective} $latest_release_url | grep -oE "[^/]+$" ) - elif type "wget" > /dev/null; then - TAG=$(wget $latest_release_url --server-response -O /dev/null 2>&1 | awk '/^ Location: /{DEST=$2} END{ print DEST}' | grep -oE "[^/]+$") - fi - else - TAG=$DESIRED_VERSION - fi -} - -# checkHelmInstalledVersion checks which version of helm is installed and -# if it needs to be changed. -checkHelmInstalledVersion() { - if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then - local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version --template="{{ .Version }}") - if [[ "$version" == "$TAG" ]]; then - echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" - return 0 - else - echo "Helm ${TAG} is available. Changing from version ${version}." - return 1 - fi - else - return 1 - fi -} - -# downloadFile downloads the latest binary package and also the checksum -# for that binary. -downloadFile() { - HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz" - DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST" - CHECKSUM_URL="$DOWNLOAD_URL.sha256" - HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)" - HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST" - HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256" - echo "Downloading $DOWNLOAD_URL" - if type "curl" > /dev/null; then - curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE" - elif type "wget" > /dev/null; then - wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL" - fi - if type "curl" > /dev/null; then - curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE" - elif type "wget" > /dev/null; then - wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL" - fi -} - -# installFile verifies the SHA256 for the file, then unpacks and -# installs it. -installFile() { - HELM_TMP="$HELM_TMP_ROOT/$PROJECT_NAME" - local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}') - local expected_sum=$(cat ${HELM_SUM_FILE}) - if [ "$sum" != "$expected_sum" ]; then - echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting." - exit 1 - fi - - mkdir -p "$HELM_TMP" - tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" - HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME" - echo "Preparing to install $PROJECT_NAME into ${HELM_INSTALL_DIR}" - runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR" - echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME" -} - -# fail_trap is executed if an error occurs. -fail_trap() { - result=$? - if [ "$result" != "0" ]; then - if [[ -n "$INPUT_ARGUMENTS" ]]; then - echo "Failed to install $PROJECT_NAME with the arguments provided: $INPUT_ARGUMENTS" - help - else - echo "Failed to install $PROJECT_NAME" - fi - echo -e "\tFor support, go to https://github.com/helm/helm." - fi - cleanup - exit $result -} - -# testVersion tests the installed client to make sure it is working. -testVersion() { - set +e - HELM="$(which $PROJECT_NAME)" - if [ "$?" = "1" ]; then - echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' - exit 1 - fi - set -e -} - -# help provides possible cli installation arguments -help () { - echo "Accepted cli arguments are:" - echo -e "\t[--help|-h ] ->> prints this help" - echo -e "\t[--version|-v ] . When not defined it fetches the latest release from GitHub" - echo -e "\te.g. --version v3.0.0 or -v canary" - echo -e "\t[--no-sudo] ->> install without sudo" -} - -# cleanup temporary files to avoid https://github.com/helm/helm/issues/2977 -cleanup() { - if [[ -d "${HELM_TMP_ROOT:-}" ]]; then - rm -rf "$HELM_TMP_ROOT" - fi -} - -# Execution - -#Stop execution on any error -trap "fail_trap" EXIT -set -e - -# Parsing input arguments (if any) -export INPUT_ARGUMENTS="${@}" -set -u -while [[ $# -gt 0 ]]; do - case $1 in - '--version'|-v) - shift - if [[ $# -ne 0 ]]; then - export DESIRED_VERSION="${1}" - else - echo -e "Please provide the desired version. e.g. --version v3.0.0 or -v canary" - exit 0 - fi - ;; - '--no-sudo') - USE_SUDO="false" - ;; - '--help'|-h) - help - exit 0 - ;; - *) exit 1 - ;; - esac - shift -done -set +u - -initArch -initOS -verifySupported -checkDesiredVersion -if ! checkHelmInstalledVersion; then - downloadFile - installFile -fi -testVersion -cleanup diff --git a/scripts/sync-repo.sh b/scripts/sync-repo.sh deleted file mode 100755 index 4531020..0000000 --- a/scripts/sync-repo.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The Helm Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Bash 'Strict Mode' -# http://redsymbol.net/articles/unofficial-bash-strict-mode -set -euo pipefail -IFS=$'\n\t' - -# Helper Functions ------------------------------------------------------------- - -# Display error message and exit -error_exit() { - echo "error: ${1:-"unknown error"}" 1>&2 - exit 1 -} - -# Checks if a command exists. Returns 1 or 0 -command_exists() { - hash "${1}" 2>/dev/null -} - -# Program Functions ------------------------------------------------------------ - -verify_prereqs() { - echo "Verifying Prerequisites...." - if command_exists gsutil; then - echo "Thumbs up! Looks like you have gsutil. Let's continue." - else - error_exit "Couldn't find gsutil. Bailing out." - fi -} - -confirm() { - case $response in - [yY][eE][sS]|[yY]) - true - ;; - *) - false - ;; - esac -} - -# Main ------------------------------------------------------------------------- - -main() { - if [ "$#" -ne 2 ]; then - error_exit "Illegal number of parameters. You must pass in local directory path and a GCS bucket name" - fi - - echo "Getting ready to sync your local directory ($1) to a remote repository at gs://$2" - - verify_prereqs - - # dry run of the command - gsutil rsync -d -n $1 gs://$2 - - read -p "Are you sure you would like to continue with these changes? [y/N]} " confirm - if [[ $confirm =~ [yY](es)* ]]; then - gsutil rsync -d $1 gs://$2 - else - error_exit "Discontinuing sync process." - fi - - echo "Your remote chart repository now matches the contents of the $1 directory!" - -} - -main "${@:-}" diff --git a/scripts/util.sh b/scripts/util.sh deleted file mode 100644 index c1e6c37..0000000 --- a/scripts/util.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The Helm Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -euo pipefail - -# Example: kube::util::trap_add 'echo "in trap DEBUG"' DEBUG -# See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal -kube::util::trap_add() { - local trap_add_cmd - trap_add_cmd=$1 - shift - - for trap_add_name in "$@"; do - local existing_cmd - local new_cmd - - # Grab the currently defined trap commands for this trap - existing_cmd=`trap -p "${trap_add_name}" | awk -F"'" '{print $2}'` - - if [[ -z "${existing_cmd}" ]]; then - new_cmd="${trap_add_cmd}" - else - new_cmd="${existing_cmd};${trap_add_cmd}" - fi - - # Assign the test - trap "${new_cmd}" "${trap_add_name}" - done -} - -# Opposite of kube::util::ensure-temp-dir() -kube::util::cleanup-temp-dir() { - rm -rf "${KUBE_TEMP}" -} - -# Create a temp dir that'll be deleted at the end of this bash session. -# -# Vars set: -# KUBE_TEMP -kube::util::ensure-temp-dir() { - if [[ -z ${KUBE_TEMP-} ]]; then - KUBE_TEMP=$(mktemp -d 2>/dev/null || mktemp -d -t kubernetes.XXXXXX) - kube::util::trap_add kube::util::cleanup-temp-dir EXIT - fi -} diff --git a/scripts/validate-license.sh b/scripts/validate-license.sh deleted file mode 100755 index dc24743..0000000 --- a/scripts/validate-license.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -# Copyright The Helm Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -euo pipefail -IFS=$'\n\t' - -find_files() { - find . -not \( \ - \( \ - -wholename './vendor' \ - -o -wholename '*testdata*' \ - -o -wholename '*third_party*' \ - \) -prune \ - \) \ - \( -name '*.go' -o -name '*.sh' \) -} - -# Use "|| :" to ignore the error code when grep returns empty -failed_license_header=($(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License")' || :)) -if (( ${#failed_license_header[@]} > 0 )); then - echo "Some source files are missing license headers." - printf '%s\n' "${failed_license_header[@]}" - exit 1 -fi - -# Use "|| :" to ignore the error code when grep returns empty -failed_copyright_header=($(find_files | xargs grep -L 'Copyright The Helm Authors.' || :)) -if (( ${#failed_copyright_header[@]} > 0 )); then - echo "Some source files are missing the copyright header." - printf '%s\n' "${failed_copyright_header[@]}" - exit 1 -fi diff --git a/testdata/crt.pem b/testdata/crt.pem deleted file mode 100644 index 715cd0f..0000000 --- a/testdata/crt.pem +++ /dev/null @@ -1,73 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 55:31:53:9b:41:72:05:dc:90:49:bd:48:13:7c:59:9e:5a:53:5e:86 - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=CO, L=Boulder, O=Helm, CN=helm.sh - Validity - Not Before: Nov 1 22:51:49 2019 GMT - Not After : Oct 29 22:51:49 2029 GMT - Subject: C=US, ST=CO, L=Boulder, O=Helm, CN=helm.sh - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public-Key: (2048 bit) - Modulus: - 00:c8:89:55:0d:0b:f1:da:e6:c0:70:7d:d3:27:cd: - b8:a8:81:8b:7c:a4:89:e5:d1:b1:78:01:1d:df:44: - 88:0b:fc:d6:81:35:3d:d1:3b:5e:8f:bb:93:b3:7e: - 28:db:ed:ff:a0:13:3a:70:a3:fe:94:6b:0b:fe:fb: - 63:00:b0:cb:dc:81:cd:80:dc:d0:2f:bf:b2:4f:9a: - 81:d4:22:dc:97:c8:8f:27:86:59:91:fa:92:05:75: - c4:cc:6b:f5:a9:6b:74:1e:f5:db:a9:f8:bf:8c:a2: - 25:fd:a0:cc:79:f4:25:57:74:a9:23:9b:e2:b7:22: - 7a:14:7a:3d:ea:f1:7e:32:6b:57:6c:2e:c6:4f:75: - 54:f9:6b:54:d2:ca:eb:54:1c:af:39:15:9b:d0:7c: - 0f:f8:55:51:04:ea:da:fa:7b:8b:63:0f:ac:39:b1: - f6:4b:8e:4e:f6:ea:e9:7b:e6:ba:5e:5a:8e:91:ef: - dc:b1:7d:52:3f:73:83:52:46:83:48:49:ff:f2:2d: - ca:54:f2:36:bb:49:cc:59:99:c0:9e:cf:8e:78:55: - 6c:ed:7d:7e:83:b8:59:2c:7d:f8:1a:81:f0:7d:f5: - 27:f2:db:ae:d4:31:54:38:fe:47:b2:ee:16:20:0f: - f1:db:2d:28:bf:6f:38:eb:11:bb:9a:d4:b2:5a:3a: - 4a:7f - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Alternative Name: - DNS:helm.sh, IP Address:127.0.0.1 - Signature Algorithm: sha256WithRSAEncryption - 4e:17:27:3d:36:4e:6c:2b:f7:d4:28:33:7e:05:26:7a:42:a0: - 2c:44:57:04:a0:de:df:40:fb:af:70:27:e6:55:20:f1:f8:c0: - 50:63:ab:b8:f1:31:5d:1e:f4:ca:8d:65:0b:d4:5e:5b:77:2f: - 2a:af:74:5f:18:2d:92:29:7f:2d:97:fb:ec:aa:e3:1e:db:b3: - 8d:01:aa:82:1a:f6:28:a8:b3:ee:15:9f:9a:f5:76:37:30:f2: - 3b:38:13:b2:d4:14:94:c6:38:fa:f9:6e:94:e8:1f:11:0b:b0: - 69:1a:b3:f9:f1:27:b4:d2:f5:64:54:7c:8f:e7:83:31:f6:0d: - a7:0e:0e:66:d8:33:2f:e0:a1:93:56:92:58:bf:50:da:56:8e: - db:42:22:f5:0c:6f:f8:4c:ef:f5:7c:2d:a6:b8:60:e4:bb:df: - a3:6c:c2:6b:99:0b:d3:0a:ad:7c:f4:74:72:9a:52:5e:81:d9: - a2:a2:dd:68:38:fb:b7:54:7f:f6:aa:ee:53:de:3d:3a:0e:86: - 53:ad:af:72:db:fb:6b:18:ce:ac:e4:64:70:13:68:da:be:e1: - 6b:46:dd:a0:72:96:9b:3f:ba:cf:11:6e:98:03:0a:69:83:9e: - 37:25:c9:36:b9:68:4f:73:ca:c6:32:5c:be:46:64:bb:a8:cc: - 71:25:8f:be ------BEGIN CERTIFICATE----- -MIIDRDCCAiygAwIBAgIUVTFTm0FyBdyQSb1IE3xZnlpTXoYwDQYJKoZIhvcNAQEL -BQAwTTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNPMRAwDgYDVQQHDAdCb3VsZGVy -MQ0wCwYDVQQKDARIZWxtMRAwDgYDVQQDDAdoZWxtLnNoMB4XDTE5MTEwMTIyNTE0 -OVoXDTI5MTAyOTIyNTE0OVowTTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNPMRAw -DgYDVQQHDAdCb3VsZGVyMQ0wCwYDVQQKDARIZWxtMRAwDgYDVQQDDAdoZWxtLnNo -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyIlVDQvx2ubAcH3TJ824 -qIGLfKSJ5dGxeAEd30SIC/zWgTU90Ttej7uTs34o2+3/oBM6cKP+lGsL/vtjALDL -3IHNgNzQL7+yT5qB1CLcl8iPJ4ZZkfqSBXXEzGv1qWt0HvXbqfi/jKIl/aDMefQl -V3SpI5vityJ6FHo96vF+MmtXbC7GT3VU+WtU0srrVByvORWb0HwP+FVRBOra+nuL -Yw+sObH2S45O9urpe+a6XlqOke/csX1SP3ODUkaDSEn/8i3KVPI2u0nMWZnAns+O -eFVs7X1+g7hZLH34GoHwffUn8tuu1DFUOP5Hsu4WIA/x2y0ov2846xG7mtSyWjpK -fwIDAQABoxwwGjAYBgNVHREEETAPggdoZWxtLnNohwR/AAABMA0GCSqGSIb3DQEB -CwUAA4IBAQBOFyc9Nk5sK/fUKDN+BSZ6QqAsRFcEoN7fQPuvcCfmVSDx+MBQY6u4 -8TFdHvTKjWUL1F5bdy8qr3RfGC2SKX8tl/vsquMe27ONAaqCGvYoqLPuFZ+a9XY3 -MPI7OBOy1BSUxjj6+W6U6B8RC7BpGrP58Se00vVkVHyP54Mx9g2nDg5m2DMv4KGT -VpJYv1DaVo7bQiL1DG/4TO/1fC2muGDku9+jbMJrmQvTCq189HRymlJegdmiot1o -OPu3VH/2qu5T3j06DoZTra9y2/trGM6s5GRwE2javuFrRt2gcpabP7rPEW6YAwpp -g543Jck2uWhPc8rGMly+RmS7qMxxJY++ ------END CERTIFICATE----- diff --git a/testdata/generate.sh b/testdata/generate.sh deleted file mode 100755 index 9751ef3..0000000 --- a/testdata/generate.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -openssl req -new -config openssl.conf -key key.pem -out key.csr -openssl ca -config openssl.conf -create_serial -batch -in key.csr -out crt.pem -key rootca.key -cert rootca.crt diff --git a/testdata/key.pem b/testdata/key.pem deleted file mode 100644 index 691e550..0000000 --- a/testdata/key.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpgIBAAKCAQEAyIlVDQvx2ubAcH3TJ824qIGLfKSJ5dGxeAEd30SIC/zWgTU9 -0Ttej7uTs34o2+3/oBM6cKP+lGsL/vtjALDL3IHNgNzQL7+yT5qB1CLcl8iPJ4ZZ -kfqSBXXEzGv1qWt0HvXbqfi/jKIl/aDMefQlV3SpI5vityJ6FHo96vF+MmtXbC7G -T3VU+WtU0srrVByvORWb0HwP+FVRBOra+nuLYw+sObH2S45O9urpe+a6XlqOke/c -sX1SP3ODUkaDSEn/8i3KVPI2u0nMWZnAns+OeFVs7X1+g7hZLH34GoHwffUn8tuu -1DFUOP5Hsu4WIA/x2y0ov2846xG7mtSyWjpKfwIDAQABAoIBAQC/XB1m58EQzCVS -sx7t2qedVJEQjcpxHdql0xr4VOMl3U2r2mx03pxrt+lH3NmMlN3bmL2pgzSJ2GSI -Gsbsf8jpUIwTraKUDe9PevbswZ+Sz3Wbl96dKGhzAWCcWWEBHGKgsKe+2Hmg75Il -Jm446btAaziDnFuJukKYi9XN/kgYPxi914O8yz2KtCIVHEHHkl1FcSqjpghPtzU3 -hm1Nv/7tW2r5IrxCGRNJQTg6l4A4mdqif1u75ZUMcbp8dTaJ2/iYBIKIsh7sFMqy -TG6ZN0p3G92ijo7rtznxXS9rIE2rcg6qhusdK8eqhV0KHOqH2nkB4jWbw1NwKFzV -2jXm4S5RAoGBAPIExNBpE30c++Wl4ITuzODd99CczFj527ZBxUdT/H/IszR7adtJ -gHnayzzycul3GnCVMEGBUBp7q09OkcacA7MqS3/Zjn2zrpViz2iluP6jl0qfs2Sp -HaePLBKz9oFVi5m17ZYYnG7etSPVzcLaEi23ws5286HToXeqfUuGd+DlAoGBANQf -FJzQ0EbNu5QcNnQqwfAahvSqc+imPL0HuQWKEMvN3UXXU7Nn8bqba/JGVhgD7/5u -3g2DyyIou6gnocN669CqY8hm0jEboggD4pC8LVj+Iot25UzoNeNuHfqeu7wAlWWL -zjfC3UpSbh1O4H8i5chpFxe9N7syzOXBI5IVPBuTAoGBAITrrZSxQSzj8E0uj2Mz -LH8MKgD/PRRZFhzBfrIwJGuiNRpL9dWkRtWmHx14IziqW3Ed3wT7Gp2Q8oN6KYIl -SbrrLdAoEqRjPS16uWNGMZZZDszDbWmJoGnYrmIPSQG7lBJ14uke1zvlQSNPV9T+ -pCFL3cg7eI+WhgYNMwd58PkpAoGBAKTXFlyaxRAQtrFtjz+NLrMY2kFt6K8l6FN5 -meXdGhpW+5pXsBreLvK17xgSYrs87BbML1FPVt9Pyiztx36ymmjI0MweYz94Wt1h -r4KMSa07qLq6hYzTc3Uu0Ks/CWMbDP4hu/qHOxKTpjCuaDVEeE7ao/B1wcZ+vs3Y -3nyadeBzAoGBAJAZl50nHPwXpEIsHO3nC1ff51cVoV3+gpcCgQ270rLEa2Uv8+Zc -8rXD/LgcLzZ6Fvp0I3jv1mXlN8W0OruZS71lCM/zBd++E04HMxcvuv4lfqzcW+3E -V0ZBn2ErSTF9yKvGedRJk+vbCi7cy38WaA+z59ct/gpiw2Z3q6w85jlF ------END RSA PRIVATE KEY----- diff --git a/testdata/openssl.conf b/testdata/openssl.conf deleted file mode 100644 index 9b27e44..0000000 --- a/testdata/openssl.conf +++ /dev/null @@ -1,42 +0,0 @@ -[ca] -default_ca = CA_default - -[CA_default] -dir = ./ -database = $dir/index.txt -new_certs_dir = ./ -serial = $dir/serial -private_key = ./rootca.key -certificate = ./rootca.crt -default_days = 3650 -default_md = sha256 -policy = policy_anything -copy_extensions = copyall - -[policy_anything] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -[ req ] -default_bits = 2048 -distinguished_name = req_distinguished_name -req_extensions = v3_req - -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -stateOrProvinceName = State or Province Name (full name) -localityName = Locality Name (eg, city) -organizationName = Organization Name (eg, company) -commonName = Common Name (e.g. server FQDN or YOUR name) - -[ v3_req ] -subjectAltName = @alternate_names - -[alternate_names] -DNS.1 = helm.sh -IP.1 = 127.0.0.1 diff --git a/testdata/rootca.crt b/testdata/rootca.crt deleted file mode 100644 index 8921043..0000000 --- a/testdata/rootca.crt +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDITCCAgkCFAasUT/De3J4aee7b1VEESf+3ndyMA0GCSqGSIb3DQEBCwUAME0x -CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEQMA4GA1UEBwwHQm91bGRlcjENMAsG -A1UECgwESGVsbTEQMA4GA1UEAwwHaGVsbS5zaDAeFw0xOTExMDEyMjM2MzZaFw0y -MjA4MjEyMjM2MzZaME0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEQMA4GA1UE -BwwHQm91bGRlcjENMAsGA1UECgwESGVsbTEQMA4GA1UEAwwHaGVsbS5zaDCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMinBcDJwiG3OVb1bCWQqTAOS3s6 -QwWkEXkoYyFFpCNvqEzQPtp+OkfD6gczc0ByGQibDLBApEQhq17inqtAxIUrTgXP -ym3l+0/U7ejuTka3ue84slkw2lVobfVEvJWGro+93GzbxvVNNYGJcD2BKJqmCCxD -I6tdTEL855kzgQUAvGITzDUxABU9+f06CW/9AlZlmBIuwrzRVjFNjflBrcm1PIUG -upMCu8zaWat8o1TnLCDKizw1JJzCgCnMxGXfzeAd1MGUG/rOFkBImHf39Jakp/7L -Icq+2FDE+0vNai0lpUpxPVTp8dcug8U3//bL3q0OqROA7Ks4wc0URGH71W8CAwEA -ATANBgkqhkiG9w0BAQsFAAOCAQEAMJqzeg6cBbUkrh9a6+qa66IFR1Mf3wVB1c61 -JN6Z70kjgSdOZ/NexxxSu347fIPyKGkmokbnE1MJVEETPmzhpuTkQDcq7KT4IcQF -S+H4l0lNn09thIlIiAJmpQrNOlrHVtpLCFB4+YnsqqFKPlcO/dGy9U26L4xfn6+n -24/o7pNEu44GnktXPjfcbajaPUSKHxeYibjdftoUEYX/79ROu7E1QnNXj7mXymw0 -rqOgIlyCUGw8WvRR8RzR6m+1lnwOc+nxFKXzTt0LqOQt9sHI1V71WrxgDE+Lck+W -fybfsgodM2Y7VXnH4A4xoKeOHxW1YcqIKt0ribt8602lD1pYBg== ------END CERTIFICATE----- diff --git a/testdata/rootca.key b/testdata/rootca.key deleted file mode 100644 index e3c1ce5..0000000 --- a/testdata/rootca.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAyKcFwMnCIbc5VvVsJZCpMA5LezpDBaQReShjIUWkI2+oTNA+ -2n46R8PqBzNzQHIZCJsMsECkRCGrXuKeq0DEhStOBc/KbeX7T9Tt6O5ORre57ziy -WTDaVWht9US8lYauj73cbNvG9U01gYlwPYEomqYILEMjq11MQvznmTOBBQC8YhPM -NTEAFT35/ToJb/0CVmWYEi7CvNFWMU2N+UGtybU8hQa6kwK7zNpZq3yjVOcsIMqL -PDUknMKAKczEZd/N4B3UwZQb+s4WQEiYd/f0lqSn/sshyr7YUMT7S81qLSWlSnE9 -VOnx1y6DxTf/9sverQ6pE4DsqzjBzRREYfvVbwIDAQABAoIBAHwyTbBP8baWx4oY -rNDvoplZL8VdgaCbNimNIxa0GW3Jrh2lhFIPcZl8HX5JjVvlg7M87XSm/kYhpQY9 -NUMA+uMGs+uK+1xcztpSDNRxtMe27wKwUEw+ndXhprX6ztOqop/cP/StcI/jM2wz -muKm8HAQttxWzlxCinKoQd4k8AYcnqc728FSODP7EsdDgiU6BhBZDqjgmqggye0y -niog+JBPDgwTgGodJWtSYuP/G2iJDUvm7bGU2gftXTJstrATLftGKX8XOgJMmDx9 -8OgDtU21LzggarOQ/iwUKX2MEfYnP8kgGLgu5nNonJCHWYGeCZoxIn70rs3WoBsU -5+FzmHkCgYEA7MFYixlTSxXfen1MwctuZ9YiwoneSLfjmBb+LP0Pfa2r0CVMPaXM -OexroIY14h64nunb7y3YifGk01RXzCBpEF5KhsZuYXAl3lGxbjbTjncU5/11Dim+ -W9g+T4zDimlK2tuweAjMfWz6XG2inZ3xvK73mGkEsUnqhWQKXBRf7VsCgYEA2PZp -KAwbpRFSYFwcZoRm81fLijZ5NbmOJtND6oG1LZVaVSYuvljvjQzeVfL4+Iju6FzT -zbnEfVsatu0cTs6jMy0yJUl6wRbHlH/G6Ra8UxSvUUEFe1Xap33RmjkK+atzALQi -pZPCIfLr+f9qQWrPMdZwzRnws0u2pKepSdXR0H0CgYB9chDdWyTkIwnPmDakdIri -X/b5Bx4Nf8oLGxvAcLHVkMD5v9l+zKvCgT+hxZslXcvK//S17Z/Pr4b7JrSChyXE -M4HfmaKA5HBcNQMDd+9ujDA6n/R29a1UcubJNbeiThoIjuEZKOhZCPY7JShFxZuB -s1+jlPmUiqrF1PUcRvtxAwKBgQDGpuelmWB+hRutyujeHQC+cnaU+EeHH3y+o9Wd -lGG1ePia2jkWZAwCU/QHMk8wEQDelJAB38O/G3mcYAH5Tk4zf4BYj6zrutXGbDBO -H1kToO7dMPG5+eQYU6Vk1jHsZEUKMeU/QckQmIHkBy7c8tT/Rt9FjCjNodd7b2Ab -kMFpaQKBgQDggmgsPFSZmo+yYDZucueXqfc8cbSWd9K1UruKMaPOsyoUWJNYARHA -cpHTpaIjDth8MUp2zLIZnPUSDkSgEAOcRH4C5CxmgSkmeJdlEEzWMF2yugczlYGO -l9SOX07w4/WJCZFeRWTqRGWs7X6iL8um0P9yFelw3SZt33ON+1fRPg== ------END RSA PRIVATE KEY-----