mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-22 06:41:36 +01:00
Define the related handlers to handler the requests of chart server/repo
add chartserver directory to put chart server code add controller to coordinate the flow add base/repo/manipulation handlers to handle requests add operator to parse more details of chart version call dep ensure
This commit is contained in:
parent
e705224b3f
commit
54a0d10994
13
src/Gopkg.lock
generated
13
src/Gopkg.lock
generated
@ -148,7 +148,10 @@
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = ["proto"]
|
||||
packages = [
|
||||
"proto",
|
||||
"ptypes/any"
|
||||
]
|
||||
revision = "130e6b02ab059e7b717a096f397c5b60111cae74"
|
||||
|
||||
[[projects]]
|
||||
@ -175,6 +178,12 @@
|
||||
revision = "7f08801859139f86dfafd1c296e2cba9a80d292e"
|
||||
version = "v1.6.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/kubernetes/helm"
|
||||
packages = ["pkg/proto/hapi/chart"]
|
||||
revision = "20adb27c7c5868466912eebdf6664e7390ebe710"
|
||||
version = "v2.9.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/lib/pq"
|
||||
@ -287,6 +296,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "a53e62d0a95585213a5c0519e5c02c570201189b9332d9dfca730d5671619dab"
|
||||
inputs-digest = "d6cb7ad53bb52b7243e16b20774f160c24ea003285cf7b6c4db782bca6f1e3c8"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@ -80,7 +80,10 @@ ignored = ["github.com/vmware/harbor/tests*"]
|
||||
name = "github.com/garyburd/redigo"
|
||||
version = "=1.6.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/golang-migrate/migrate"
|
||||
version = "=3.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/kubernetes/helm"
|
||||
version = "=2.9.1"
|
16
src/chartserver/base_handler.go
Normal file
16
src/chartserver/base_handler.go
Normal file
@ -0,0 +1,16 @@
|
||||
package chartserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
//BaseHandler defines the handlers related with the chart server itself.
|
||||
type BaseHandler struct {
|
||||
//Proxy used to to transfer the traffic of requests
|
||||
//It's mainly used to talk to the backend chart server
|
||||
trafficProxy *httputil.ReverseProxy
|
||||
}
|
||||
|
||||
//GetHealthStatus will return the health status of the backend chart repository server
|
||||
func (bh *BaseHandler) GetHealthStatus(w http.ResponseWriter, req *http.Request) {}
|
12
src/chartserver/chart_operator.go
Normal file
12
src/chartserver/chart_operator.go
Normal file
@ -0,0 +1,12 @@
|
||||
package chartserver
|
||||
|
||||
import "github.com/kubernetes/helm/pkg/proto/hapi/chart"
|
||||
|
||||
//ChartOperator is designed to process the contents of
|
||||
//the specified chart version to get more details
|
||||
type ChartOperator struct{}
|
||||
|
||||
//GetChartDetails parse the details from the provided content bytes
|
||||
func (cho *ChartOperator) GetChartDetails(content []byte) *chart.Chart {
|
||||
return nil
|
||||
}
|
62
src/chartserver/controller.go
Normal file
62
src/chartserver/controller.go
Normal file
@ -0,0 +1,62 @@
|
||||
package chartserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
//Controller is used to handle flows of related requests based on the corresponding handlers
|
||||
//A reverse proxy will be created and managed to proxy the related traffics between API and
|
||||
//backend chart server
|
||||
type Controller struct {
|
||||
//The access endpoint of the backend chart repository server
|
||||
backendServerAddr *url.URL
|
||||
|
||||
//To cover the server info and status requests
|
||||
baseHandler *BaseHandler
|
||||
|
||||
//To cover the chart repository requests
|
||||
repositoryHandler *RepositoryHandler
|
||||
|
||||
//To cover all the manipulation requests
|
||||
manipulationHandler *ManipulationHandler
|
||||
}
|
||||
|
||||
//NewController is constructor of the chartserver.Controller
|
||||
func NewController(backendServer *url.URL) (*Controller, error) {
|
||||
if backendServer == nil {
|
||||
return nil, errors.New("failed to create chartserver.Controller: backend sever address is required")
|
||||
}
|
||||
|
||||
//Currently, no customization requirements needed, so let's use the simple proxy here now
|
||||
proxy := httputil.NewSingleHostReverseProxy(backendServer)
|
||||
|
||||
//Initialize chart operator for use
|
||||
operator := &ChartOperator{}
|
||||
|
||||
return &Controller{
|
||||
backendServerAddr: backendServer,
|
||||
baseHandler: &BaseHandler{proxy},
|
||||
repositoryHandler: &RepositoryHandler{proxy},
|
||||
manipulationHandler: &ManipulationHandler{
|
||||
trafficProxy: proxy,
|
||||
chartOperator: operator,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
//GetBaseHandler returns the reference of BaseHandler
|
||||
func (c *Controller) GetBaseHandler() *BaseHandler {
|
||||
return c.baseHandler
|
||||
}
|
||||
|
||||
//GetRepositoryHandler returns the reference of RepositoryHandler
|
||||
func (c *Controller) GetRepositoryHandler() *RepositoryHandler {
|
||||
return c.repositoryHandler
|
||||
}
|
||||
|
||||
//GetManipulationHandler returns the reference of ManipulationHandler
|
||||
func (c *Controller) GetManipulationHandler() *ManipulationHandler {
|
||||
return c.manipulationHandler
|
||||
}
|
37
src/chartserver/manipulation_handler.go
Normal file
37
src/chartserver/manipulation_handler.go
Normal file
@ -0,0 +1,37 @@
|
||||
package chartserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
//ManipulationHandler includes all the handler methods for the purpose of manipulating the
|
||||
//chart repository
|
||||
type ManipulationHandler struct {
|
||||
//Proxy used to to transfer the traffic of requests
|
||||
//It's mainly used to talk to the backend chart server
|
||||
trafficProxy *httputil.ReverseProxy
|
||||
|
||||
//Parse and process the chart version to provide required info data
|
||||
chartOperator *ChartOperator
|
||||
}
|
||||
|
||||
//ListCharts lists all the charts under the specified namespace
|
||||
func (mh *ManipulationHandler) ListCharts(w http.ResponseWriter, req *http.Request) {}
|
||||
|
||||
//GetChart returns all the chart versions under the specified chart
|
||||
func (mh *ManipulationHandler) GetChart(w http.ResponseWriter, req *http.Request) {}
|
||||
|
||||
//GetChartVersion get the specified version for one chart
|
||||
//This handler should return the details of the chart version,
|
||||
//maybe including metadata,dependencies and values etc.
|
||||
func (mh *ManipulationHandler) GetChartVersion(w http.ResponseWriter, req *http.Request) {}
|
||||
|
||||
//UploadChartVersion will save the new version of the chart to the backend storage
|
||||
func (mh *ManipulationHandler) UploadChartVersion(w http.ResponseWriter, req *http.Request) {}
|
||||
|
||||
//UploadProvenanceFile will save the provenance file of the chart to the backend storage
|
||||
func (mh *ManipulationHandler) UploadProvenanceFile(w http.ResponseWriter, req *http.Request) {}
|
||||
|
||||
//DeleteChartVersion will delete the specified version of the chart
|
||||
func (mh *ManipulationHandler) DeleteChartVersion(w http.ResponseWriter, req *http.Request) {}
|
27
src/chartserver/repo_handler.go
Normal file
27
src/chartserver/repo_handler.go
Normal file
@ -0,0 +1,27 @@
|
||||
package chartserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
)
|
||||
|
||||
//RepositoryHandler defines all the handlers to handle the requests related with chart repository
|
||||
//e.g: index.yaml and downloading chart objects
|
||||
type RepositoryHandler struct {
|
||||
//Proxy used to to transfer the traffic of requests
|
||||
//It's mainly used to talk to the backend chart server
|
||||
trafficProxy *httputil.ReverseProxy
|
||||
}
|
||||
|
||||
//GetIndexFileWithNS will read the index.yaml data under the specified namespace
|
||||
func (rh *RepositoryHandler) GetIndexFileWithNS(w http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
//GetIndexFile will read the index.yaml under all namespaces and merge them as a single one
|
||||
//Please be aware that, to support this function, the backend chart repository server should
|
||||
//enable multi-tenancies
|
||||
func (rh *RepositoryHandler) GetIndexFile(w http.ResponseWriter, req *http.Request) {}
|
||||
|
||||
//DownloadChartObject will download the stored chart object to the client
|
||||
//e.g: helm install
|
||||
func (rh *RepositoryHandler) DownloadChartObject(w http.ResponseWriter, req *http.Request) {}
|
19
src/vendor/github.com/kubernetes/helm/.circleci/bootstrap.sh
generated
vendored
Executable file
19
src/vendor/github.com/kubernetes/helm/.circleci/bootstrap.sh
generated
vendored
Executable file
@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT 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
|
||||
|
||||
apt-get update -y && apt-get install -yq zip
|
||||
make bootstrap
|
38
src/vendor/github.com/kubernetes/helm/.circleci/config.yml
generated
vendored
Normal file
38
src/vendor/github.com/kubernetes/helm/.circleci/config.yml
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
working_directory: /go/src/k8s.io/helm
|
||||
parallelism: 3
|
||||
docker:
|
||||
- image: golang:1.10
|
||||
environment:
|
||||
PROJECT_NAME: "kubernetes-helm"
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 17.09.0-ce
|
||||
- restore_cache:
|
||||
keys:
|
||||
- glide-{{ checksum "glide.yaml" }}-{{ checksum "glide.lock" }}
|
||||
- glide- # used as a fall through if the checksum fails to find exact entry
|
||||
- run:
|
||||
name: install dependencies
|
||||
command: .circleci/bootstrap.sh
|
||||
- save_cache:
|
||||
key: glide-{{ checksum "glide.yaml" }}-{{ checksum "glide.lock" }}
|
||||
paths:
|
||||
- /root/.glide # Where the glide cache is stored
|
||||
- run:
|
||||
name: test
|
||||
command: .circleci/test.sh
|
||||
- deploy:
|
||||
name: deploy
|
||||
command: .circleci/deploy.sh
|
||||
workflows:
|
||||
version: 2
|
||||
build:
|
||||
jobs:
|
||||
- build:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
64
src/vendor/github.com/kubernetes/helm/.circleci/deploy.sh
generated
vendored
Executable file
64
src/vendor/github.com/kubernetes/helm/.circleci/deploy.sh
generated
vendored
Executable file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT 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
|
||||
|
||||
: ${GCLOUD_SERVICE_KEY:?"GCLOUD_SERVICE_KEY environment variable is not set"}
|
||||
: ${PROJECT_NAME:?"PROJECT_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 master or a tag"
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "Install docker client"
|
||||
VER="17.09.0-ce"
|
||||
curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
|
||||
tar -xz -C /tmp -f /tmp/docker-$VER.tgz
|
||||
mv /tmp/docker/* /usr/bin
|
||||
|
||||
echo "Install gcloud components"
|
||||
export CLOUDSDK_CORE_DISABLE_PROMPTS=1
|
||||
curl https://sdk.cloud.google.com | bash
|
||||
${HOME}/google-cloud-sdk/bin/gcloud --quiet components update
|
||||
|
||||
echo "Configuring gcloud authentication"
|
||||
echo "${GCLOUD_SERVICE_KEY}" | base64 --decode > "${HOME}/gcloud-service-key.json"
|
||||
${HOME}/google-cloud-sdk/bin/gcloud auth activate-service-account --key-file "${HOME}/gcloud-service-key.json"
|
||||
${HOME}/google-cloud-sdk/bin/gcloud config set project "${PROJECT_NAME}"
|
||||
docker login -u _json_key -p "$(cat ${HOME}/gcloud-service-key.json)" https://gcr.io
|
||||
|
||||
echo "Building the tiller image"
|
||||
make docker-build VERSION="${VERSION}"
|
||||
|
||||
echo "Pushing image to gcr.io"
|
||||
docker push "gcr.io/kubernetes-helm/tiller:${VERSION}"
|
||||
|
||||
echo "Building helm binaries"
|
||||
make build-cross
|
||||
make dist checksum VERSION="${VERSION}"
|
||||
|
||||
echo "Pushing binaries to gs bucket"
|
||||
${HOME}/google-cloud-sdk/bin/gsutil cp ./_dist/* "gs://${PROJECT_NAME}"
|
53
src/vendor/github.com/kubernetes/helm/.circleci/test.sh
generated
vendored
Executable file
53
src/vendor/github.com/kubernetes/helm/.circleci/test.sh
generated
vendored
Executable file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT 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'
|
||||
|
||||
HELM_ROOT="${BASH_SOURCE[0]%/*}/.."
|
||||
cd "$HELM_ROOT"
|
||||
|
||||
run_unit_test() {
|
||||
if [[ "${CIRCLE_BRANCH-}" == "master" ]]; then
|
||||
echo "Running unit tests with coverage'"
|
||||
./scripts/coverage.sh --coveralls
|
||||
else
|
||||
echo "Running 'unit tests'"
|
||||
make test-unit
|
||||
fi
|
||||
}
|
||||
|
||||
run_style_check() {
|
||||
echo "Running 'make test-style'"
|
||||
make test-style
|
||||
}
|
||||
|
||||
run_docs_check() {
|
||||
echo "Running 'make verify-docs'"
|
||||
make verify-docs
|
||||
}
|
||||
|
||||
# Build to ensure packages are compiled
|
||||
echo "Running 'make build'"
|
||||
make build
|
||||
|
||||
case "${CIRCLE_NODE_INDEX-0}" in
|
||||
0) run_unit_test ;;
|
||||
1) run_style_check ;;
|
||||
2) run_docs_check ;;
|
||||
esac
|
9
src/vendor/github.com/kubernetes/helm/.github/issue_template.md
generated
vendored
Normal file
9
src/vendor/github.com/kubernetes/helm/.github/issue_template.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<!-- If you need help or think you have found a bug, please help us with your issue by entering the following information (otherwise you can delete this text): -->
|
||||
|
||||
Output of `helm version`:
|
||||
|
||||
Output of `kubectl version`:
|
||||
|
||||
Cloud Provider/Platform (AKS, GKE, Minikube etc.):
|
||||
|
||||
|
12
src/vendor/github.com/kubernetes/helm/.gitignore
generated
vendored
Normal file
12
src/vendor/github.com/kubernetes/helm/.gitignore
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
.DS_Store
|
||||
.coverage/
|
||||
.vimrc
|
||||
.vscode/
|
||||
_dist/
|
||||
_proto/*.pb.go
|
||||
bin/
|
||||
rootfs/tiller
|
||||
rootfs/rudder
|
||||
vendor/
|
||||
*.exe
|
||||
.idea/
|
236
src/vendor/github.com/kubernetes/helm/CONTRIBUTING.md
generated
vendored
Normal file
236
src/vendor/github.com/kubernetes/helm/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
# Contributing Guidelines
|
||||
|
||||
The Kubernetes 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/kubernetes/helm/issues). However, if
|
||||
you are reporting a _security vulnerability_, please email a report to
|
||||
[helm-security@deis.com](mailto:helm-security@deis.com). This will give
|
||||
us a chance to try to fix the issue before it is exploited in the wild.
|
||||
|
||||
## Contributor License Agreements
|
||||
|
||||
We'd love to accept your patches! Before we can take them, we have to jump a
|
||||
couple of legal hurdles.
|
||||
|
||||
The Cloud Native Computing Foundation (CNCF) CLA [must be signed](https://github.com/kubernetes/community/blob/master/CLA.md) by all contributors.
|
||||
Please fill out either the individual or corporate Contributor License
|
||||
Agreement (CLA).
|
||||
|
||||
Once you are CLA'ed, we'll be able to accept your pull requests. For any issues that you face during this process,
|
||||
please add a comment [here](https://github.com/kubernetes/kubernetes/issues/27796) explaining the issue and we will help get it sorted out.
|
||||
|
||||
***NOTE***: Only original source code from you and other people that have
|
||||
signed the CLA can be accepted into the repository. This policy does not
|
||||
apply to [third_party](third_party/) and [vendor](vendor/).
|
||||
|
||||
## Support Channels
|
||||
|
||||
Whether you are a user or contributor, official support channels include:
|
||||
|
||||
- GitHub [issues](https://github.com/kubernetes/helm/issues/new)
|
||||
- Slack: #Helm room in the [Kubernetes Slack](http://slack.kubernetes.io/)
|
||||
|
||||
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.
|
||||
|
||||
## 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 `2.2.0` an issue/PR could fall in to one of 4 different active milestones:
|
||||
`2.2.1`, `2.3.0`, `Upcoming - Minor`, or `Upcoming - Major`. If an issue pertains to a
|
||||
specific upcoming bug or minor release, it would go into `2.2.1` or `2.3.0`. If the issue/PR
|
||||
does not have a specific milestone yet, but it is likely that it will land in a `2.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.
|
||||
|
||||
## Semver
|
||||
|
||||
Helm maintains a strong commitment to backward compatibility. All of our changes to protocols and formats are backward compatible from Helm 2.0 until Helm 3.0. No features, flags, or commands are removed or substantially modified (other than bug fixes).
|
||||
|
||||
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 2.0 and 3.0:
|
||||
|
||||
- Protobuf and gRPC changes MUST be backward compatible.
|
||||
- Command line commands, flags, and arguments MUST be backward compatible
|
||||
- File formats (such as Chart.yaml, repositories.yaml, and requirements.yaml) MUST be backward compatible
|
||||
- Any chart that worked on a previous version of Helm MUST work on a new version of Helm (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/` may be changed from release to release without notice).
|
||||
|
||||
## Issues
|
||||
|
||||
Issues are used as the primary method for tracking anything to do with the Helm project.
|
||||
|
||||
### Issue Types
|
||||
|
||||
There are 4 types of issues (each with their own corresponding [label](#labels)):
|
||||
- Question: 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.
|
||||
- Features: 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.
|
||||
- Bugs: These track bugs with the code or 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 "starter"). 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".
|
||||
- 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
|
||||
- "Feature" and "Bug" issues 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 them self or make a comment in the issue
|
||||
saying that they are taking it.
|
||||
- "Proposal" and "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://github.com/kubernetes/helm/blob/master/docs/developers.md
|
||||
|
||||
The next section contains more information on the workflow followed for PRs
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Like any good open source project, we use Pull Requests to track code changes
|
||||
|
||||
### PR Lifecycle
|
||||
|
||||
1. PR creation
|
||||
- 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.
|
||||
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.
|
||||
- Reviews from others in the community, especially those who have encountered a bug or have
|
||||
requested a feature, are highly encouraged, but not required. Maintainer reviews **are** required
|
||||
before any merge
|
||||
- Any PR with the `size/large` label requires 2 review approvals from maintainers before it can be
|
||||
merged. Those with `size/medium` are per the judgement of the maintainers
|
||||
4. Reviewing/Discussion
|
||||
- Once a maintainer begins reviewing a PR, they will remove the `awaiting review` label and add
|
||||
the `in progress` label so the person submitting knows that it is being worked on. This is
|
||||
especially helpful when the review may take awhile.
|
||||
- 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. 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.
|
||||
- If the owner of the PR is listed in `OWNERS`, 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 committer may
|
||||
merge the PR once it is approved.
|
||||
|
||||
#### 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 standup 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 will be handled first by maintainers |
|
||||
| `docs` | Indicates the issue or PR is a documentation change |
|
||||
| `duplicate` | Indicates that the issue or PR is a duplicate of another |
|
||||
| `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` | This issue is one the core maintainers cannot get to right now and would appreciate help with |
|
||||
| `proposal` | This issue is a proposal |
|
||||
| `question/support` | This issue is a support request or question |
|
||||
| `starter` | This issue is a good for someone new to contributing to Helm |
|
||||
| `wont fix` | The issue has been discussed and will not be implemented (or accepted in the case of a proposal) |
|
||||
|
||||
### PR Specific
|
||||
|
||||
| Label | Description |
|
||||
| ----- | ----------- |
|
||||
| `awaiting review` | The PR has been triaged and is ready for someone to review |
|
||||
| `breaking` | The PR has breaking changes (such as API changes) |
|
||||
| `cncf-cla: no` | The PR submitter has **not** signed the project CLA. |
|
||||
| `cncf-cla: yes` | The PR submitter has signed the project CLA. This is required to merge. |
|
||||
| `in progress` | Indicates that a maintainer is looking at the PR, even if no review has been posted yet |
|
||||
| `needs pick` | Indicates that the PR needs to be picked into a feature branch (generally bugfix branches). Once it has been, the `picked` label should be applied and this one removed |
|
||||
| `needs rebase` | A helper label used to indicate that the PR needs to be rebased before it can be merged. Used for easy filtering |
|
||||
| `picked` | This PR has been 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/large`
|
||||
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/small` even though the number
|
||||
lines is greater than defined below.
|
||||
|
||||
| Label | Description |
|
||||
| ----- | ----------- |
|
||||
| `size/small` | Anything less than or equal to 4 files and 150 lines. Only small amounts of manual testing may be required |
|
||||
| `size/medium` | Anything greater than `size/small` and less than or equal to 8 files and 300 lines. Manual validation should be required. |
|
||||
| `size/large` | Anything greater than `size/medium`. This should be thoroughly tested before merging and always requires 2 approvals. This also should be applied to anything that is a significant logic change. |
|
202
src/vendor/github.com/kubernetes/helm/LICENSE
generated
vendored
Normal file
202
src/vendor/github.com/kubernetes/helm/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2016 The Kubernetes Authors All Rights Reserved
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
143
src/vendor/github.com/kubernetes/helm/Makefile
generated
vendored
Normal file
143
src/vendor/github.com/kubernetes/helm/Makefile
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
DOCKER_REGISTRY ?= gcr.io
|
||||
IMAGE_PREFIX ?= kubernetes-helm
|
||||
SHORT_NAME ?= tiller
|
||||
SHORT_NAME_RUDDER ?= rudder
|
||||
TARGETS ?= darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64
|
||||
DIST_DIRS = find * -type d -exec
|
||||
APP = helm
|
||||
|
||||
# go option
|
||||
GO ?= go
|
||||
PKG := $(shell glide novendor)
|
||||
TAGS :=
|
||||
TESTS := .
|
||||
TESTFLAGS :=
|
||||
LDFLAGS := -w -s
|
||||
GOFLAGS :=
|
||||
BINDIR := $(CURDIR)/bin
|
||||
BINARIES := helm tiller
|
||||
|
||||
# Required for globs to work correctly
|
||||
SHELL=/bin/bash
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
GOBIN=$(BINDIR) $(GO) install $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' k8s.io/helm/cmd/...
|
||||
|
||||
# usage: make clean build-cross dist APP=helm|tiller VERSION=v2.0.0-alpha.3
|
||||
.PHONY: build-cross
|
||||
build-cross: LDFLAGS += -extldflags "-static"
|
||||
build-cross:
|
||||
CGO_ENABLED=0 gox -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/{{.Dir}}" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' k8s.io/helm/cmd/$(APP)
|
||||
|
||||
.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: checksum
|
||||
checksum:
|
||||
for f in _dist/*.{gz,zip} ; do \
|
||||
shasum -a 256 "$${f}" | awk '{print $$1}' > "$${f}.sha256" ; \
|
||||
done
|
||||
|
||||
.PHONY: check-docker
|
||||
check-docker:
|
||||
@if [ -z $$(which docker) ]; then \
|
||||
echo "Missing \`docker\` client which is required for development"; \
|
||||
exit 2; \
|
||||
fi
|
||||
|
||||
.PHONY: docker-binary
|
||||
docker-binary: BINDIR = ./rootfs
|
||||
docker-binary: GOFLAGS += -a -installsuffix cgo
|
||||
docker-binary:
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 $(GO) build -o $(BINDIR)/tiller $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' k8s.io/helm/cmd/tiller
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build: check-docker docker-binary
|
||||
docker build --rm -t ${IMAGE} rootfs
|
||||
docker tag ${IMAGE} ${MUTABLE_IMAGE}
|
||||
|
||||
.PHONY: docker-binary-rudder
|
||||
docker-binary-rudder: BINDIR = ./rootfs
|
||||
docker-binary-rudder: GOFLAGS += -a -installsuffix cgo
|
||||
docker-binary-rudder:
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 $(GO) build -o $(BINDIR)/rudder $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' k8s.io/helm/cmd/rudder
|
||||
|
||||
.PHONY: docker-build-experimental
|
||||
docker-build-experimental: check-docker docker-binary docker-binary-rudder
|
||||
docker build --rm -t ${IMAGE} rootfs -f rootfs/Dockerfile.experimental
|
||||
docker tag ${IMAGE} ${MUTABLE_IMAGE}
|
||||
docker build --rm -t ${IMAGE_RUDDER} rootfs -f rootfs/Dockerfile.rudder
|
||||
docker tag ${IMAGE_RUDDER} ${MUTABLE_IMAGE_RUDDER}
|
||||
|
||||
.PHONY: test
|
||||
test: build
|
||||
test: TESTFLAGS += -race -v
|
||||
test: test-style
|
||||
test: test-unit
|
||||
|
||||
.PHONY: test-unit
|
||||
test-unit:
|
||||
@echo
|
||||
@echo "==> Running unit tests <=="
|
||||
HELM_HOME=/no/such/dir $(GO) test $(GOFLAGS) -run $(TESTS) $(PKG) $(TESTFLAGS)
|
||||
|
||||
.PHONY: test-style
|
||||
test-style:
|
||||
@scripts/validate-go.sh
|
||||
@scripts/validate-license.sh
|
||||
|
||||
.PHONY: protoc
|
||||
protoc:
|
||||
$(MAKE) -C _proto/ all
|
||||
|
||||
.PHONY: docs
|
||||
docs: build
|
||||
@scripts/update-docs.sh
|
||||
|
||||
.PHONY: verify-docs
|
||||
verify-docs: build
|
||||
@scripts/verify-docs.sh
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(BINDIR) ./rootfs/tiller ./_dist
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
@scripts/coverage.sh
|
||||
|
||||
HAS_GLIDE := $(shell command -v glide;)
|
||||
HAS_GOX := $(shell command -v gox;)
|
||||
HAS_GIT := $(shell command -v git;)
|
||||
HAS_HG := $(shell command -v hg;)
|
||||
|
||||
.PHONY: bootstrap
|
||||
bootstrap:
|
||||
ifndef HAS_GLIDE
|
||||
go get -u github.com/Masterminds/glide
|
||||
endif
|
||||
ifndef HAS_GOX
|
||||
go get -u github.com/mitchellh/gox
|
||||
endif
|
||||
|
||||
ifndef HAS_GIT
|
||||
$(error You must install Git)
|
||||
endif
|
||||
ifndef HAS_HG
|
||||
$(error You must install Mercurial)
|
||||
endif
|
||||
glide install --strip-vendor
|
||||
go build -o bin/protoc-gen-go ./vendor/github.com/golang/protobuf/protoc-gen-go
|
||||
|
||||
include versioning.mk
|
30
src/vendor/github.com/kubernetes/helm/OWNERS
generated
vendored
Normal file
30
src/vendor/github.com/kubernetes/helm/OWNERS
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
maintainers:
|
||||
- adamreese
|
||||
- bacongobbler
|
||||
- jascott1
|
||||
- mattfarina
|
||||
- michelleN
|
||||
- nebril
|
||||
- prydonius
|
||||
- SlickNik
|
||||
- technosophos
|
||||
- thomastaylor312
|
||||
- viglesiasce
|
||||
reviewers:
|
||||
- adamreese
|
||||
- bacongobbler
|
||||
- fibonacci1729
|
||||
- jascott1
|
||||
- mattfarina
|
||||
- michelleN
|
||||
- migmartri
|
||||
- nebril
|
||||
- prydonius
|
||||
- SlickNik
|
||||
- technosophos
|
||||
- thomastaylor312
|
||||
- viglesiasce
|
||||
emeritus:
|
||||
- migmartri
|
||||
- seh
|
||||
- vaikas-google
|
73
src/vendor/github.com/kubernetes/helm/README.md
generated
vendored
Normal file
73
src/vendor/github.com/kubernetes/helm/README.md
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
# Kubernetes Helm
|
||||
|
||||
[](https://circleci.com/gh/kubernetes/helm)
|
||||
[](https://goreportcard.com/report/github.com/kubernetes/helm)
|
||||
[](https://godoc.org/github.com/kubernetes/helm)
|
||||
|
||||
Helm is a tool for managing Kubernetes charts. Charts are packages of
|
||||
pre-configured Kubernetes resources.
|
||||
|
||||
Use Helm to:
|
||||
|
||||
- Find and use [popular software packaged as Kubernetes charts](https://github.com/kubernetes/charts)
|
||||
- Share your own applications as Kubernetes charts
|
||||
- Create reproducible builds of your Kubernetes applications
|
||||
- Intelligently manage your Kubernetes manifest files
|
||||
- Manage releases of Helm packages
|
||||
|
||||
## Helm in a Handbasket
|
||||
|
||||
Helm is a tool that streamlines installing and managing Kubernetes applications.
|
||||
Think of it like apt/yum/homebrew for Kubernetes.
|
||||
|
||||
- Helm has two parts: a client (`helm`) and a server (`tiller`)
|
||||
- Tiller runs inside of your Kubernetes cluster, and manages releases (installations)
|
||||
of your charts.
|
||||
- Helm runs on your laptop, CI/CD, or wherever you want it to run.
|
||||
- Charts are Helm packages that contain at least two things:
|
||||
- A description of the package (`Chart.yaml`)
|
||||
- One or more templates, which contain Kubernetes manifest files
|
||||
- Charts can be stored on disk, or fetched from remote chart repositories
|
||||
(like Debian or RedHat packages)
|
||||
|
||||
## Install
|
||||
|
||||
Binary downloads of the Helm client can be found at the following links:
|
||||
|
||||
- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.9.1-darwin-amd64.tar.gz)
|
||||
- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.9.1-linux-amd64.tar.gz)
|
||||
- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.9.1-linux-386.tar.gz)
|
||||
- [Windows](https://kubernetes-helm.storage.googleapis.com/helm-v2.9.1-windows-amd64.tar.gz)
|
||||
|
||||
Unpack the `helm` binary and add it to your PATH and you are good to go!
|
||||
macOS/[homebrew](https://brew.sh/) users can also use `brew install kubernetes-helm`.
|
||||
|
||||
To rapidly get Helm up and running, start with the [Quick Start Guide](https://docs.helm.sh/using_helm/#quickstart-guide).
|
||||
|
||||
See the [installation guide](https://docs.helm.sh/using_helm/#installing-helm) for more options,
|
||||
including installing pre-releases.
|
||||
|
||||
## Docs
|
||||
|
||||
Get started with the [Quick Start guide](https://docs.helm.sh/using_helm/#quickstart-guide) or plunge into the [complete documentation](https://docs.helm.sh)
|
||||
|
||||
## Roadmap
|
||||
|
||||
The [Helm roadmap uses Github milestones](https://github.com/kubernetes/helm/milestones) to track the progress of the project.
|
||||
|
||||
## Community, discussion, contribution, and support
|
||||
|
||||
You can reach the Helm community and developers via the following channels:
|
||||
|
||||
- [Kubernetes Slack](http://slack.k8s.io):
|
||||
- #helm-users
|
||||
- #helm-dev
|
||||
- #charts
|
||||
- Mailing Lists:
|
||||
- [Helm Mailing List](https://lists.cncf.io/g/cncf-kubernetes-helm)
|
||||
- [Kubernetes SIG Apps Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-apps)
|
||||
- Developer Call: Thursdays at 9:30-10:00 Pacific. [https://zoom.us/j/4526666954](https://zoom.us/j/4526666954)
|
||||
|
||||
### Code of conduct
|
||||
|
||||
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
|
58
src/vendor/github.com/kubernetes/helm/_proto/Makefile
generated
vendored
Normal file
58
src/vendor/github.com/kubernetes/helm/_proto/Makefile
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
space := $(empty) $(empty)
|
||||
comma := ,
|
||||
empty :=
|
||||
|
||||
import_path = k8s.io/helm/pkg/proto/hapi
|
||||
|
||||
dst = ../pkg/proto
|
||||
target = go
|
||||
plugins = grpc
|
||||
|
||||
chart_ias = $(subst $(space),$(comma),$(addsuffix =$(import_path)/$(chart_pkg),$(addprefix M,$(chart_pbs))))
|
||||
chart_pbs = $(sort $(wildcard hapi/chart/*.proto))
|
||||
chart_pkg = chart
|
||||
|
||||
release_ias = $(subst $(space),$(comma),$(addsuffix =$(import_path)/$(release_pkg),$(addprefix M,$(release_pbs))))
|
||||
release_pbs = $(sort $(wildcard hapi/release/*.proto))
|
||||
release_pkg = release
|
||||
|
||||
services_ias = $(subst $(space),$(comma),$(addsuffix =$(import_path)/$(services_pkg),$(addprefix M,$(services_pbs))))
|
||||
services_pbs = $(sort $(wildcard hapi/services/*.proto))
|
||||
services_pkg = services
|
||||
|
||||
rudder_ias = $(subst $(space),$(comma),$(addsuffix =$(import_path)/$(rudder_pkg),$(addprefix M,$(rudder_pbs))))
|
||||
rudder_pbs = $(sort $(wildcard hapi/rudder/*.proto))
|
||||
rudder_pkg = rudder
|
||||
|
||||
version_ias = $(subst $(space),$(comma),$(addsuffix =$(import_path)/$(version_pkg),$(addprefix M,$(version_pbs))))
|
||||
version_pbs = $(sort $(wildcard hapi/version/*.proto))
|
||||
version_pkg = version
|
||||
|
||||
google_deps = Mgoogle/protobuf/timestamp.proto=github.com/golang/protobuf/ptypes/timestamp,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any
|
||||
|
||||
.PHONY: all
|
||||
all: chart release services rudder version
|
||||
|
||||
.PHONY: chart
|
||||
chart:
|
||||
PATH=../bin:$(PATH) protoc --$(target)_out=plugins=$(plugins),$(google_deps),$(chart_ias):$(dst) $(chart_pbs)
|
||||
|
||||
.PHONY: release
|
||||
release:
|
||||
PATH=../bin:$(PATH) protoc --$(target)_out=plugins=$(plugins),$(google_deps),$(chart_ias),$(version_ias):$(dst) $(release_pbs)
|
||||
|
||||
.PHONY: services
|
||||
services:
|
||||
PATH=../bin:$(PATH) protoc --$(target)_out=plugins=$(plugins),$(google_deps),$(chart_ias),$(version_ias),$(release_ias):$(dst) $(services_pbs)
|
||||
|
||||
.PHONY: rudder
|
||||
rudder:
|
||||
PATH=../bin:$(PATH) protoc --$(target)_out=plugins=$(plugins),$(google_deps),$(chart_ias),$(version_ias),$(release_ias):$(dst) $(rudder_pbs)
|
||||
|
||||
.PHONY: version
|
||||
version:
|
||||
PATH=../bin:$(PATH) protoc --$(target)_out=plugins=$(plugins),$(google_deps):$(dst) $(version_pbs)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dst)/hapi 2>/dev/null
|
44
src/vendor/github.com/kubernetes/helm/_proto/hapi/chart/chart.proto
generated
vendored
Normal file
44
src/vendor/github.com/kubernetes/helm/_proto/hapi/chart/chart.proto
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.chart;
|
||||
|
||||
import "hapi/chart/config.proto";
|
||||
import "hapi/chart/metadata.proto";
|
||||
import "hapi/chart/template.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option go_package = "chart";
|
||||
|
||||
// Chart is a helm package that contains metadata, a default config, zero or more
|
||||
// optionally parameterizable templates, and zero or more charts (dependencies).
|
||||
message Chart {
|
||||
// Contents of the Chartfile.
|
||||
hapi.chart.Metadata metadata = 1;
|
||||
|
||||
// Templates for this chart.
|
||||
repeated hapi.chart.Template templates = 2;
|
||||
|
||||
// Charts that this chart depends on.
|
||||
repeated Chart dependencies = 3;
|
||||
|
||||
// Default config for this template.
|
||||
hapi.chart.Config values = 4;
|
||||
|
||||
// Miscellaneous files in a chart archive,
|
||||
// e.g. README, LICENSE, etc.
|
||||
repeated google.protobuf.Any files = 5;
|
||||
}
|
31
src/vendor/github.com/kubernetes/helm/_proto/hapi/chart/config.proto
generated
vendored
Normal file
31
src/vendor/github.com/kubernetes/helm/_proto/hapi/chart/config.proto
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.chart;
|
||||
|
||||
option go_package = "chart";
|
||||
|
||||
// Config supplies values to the parametrizable templates of a chart.
|
||||
message Config {
|
||||
string raw = 1;
|
||||
|
||||
map<string,Value> values = 2;
|
||||
}
|
||||
|
||||
// Value describes a configuration value as a string.
|
||||
message Value {
|
||||
string value = 1;
|
||||
}
|
93
src/vendor/github.com/kubernetes/helm/_proto/hapi/chart/metadata.proto
generated
vendored
Normal file
93
src/vendor/github.com/kubernetes/helm/_proto/hapi/chart/metadata.proto
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.chart;
|
||||
|
||||
option go_package = "chart";
|
||||
|
||||
// Maintainer describes a Chart maintainer.
|
||||
message Maintainer {
|
||||
// Name is a user name or organization name
|
||||
string name = 1;
|
||||
|
||||
// Email is an optional email address to contact the named maintainer
|
||||
string email = 2;
|
||||
|
||||
// Url is an optional URL to an address for the named maintainer
|
||||
string url = 3;
|
||||
}
|
||||
|
||||
// Metadata for a Chart file. This models the structure of a Chart.yaml file.
|
||||
//
|
||||
// Spec: https://k8s.io/helm/blob/master/docs/design/chart_format.md#the-chart-file
|
||||
message Metadata {
|
||||
enum Engine {
|
||||
UNKNOWN = 0;
|
||||
GOTPL = 1;
|
||||
}
|
||||
// The name of the chart
|
||||
string name = 1;
|
||||
|
||||
// The URL to a relevant project page, git repo, or contact person
|
||||
string home = 2;
|
||||
|
||||
// Source is the URL to the source code of this chart
|
||||
repeated string sources = 3;
|
||||
|
||||
// A SemVer 2 conformant version string of the chart
|
||||
string version = 4;
|
||||
|
||||
// A one-sentence description of the chart
|
||||
string description = 5;
|
||||
|
||||
// A list of string keywords
|
||||
repeated string keywords = 6;
|
||||
|
||||
// A list of name and URL/email address combinations for the maintainer(s)
|
||||
repeated Maintainer maintainers = 7;
|
||||
|
||||
// The name of the template engine to use. Defaults to 'gotpl'.
|
||||
string engine = 8;
|
||||
|
||||
// The URL to an icon file.
|
||||
string icon = 9;
|
||||
|
||||
// The API Version of this chart.
|
||||
string apiVersion = 10;
|
||||
|
||||
// The condition to check to enable chart
|
||||
string condition = 11;
|
||||
|
||||
// The tags to check to enable chart
|
||||
string tags = 12;
|
||||
|
||||
// The version of the application enclosed inside of this chart.
|
||||
string appVersion = 13;
|
||||
|
||||
// Whether or not this chart is deprecated
|
||||
bool deprecated = 14;
|
||||
|
||||
// TillerVersion is a SemVer constraints on what version of Tiller is required.
|
||||
// See SemVer ranges here: https://github.com/Masterminds/semver#basic-comparisons
|
||||
string tillerVersion = 15;
|
||||
|
||||
// Annotations are additional mappings uninterpreted by Tiller,
|
||||
// made available for inspection by other applications.
|
||||
map<string,string> annotations = 16;
|
||||
|
||||
// KubeVersion is a SemVer constraint specifying the version of Kubernetes required.
|
||||
string kubeVersion = 17;
|
||||
}
|
31
src/vendor/github.com/kubernetes/helm/_proto/hapi/chart/template.proto
generated
vendored
Normal file
31
src/vendor/github.com/kubernetes/helm/_proto/hapi/chart/template.proto
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.chart;
|
||||
|
||||
option go_package = "chart";
|
||||
|
||||
// Template represents a template as a name/value pair.
|
||||
//
|
||||
// By convention, name is a relative path within the scope of the chart's
|
||||
// base directory.
|
||||
message Template {
|
||||
// Name is the path-like name of the template.
|
||||
string name = 1;
|
||||
|
||||
// Data is the template as byte data.
|
||||
bytes data = 2;
|
||||
}
|
58
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/hook.proto
generated
vendored
Normal file
58
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/hook.proto
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.release;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "release";
|
||||
|
||||
// Hook defines a hook object.
|
||||
message Hook {
|
||||
enum Event {
|
||||
UNKNOWN = 0;
|
||||
PRE_INSTALL = 1;
|
||||
POST_INSTALL = 2;
|
||||
PRE_DELETE = 3;
|
||||
POST_DELETE = 4;
|
||||
PRE_UPGRADE = 5;
|
||||
POST_UPGRADE = 6;
|
||||
PRE_ROLLBACK = 7;
|
||||
POST_ROLLBACK = 8;
|
||||
RELEASE_TEST_SUCCESS = 9;
|
||||
RELEASE_TEST_FAILURE = 10;
|
||||
}
|
||||
enum DeletePolicy {
|
||||
SUCCEEDED = 0;
|
||||
FAILED = 1;
|
||||
BEFORE_HOOK_CREATION = 2;
|
||||
}
|
||||
string name = 1;
|
||||
// Kind is the Kubernetes kind.
|
||||
string kind = 2;
|
||||
// Path is the chart-relative path to the template.
|
||||
string path = 3;
|
||||
// Manifest is the manifest contents.
|
||||
string manifest = 4;
|
||||
// Events are the events that this hook fires on.
|
||||
repeated Event events = 5;
|
||||
// LastRun indicates the date/time this was last run.
|
||||
google.protobuf.Timestamp last_run = 6;
|
||||
// Weight indicates the sort order for execution among similar Hook type
|
||||
int32 weight = 7;
|
||||
// DeletePolicies are the policies that indicate when to delete the hook
|
||||
repeated DeletePolicy delete_policies = 8;
|
||||
}
|
37
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/info.proto
generated
vendored
Normal file
37
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/info.proto
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.release;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "hapi/release/status.proto";
|
||||
|
||||
option go_package = "release";
|
||||
|
||||
// Info describes release information.
|
||||
message Info {
|
||||
Status status = 1;
|
||||
|
||||
google.protobuf.Timestamp first_deployed = 2;
|
||||
|
||||
google.protobuf.Timestamp last_deployed = 3;
|
||||
|
||||
// Deleted tracks when this object was deleted.
|
||||
google.protobuf.Timestamp deleted = 4;
|
||||
|
||||
// Description is human-friendly "log entry" about this release.
|
||||
string Description = 5;
|
||||
}
|
53
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/release.proto
generated
vendored
Normal file
53
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/release.proto
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.release;
|
||||
|
||||
import "hapi/release/hook.proto";
|
||||
import "hapi/release/info.proto";
|
||||
import "hapi/chart/config.proto";
|
||||
import "hapi/chart/chart.proto";
|
||||
|
||||
option go_package = "release";
|
||||
|
||||
// Release describes a deployment of a chart, together with the chart
|
||||
// and the variables used to deploy that chart.
|
||||
message Release {
|
||||
// Name is the name of the release
|
||||
string name = 1;
|
||||
|
||||
// Info provides information about a release
|
||||
hapi.release.Info info = 2;
|
||||
|
||||
// Chart is the chart that was released.
|
||||
hapi.chart.Chart chart = 3;
|
||||
|
||||
// Config is the set of extra Values added to the chart.
|
||||
// These values override the default values inside of the chart.
|
||||
hapi.chart.Config config = 4;
|
||||
|
||||
// Manifest is the string representation of the rendered template.
|
||||
string manifest = 5;
|
||||
|
||||
// Hooks are all of the hooks declared for this release.
|
||||
repeated hapi.release.Hook hooks = 6;
|
||||
|
||||
// Version is an int32 which represents the version of the release.
|
||||
int32 version = 7;
|
||||
|
||||
// Namespace is the kubernetes namespace of the release.
|
||||
string namespace = 8;
|
||||
}
|
61
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/status.proto
generated
vendored
Normal file
61
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/status.proto
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.release;
|
||||
|
||||
import "hapi/release/test_suite.proto";
|
||||
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option go_package = "release";
|
||||
|
||||
// Status defines the status of a release.
|
||||
message Status {
|
||||
enum Code {
|
||||
// Status_UNKNOWN indicates that a release is in an uncertain state.
|
||||
UNKNOWN = 0;
|
||||
// Status_DEPLOYED indicates that the release has been pushed to Kubernetes.
|
||||
DEPLOYED = 1;
|
||||
// Status_DELETED indicates that a release has been deleted from Kubermetes.
|
||||
DELETED = 2;
|
||||
// Status_SUPERSEDED indicates that this release object is outdated and a newer one exists.
|
||||
SUPERSEDED = 3;
|
||||
// Status_FAILED indicates that the release was not successfully deployed.
|
||||
FAILED = 4;
|
||||
// Status_DELETING indicates that a delete operation is underway.
|
||||
DELETING = 5;
|
||||
// Status_PENDING_INSTALL indicates that an install operation is underway.
|
||||
PENDING_INSTALL = 6;
|
||||
// Status_PENDING_UPGRADE indicates that an upgrade operation is underway.
|
||||
PENDING_UPGRADE = 7;
|
||||
// Status_PENDING_ROLLBACK indicates that an rollback operation is underway.
|
||||
PENDING_ROLLBACK = 8;
|
||||
}
|
||||
|
||||
Code code = 1;
|
||||
|
||||
// Deprecated
|
||||
// google.protobuf.Any details = 2;
|
||||
|
||||
// Cluster resources as kubectl would print them.
|
||||
string resources = 3;
|
||||
|
||||
// Contains the rendered templates/NOTES.txt if available
|
||||
string notes = 4;
|
||||
|
||||
// LastTestSuiteRun provides results on the last test run on a release
|
||||
hapi.release.TestSuite last_test_suite_run = 5;
|
||||
}
|
37
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/test_run.proto
generated
vendored
Normal file
37
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/test_run.proto
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.release;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
option go_package = "release";
|
||||
|
||||
message TestRun {
|
||||
enum Status {
|
||||
UNKNOWN = 0;
|
||||
SUCCESS = 1;
|
||||
FAILURE = 2;
|
||||
RUNNING = 3;
|
||||
}
|
||||
|
||||
string name = 1;
|
||||
Status status = 2;
|
||||
string info = 3;
|
||||
google.protobuf.Timestamp started_at = 4;
|
||||
google.protobuf.Timestamp completed_at = 5;
|
||||
}
|
34
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/test_suite.proto
generated
vendored
Normal file
34
src/vendor/github.com/kubernetes/helm/_proto/hapi/release/test_suite.proto
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.release;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "hapi/release/test_run.proto";
|
||||
|
||||
option go_package = "release";
|
||||
|
||||
// TestSuite comprises of the last run of the pre-defined test suite of a release version
|
||||
message TestSuite {
|
||||
// StartedAt indicates the date/time this test suite was kicked off
|
||||
google.protobuf.Timestamp started_at = 1;
|
||||
|
||||
// CompletedAt indicates the date/time this test suite was completed
|
||||
google.protobuf.Timestamp completed_at = 2;
|
||||
|
||||
// Results are the results of each segment of the test
|
||||
repeated hapi.release.TestRun results = 3;
|
||||
}
|
120
src/vendor/github.com/kubernetes/helm/_proto/hapi/rudder/rudder.proto
generated
vendored
Normal file
120
src/vendor/github.com/kubernetes/helm/_proto/hapi/rudder/rudder.proto
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2017 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.services.rudder;
|
||||
|
||||
import "hapi/release/info.proto";
|
||||
import "hapi/release/release.proto";
|
||||
|
||||
option go_package = "rudder";
|
||||
|
||||
service ReleaseModuleService {
|
||||
rpc Version(VersionReleaseRequest) returns (VersionReleaseResponse) {
|
||||
}
|
||||
|
||||
// InstallRelease requests installation of a chart as a new release.
|
||||
rpc InstallRelease(InstallReleaseRequest) returns (InstallReleaseResponse) {
|
||||
}
|
||||
|
||||
// DeleteRelease requests deletion of a named release.
|
||||
rpc DeleteRelease(DeleteReleaseRequest) returns (DeleteReleaseResponse) {
|
||||
}
|
||||
|
||||
// RollbackRelease rolls back a release to a previous version.
|
||||
rpc RollbackRelease(RollbackReleaseRequest) returns (RollbackReleaseResponse) {
|
||||
}
|
||||
|
||||
// UpgradeRelease updates release content.
|
||||
rpc UpgradeRelease(UpgradeReleaseRequest) returns (UpgradeReleaseResponse) {
|
||||
}
|
||||
|
||||
// ReleaseStatus retrieves release status.
|
||||
rpc ReleaseStatus(ReleaseStatusRequest) returns (ReleaseStatusResponse) {
|
||||
}
|
||||
}
|
||||
|
||||
message Result {
|
||||
enum Status {
|
||||
// No status set
|
||||
UNKNOWN = 0;
|
||||
// Operation was successful
|
||||
SUCCESS = 1;
|
||||
// Operation had no results (e.g. upgrade identical, rollback to same, delete non-existent)
|
||||
UNCHANGED = 2;
|
||||
// Operation failed
|
||||
ERROR = 3;
|
||||
}
|
||||
string info = 1;
|
||||
repeated string log = 2;
|
||||
}
|
||||
|
||||
message VersionReleaseRequest {
|
||||
}
|
||||
|
||||
message VersionReleaseResponse {
|
||||
string name = 1; // The canonical name of the release module
|
||||
string version = 2; // The version of the release module
|
||||
}
|
||||
|
||||
message InstallReleaseRequest {
|
||||
hapi.release.Release release = 1;
|
||||
}
|
||||
message InstallReleaseResponse {
|
||||
hapi.release.Release release = 1;
|
||||
Result result = 2;
|
||||
}
|
||||
|
||||
message DeleteReleaseRequest {
|
||||
hapi.release.Release release = 1;
|
||||
}
|
||||
message DeleteReleaseResponse {
|
||||
hapi.release.Release release = 1;
|
||||
Result result = 2;
|
||||
}
|
||||
|
||||
message UpgradeReleaseRequest{
|
||||
hapi.release.Release current = 1;
|
||||
hapi.release.Release target = 2;
|
||||
int64 Timeout = 3;
|
||||
bool Wait = 4;
|
||||
bool Recreate = 5;
|
||||
bool Force = 6;
|
||||
}
|
||||
message UpgradeReleaseResponse{
|
||||
hapi.release.Release release = 1;
|
||||
Result result = 2;
|
||||
}
|
||||
|
||||
message RollbackReleaseRequest{
|
||||
hapi.release.Release current = 1;
|
||||
hapi.release.Release target = 2;
|
||||
int64 Timeout = 3;
|
||||
bool Wait = 4;
|
||||
bool Recreate = 5;
|
||||
bool Force = 6;
|
||||
}
|
||||
message RollbackReleaseResponse{
|
||||
hapi.release.Release release = 1;
|
||||
Result result = 2;
|
||||
}
|
||||
|
||||
message ReleaseStatusRequest{
|
||||
hapi.release.Release release = 1;
|
||||
}
|
||||
message ReleaseStatusResponse{
|
||||
hapi.release.Release release = 1;
|
||||
hapi.release.Info info = 2;
|
||||
}
|
337
src/vendor/github.com/kubernetes/helm/_proto/hapi/services/tiller.proto
generated
vendored
Normal file
337
src/vendor/github.com/kubernetes/helm/_proto/hapi/services/tiller.proto
generated
vendored
Normal file
@ -0,0 +1,337 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.services.tiller;
|
||||
|
||||
import "hapi/chart/chart.proto";
|
||||
import "hapi/chart/config.proto";
|
||||
import "hapi/release/release.proto";
|
||||
import "hapi/release/info.proto";
|
||||
import "hapi/release/test_run.proto";
|
||||
import "hapi/release/status.proto";
|
||||
import "hapi/version/version.proto";
|
||||
|
||||
option go_package = "services";
|
||||
|
||||
// ReleaseService is the service that a helm application uses to mutate,
|
||||
// query, and manage releases.
|
||||
//
|
||||
// Release: A named installation composed of a chart and
|
||||
// config. At any given time a release has one
|
||||
// chart and one config.
|
||||
//
|
||||
// Config: A config is a YAML file that supplies values
|
||||
// to the parametrizable templates of a chart.
|
||||
//
|
||||
// Chart: A chart is a helm package that contains
|
||||
// metadata, a default config, zero or more
|
||||
// optionally parameterizable templates, and
|
||||
// zero or more charts (dependencies).
|
||||
service ReleaseService {
|
||||
// ListReleases retrieves release history.
|
||||
// TODO: Allow filtering the set of releases by
|
||||
// release status. By default, ListAllReleases returns the releases who
|
||||
// current status is "Active".
|
||||
rpc ListReleases(ListReleasesRequest) returns (stream ListReleasesResponse) {
|
||||
}
|
||||
|
||||
// GetReleasesStatus retrieves status information for the specified release.
|
||||
rpc GetReleaseStatus(GetReleaseStatusRequest) returns (GetReleaseStatusResponse) {
|
||||
}
|
||||
|
||||
// GetReleaseContent retrieves the release content (chart + value) for the specified release.
|
||||
rpc GetReleaseContent(GetReleaseContentRequest) returns (GetReleaseContentResponse) {
|
||||
}
|
||||
|
||||
// UpdateRelease updates release content.
|
||||
rpc UpdateRelease(UpdateReleaseRequest) returns (UpdateReleaseResponse) {
|
||||
}
|
||||
|
||||
// InstallRelease requests installation of a chart as a new release.
|
||||
rpc InstallRelease(InstallReleaseRequest) returns (InstallReleaseResponse) {
|
||||
}
|
||||
|
||||
// UninstallRelease requests deletion of a named release.
|
||||
rpc UninstallRelease(UninstallReleaseRequest) returns (UninstallReleaseResponse) {
|
||||
}
|
||||
|
||||
// GetVersion returns the current version of the server.
|
||||
rpc GetVersion(GetVersionRequest) returns (GetVersionResponse) {
|
||||
}
|
||||
|
||||
// RollbackRelease rolls back a release to a previous version.
|
||||
rpc RollbackRelease(RollbackReleaseRequest) returns (RollbackReleaseResponse) {
|
||||
}
|
||||
|
||||
// ReleaseHistory retrieves a releasse's history.
|
||||
rpc GetHistory(GetHistoryRequest) returns (GetHistoryResponse) {
|
||||
}
|
||||
|
||||
// RunReleaseTest executes the tests defined of a named release
|
||||
rpc RunReleaseTest(TestReleaseRequest) returns (stream TestReleaseResponse) {
|
||||
}
|
||||
}
|
||||
|
||||
// ListReleasesRequest requests a list of releases.
|
||||
//
|
||||
// Releases can be retrieved in chunks by setting limit and offset.
|
||||
//
|
||||
// Releases can be sorted according to a few pre-determined sort stategies.
|
||||
message ListReleasesRequest {
|
||||
// Limit is the maximum number of releases to be returned.
|
||||
int64 limit = 1;
|
||||
|
||||
// Offset is the last release name that was seen. The next listing
|
||||
// operation will start with the name after this one.
|
||||
// Example: If list one returns albert, bernie, carl, and sets 'next: dennis'.
|
||||
// dennis is the offset. Supplying 'dennis' for the next request should
|
||||
// cause the next batch to return a set of results starting with 'dennis'.
|
||||
string offset = 2;
|
||||
|
||||
// SortBy is the sort field that the ListReleases server should sort data before returning.
|
||||
ListSort.SortBy sort_by = 3;
|
||||
|
||||
// Filter is a regular expression used to filter which releases should be listed.
|
||||
//
|
||||
// Anything that matches the regexp will be included in the results.
|
||||
string filter = 4;
|
||||
|
||||
// SortOrder is the ordering directive used for sorting.
|
||||
ListSort.SortOrder sort_order = 5;
|
||||
|
||||
repeated hapi.release.Status.Code status_codes = 6;
|
||||
// Namespace is the filter to select releases only from a specific namespace.
|
||||
string namespace = 7;
|
||||
}
|
||||
|
||||
// ListSort defines sorting fields on a release list.
|
||||
message ListSort{
|
||||
// SortBy defines sort operations.
|
||||
enum SortBy {
|
||||
UNKNOWN = 0;
|
||||
NAME = 1;
|
||||
LAST_RELEASED = 2;
|
||||
}
|
||||
|
||||
// SortOrder defines sort orders to augment sorting operations.
|
||||
enum SortOrder {
|
||||
ASC = 0;
|
||||
DESC = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ListReleasesResponse is a list of releases.
|
||||
message ListReleasesResponse {
|
||||
// Count is the expected total number of releases to be returned.
|
||||
int64 count = 1;
|
||||
|
||||
// Next is the name of the next release. If this is other than an empty
|
||||
// string, it means there are more results.
|
||||
string next = 2;
|
||||
|
||||
// Total is the total number of queryable releases.
|
||||
int64 total = 3;
|
||||
|
||||
// Releases is the list of found release objects.
|
||||
repeated hapi.release.Release releases = 4;
|
||||
}
|
||||
|
||||
// GetReleaseStatusRequest is a request to get the status of a release.
|
||||
message GetReleaseStatusRequest {
|
||||
// Name is the name of the release
|
||||
string name = 1;
|
||||
// Version is the version of the release
|
||||
int32 version = 2;
|
||||
}
|
||||
|
||||
// GetReleaseStatusResponse is the response indicating the status of the named release.
|
||||
message GetReleaseStatusResponse {
|
||||
// Name is the name of the release.
|
||||
string name = 1;
|
||||
|
||||
// Info contains information about the release.
|
||||
hapi.release.Info info = 2;
|
||||
|
||||
// Namespace the release was released into
|
||||
string namespace = 3;
|
||||
}
|
||||
|
||||
// GetReleaseContentRequest is a request to get the contents of a release.
|
||||
message GetReleaseContentRequest {
|
||||
// The name of the release
|
||||
string name = 1;
|
||||
// Version is the version of the release
|
||||
int32 version = 2;
|
||||
}
|
||||
|
||||
// GetReleaseContentResponse is a response containing the contents of a release.
|
||||
message GetReleaseContentResponse {
|
||||
// The release content
|
||||
hapi.release.Release release = 1;
|
||||
}
|
||||
|
||||
// UpdateReleaseRequest updates a release.
|
||||
message UpdateReleaseRequest {
|
||||
// The name of the release
|
||||
string name = 1;
|
||||
// Chart is the protobuf representation of a chart.
|
||||
hapi.chart.Chart chart = 2;
|
||||
// Values is a string containing (unparsed) YAML values.
|
||||
hapi.chart.Config values = 3;
|
||||
// dry_run, if true, will run through the release logic, but neither create
|
||||
bool dry_run = 4;
|
||||
// DisableHooks causes the server to skip running any hooks for the upgrade.
|
||||
bool disable_hooks = 5;
|
||||
// Performs pods restart for resources if applicable
|
||||
bool recreate = 6;
|
||||
// timeout specifies the max amount of time any kubernetes client command can run.
|
||||
int64 timeout = 7;
|
||||
// ResetValues will cause Tiller to ignore stored values, resetting to default values.
|
||||
bool reset_values = 8;
|
||||
// wait, if true, will wait until all Pods, PVCs, and Services are in a ready state
|
||||
// before marking the release as successful. It will wait for as long as timeout
|
||||
bool wait = 9;
|
||||
// ReuseValues will cause Tiller to reuse the values from the last release.
|
||||
// This is ignored if reset_values is set.
|
||||
bool reuse_values = 10;
|
||||
// Force resource update through delete/recreate if needed.
|
||||
bool force = 11;
|
||||
}
|
||||
|
||||
// UpdateReleaseResponse is the response to an update request.
|
||||
message UpdateReleaseResponse {
|
||||
hapi.release.Release release = 1;
|
||||
}
|
||||
|
||||
message RollbackReleaseRequest {
|
||||
// The name of the release
|
||||
string name = 1;
|
||||
// dry_run, if true, will run through the release logic but no create
|
||||
bool dry_run = 2;
|
||||
// DisableHooks causes the server to skip running any hooks for the rollback
|
||||
bool disable_hooks = 3;
|
||||
// Version is the version of the release to deploy.
|
||||
int32 version = 4;
|
||||
// Performs pods restart for resources if applicable
|
||||
bool recreate = 5;
|
||||
// timeout specifies the max amount of time any kubernetes client command can run.
|
||||
int64 timeout = 6;
|
||||
// wait, if true, will wait until all Pods, PVCs, and Services are in a ready state
|
||||
// before marking the release as successful. It will wait for as long as timeout
|
||||
bool wait = 7;
|
||||
// Force resource update through delete/recreate if needed.
|
||||
bool force = 8;
|
||||
}
|
||||
|
||||
// RollbackReleaseResponse is the response to an update request.
|
||||
message RollbackReleaseResponse {
|
||||
hapi.release.Release release = 1;
|
||||
}
|
||||
|
||||
// InstallReleaseRequest is the request for an installation of a chart.
|
||||
message InstallReleaseRequest {
|
||||
// Chart is the protobuf representation of a chart.
|
||||
hapi.chart.Chart chart = 1;
|
||||
// Values is a string containing (unparsed) YAML values.
|
||||
hapi.chart.Config values = 2;
|
||||
// DryRun, if true, will run through the release logic, but neither create
|
||||
// a release object nor deploy to Kubernetes. The release object returned
|
||||
// in the response will be fake.
|
||||
bool dry_run = 3;
|
||||
|
||||
// Name is the candidate release name. This must be unique to the
|
||||
// namespace, otherwise the server will return an error. If it is not
|
||||
// supplied, the server will autogenerate one.
|
||||
string name = 4;
|
||||
|
||||
// DisableHooks causes the server to skip running any hooks for the install.
|
||||
bool disable_hooks = 5;
|
||||
|
||||
// Namepace is the kubernetes namespace of the release.
|
||||
string namespace = 6;
|
||||
|
||||
// ReuseName requests that Tiller re-uses a name, instead of erroring out.
|
||||
bool reuse_name = 7;
|
||||
|
||||
// timeout specifies the max amount of time any kubernetes client command can run.
|
||||
int64 timeout = 8;
|
||||
// wait, if true, will wait until all Pods, PVCs, and Services are in a ready state
|
||||
// before marking the release as successful. It will wait for as long as timeout
|
||||
bool wait = 9;
|
||||
}
|
||||
|
||||
// InstallReleaseResponse is the response from a release installation.
|
||||
message InstallReleaseResponse {
|
||||
hapi.release.Release release = 1;
|
||||
}
|
||||
|
||||
// UninstallReleaseRequest represents a request to uninstall a named release.
|
||||
message UninstallReleaseRequest {
|
||||
// Name is the name of the release to delete.
|
||||
string name = 1;
|
||||
// DisableHooks causes the server to skip running any hooks for the uninstall.
|
||||
bool disable_hooks = 2;
|
||||
// Purge removes the release from the store and make its name free for later use.
|
||||
bool purge = 3;
|
||||
// timeout specifies the max amount of time any kubernetes client command can run.
|
||||
int64 timeout = 4;
|
||||
}
|
||||
|
||||
// UninstallReleaseResponse represents a successful response to an uninstall request.
|
||||
message UninstallReleaseResponse {
|
||||
// Release is the release that was marked deleted.
|
||||
hapi.release.Release release = 1;
|
||||
// Info is an uninstall message
|
||||
string info = 2;
|
||||
}
|
||||
|
||||
// GetVersionRequest requests for version information.
|
||||
message GetVersionRequest {
|
||||
}
|
||||
|
||||
message GetVersionResponse {
|
||||
hapi.version.Version Version = 1;
|
||||
}
|
||||
|
||||
// GetHistoryRequest requests a release's history.
|
||||
message GetHistoryRequest {
|
||||
// The name of the release.
|
||||
string name = 1;
|
||||
// The maximum number of releases to include.
|
||||
int32 max = 2;
|
||||
}
|
||||
|
||||
// GetHistoryResponse is received in response to a GetHistory rpc.
|
||||
message GetHistoryResponse {
|
||||
repeated hapi.release.Release releases = 1;
|
||||
}
|
||||
|
||||
// TestReleaseRequest is a request to get the status of a release.
|
||||
message TestReleaseRequest {
|
||||
// Name is the name of the release
|
||||
string name = 1;
|
||||
// timeout specifies the max amount of time any kubernetes client command can run.
|
||||
int64 timeout = 2;
|
||||
// cleanup specifies whether or not to attempt pod deletion after test completes
|
||||
bool cleanup = 3;
|
||||
}
|
||||
|
||||
// TestReleaseResponse represents a message from executing a test
|
||||
message TestReleaseResponse {
|
||||
string msg = 1;
|
||||
hapi.release.TestRun.Status status = 2;
|
||||
|
||||
}
|
26
src/vendor/github.com/kubernetes/helm/_proto/hapi/version/version.proto
generated
vendored
Normal file
26
src/vendor/github.com/kubernetes/helm/_proto/hapi/version/version.proto
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package hapi.version;
|
||||
|
||||
option go_package = "version";
|
||||
|
||||
message Version {
|
||||
// Sem ver string for the version
|
||||
string sem_ver = 1;
|
||||
string git_commit = 2;
|
||||
string git_tree_state = 3;
|
||||
}
|
229
src/vendor/github.com/kubernetes/helm/cmd/helm/completion.go
generated
vendored
Normal file
229
src/vendor/github.com/kubernetes/helm/cmd/helm/completion.go
generated
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"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 fmt.Errorf("shell not specified")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("too many arguments, expected only the shell type")
|
||||
}
|
||||
run, found := completionShells[args[0]]
|
||||
if !found {
|
||||
return fmt.Errorf("unsupported shell type %q", args[0])
|
||||
}
|
||||
|
||||
return run(out, cmd)
|
||||
}
|
||||
|
||||
func runCompletionBash(out io.Writer, cmd *cobra.Command) error {
|
||||
return cmd.Root().GenBashCompletion(out)
|
||||
}
|
||||
|
||||
func runCompletionZsh(out io.Writer, cmd *cobra.Command) error {
|
||||
zshInitialization := `
|
||||
__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
|
||||
echo "${w}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
__helm_compopt() {
|
||||
true # don't do anything. Not supported by bashcompinit in zsh
|
||||
}
|
||||
__helm_declare() {
|
||||
if [ "$1" == "-F" ]; then
|
||||
whence -w "$@"
|
||||
else
|
||||
builtin declare "$@"
|
||||
fi
|
||||
}
|
||||
__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; 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}/__helm_declare/g" \
|
||||
-e "s/\\\$(type${RWORD}/\$(__helm_type/g" \
|
||||
<<'BASH_COMPLETION_EOF'
|
||||
`
|
||||
out.Write([]byte(zshInitialization))
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
cmd.Root().GenBashCompletion(buf)
|
||||
out.Write(buf.Bytes())
|
||||
|
||||
zshTail := `
|
||||
BASH_COMPLETION_EOF
|
||||
}
|
||||
__helm_bash_source <(__helm_convert_bash_to_zsh)
|
||||
`
|
||||
out.Write([]byte(zshTail))
|
||||
return nil
|
||||
}
|
105
src/vendor/github.com/kubernetes/helm/cmd/helm/create.go
generated
vendored
Normal file
105
src/vendor/github.com/kubernetes/helm/cmd/helm/create.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/proto/hapi/chart"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
'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 createCmd struct {
|
||||
home helmpath.Home
|
||||
name string
|
||||
out io.Writer
|
||||
starter string
|
||||
}
|
||||
|
||||
func newCreateCmd(out io.Writer) *cobra.Command {
|
||||
cc := &createCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "create NAME",
|
||||
Short: "create a new chart with the given name",
|
||||
Long: createDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cc.home = settings.Home
|
||||
if len(args) == 0 {
|
||||
return errors.New("the name of the new chart is required")
|
||||
}
|
||||
cc.name = args[0]
|
||||
return cc.run()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&cc.starter, "starter", "p", "", "the named Helm starter scaffold")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *createCmd) run() error {
|
||||
fmt.Fprintf(c.out, "Creating %s\n", c.name)
|
||||
|
||||
chartname := filepath.Base(c.name)
|
||||
cfile := &chart.Metadata{
|
||||
Name: chartname,
|
||||
Description: "A Helm chart for Kubernetes",
|
||||
Version: "0.1.0",
|
||||
AppVersion: "1.0",
|
||||
ApiVersion: chartutil.ApiVersionV1,
|
||||
}
|
||||
|
||||
if c.starter != "" {
|
||||
// Create from the starter
|
||||
lstarter := filepath.Join(c.home.Starters(), c.starter)
|
||||
return chartutil.CreateFrom(cfile, filepath.Dir(c.name), lstarter)
|
||||
}
|
||||
|
||||
_, err := chartutil.Create(cfile, filepath.Dir(c.name))
|
||||
return err
|
||||
}
|
164
src/vendor/github.com/kubernetes/helm/cmd/helm/create_test.go
generated
vendored
Normal file
164
src/vendor/github.com/kubernetes/helm/cmd/helm/create_test.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/proto/hapi/chart"
|
||||
)
|
||||
|
||||
func TestCreateCmd(t *testing.T) {
|
||||
cname := "testchart"
|
||||
// Make a temp dir
|
||||
tdir, err := ioutil.TempDir("", "helm-create-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tdir)
|
||||
|
||||
// CD into it
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Chdir(tdir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Chdir(pwd)
|
||||
|
||||
// Run a create
|
||||
cmd := newCreateCmd(ioutil.Discard)
|
||||
if err := cmd.RunE(cmd, []string{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 := chartutil.LoadDir(cname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.Metadata.Name != cname {
|
||||
t.Errorf("Expected %q name, got %q", cname, c.Metadata.Name)
|
||||
}
|
||||
if c.Metadata.ApiVersion != chartutil.ApiVersionV1 {
|
||||
t.Errorf("Wrong API version: %q", c.Metadata.ApiVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateStarterCmd(t *testing.T) {
|
||||
cname := "testchart"
|
||||
// Make a temp dir
|
||||
tdir, err := ioutil.TempDir("", "helm-create-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tdir)
|
||||
|
||||
thome, err := tempHelmHome(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.RemoveAll(thome.String())
|
||||
cleanup()
|
||||
}()
|
||||
|
||||
settings.Home = thome
|
||||
|
||||
// Create a starter.
|
||||
starterchart := filepath.Join(thome.String(), "starters")
|
||||
os.Mkdir(starterchart, 0755)
|
||||
if dest, err := chartutil.Create(&chart.Metadata{Name: "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"), 0755); err != nil {
|
||||
t.Fatalf("Could not write template: %s", err)
|
||||
}
|
||||
|
||||
// CD into it
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Chdir(tdir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Chdir(pwd)
|
||||
|
||||
// Run a create
|
||||
cmd := newCreateCmd(ioutil.Discard)
|
||||
cmd.ParseFlags([]string{"--starter", "starterchart"})
|
||||
if err := cmd.RunE(cmd, []string{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 := chartutil.LoadDir(cname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.Metadata.Name != cname {
|
||||
t.Errorf("Expected %q name, got %q", cname, c.Metadata.Name)
|
||||
}
|
||||
if c.Metadata.ApiVersion != chartutil.ApiVersionV1 {
|
||||
t.Errorf("Wrong API version: %q", c.Metadata.ApiVersion)
|
||||
}
|
||||
|
||||
if l := len(c.Templates); l != 6 {
|
||||
t.Errorf("Expected 5 templates, got %d", l)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, tpl := range c.Templates {
|
||||
if tpl.Name == "templates/foo.tpl" {
|
||||
found = true
|
||||
data := tpl.Data
|
||||
if string(data) != "test" {
|
||||
t.Errorf("Expected template 'test', got %q", string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("Did not find foo.tpl")
|
||||
}
|
||||
|
||||
}
|
101
src/vendor/github.com/kubernetes/helm/cmd/helm/delete.go
generated
vendored
Executable file
101
src/vendor/github.com/kubernetes/helm/cmd/helm/delete.go
generated
vendored
Executable file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
||||
const deleteDesc = `
|
||||
This command takes a release name, and then deletes the release from Kubernetes.
|
||||
It removes all of the resources associated with the last release of the chart.
|
||||
|
||||
Use the '--dry-run' flag to see which releases will be deleted without actually
|
||||
deleting them.
|
||||
`
|
||||
|
||||
type deleteCmd struct {
|
||||
name string
|
||||
dryRun bool
|
||||
disableHooks bool
|
||||
purge bool
|
||||
timeout int64
|
||||
|
||||
out io.Writer
|
||||
client helm.Interface
|
||||
}
|
||||
|
||||
func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
del := &deleteCmd{
|
||||
out: out,
|
||||
client: c,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete [flags] RELEASE_NAME [...]",
|
||||
Aliases: []string{"del"},
|
||||
SuggestFor: []string{"remove", "rm"},
|
||||
Short: "given a release name, delete the release from Kubernetes",
|
||||
Long: deleteDesc,
|
||||
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("command 'delete' requires a release name")
|
||||
}
|
||||
del.client = ensureHelmClient(del.client)
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
del.name = args[i]
|
||||
if err := del.run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "release \"%s\" deleted\n", del.name)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&del.dryRun, "dry-run", false, "simulate a delete")
|
||||
f.BoolVar(&del.disableHooks, "no-hooks", false, "prevent hooks from running during deletion")
|
||||
f.BoolVar(&del.purge, "purge", false, "remove the release from the store and make its name free for later use")
|
||||
f.Int64Var(&del.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (d *deleteCmd) run() error {
|
||||
opts := []helm.DeleteOption{
|
||||
helm.DeleteDryRun(d.dryRun),
|
||||
helm.DeleteDisableHooks(d.disableHooks),
|
||||
helm.DeletePurge(d.purge),
|
||||
helm.DeleteTimeout(d.timeout),
|
||||
}
|
||||
res, err := d.client.DeleteRelease(d.name, opts...)
|
||||
if res != nil && res.Info != "" {
|
||||
fmt.Fprintln(d.out, res.Info)
|
||||
}
|
||||
|
||||
return prettyError(err)
|
||||
}
|
73
src/vendor/github.com/kubernetes/helm/cmd/helm/delete_test.go
generated
vendored
Normal file
73
src/vendor/github.com/kubernetes/helm/cmd/helm/delete_test.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
|
||||
tests := []releaseCase{
|
||||
{
|
||||
name: "basic delete",
|
||||
args: []string{"aeneas"},
|
||||
flags: []string{},
|
||||
expected: "", // Output of a delete is an empty string and exit 0.
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}),
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})},
|
||||
},
|
||||
{
|
||||
name: "delete with timeout",
|
||||
args: []string{"aeneas"},
|
||||
flags: []string{"--timeout", "120"},
|
||||
expected: "",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}),
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})},
|
||||
},
|
||||
{
|
||||
name: "delete without hooks",
|
||||
args: []string{"aeneas"},
|
||||
flags: []string{"--no-hooks"},
|
||||
expected: "",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}),
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})},
|
||||
},
|
||||
{
|
||||
name: "purge",
|
||||
args: []string{"aeneas"},
|
||||
flags: []string{"--purge"},
|
||||
expected: "",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}),
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})},
|
||||
},
|
||||
{
|
||||
name: "delete without release",
|
||||
args: []string{},
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newDeleteCmd(c, out)
|
||||
})
|
||||
}
|
276
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency.go
generated
vendored
Normal file
276
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency.go
generated
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/Masterminds/semver"
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
)
|
||||
|
||||
const dependencyDesc = `
|
||||
Manage the dependencies of a chart.
|
||||
|
||||
Helm charts store their dependencies in 'charts/'. For chart developers, it is
|
||||
often easier to manage a single dependency file ('requirements.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.
|
||||
|
||||
A 'requirements.yaml' file is a YAML file in which developers can declare chart
|
||||
dependencies, along with the location of the chart and the desired version.
|
||||
For example, this requirements file declares two dependencies:
|
||||
|
||||
# requirements.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,
|
||||
|
||||
# requirements.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. It will emit a warning
|
||||
if it cannot find a requirements.yaml.
|
||||
`
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
cmd.AddCommand(newDependencyListCmd(out))
|
||||
cmd.AddCommand(newDependencyUpdateCmd(out))
|
||||
cmd.AddCommand(newDependencyBuildCmd(out))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type dependencyListCmd struct {
|
||||
out io.Writer
|
||||
chartpath string
|
||||
}
|
||||
|
||||
func newDependencyListCmd(out io.Writer) *cobra.Command {
|
||||
dlc := &dependencyListCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list [flags] CHART",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "list the dependencies for the given chart",
|
||||
Long: dependencyListDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cp := "."
|
||||
if len(args) > 0 {
|
||||
cp = args[0]
|
||||
}
|
||||
|
||||
var err error
|
||||
dlc.chartpath, err = filepath.Abs(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dlc.run()
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (l *dependencyListCmd) run() error {
|
||||
c, err := chartutil.Load(l.chartpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := chartutil.LoadRequirements(c)
|
||||
if err != nil {
|
||||
if err == chartutil.ErrRequirementsNotFound {
|
||||
fmt.Fprintf(l.out, "WARNING: no requirements at %s/charts\n", l.chartpath)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
l.printRequirements(r, l.out)
|
||||
fmt.Fprintln(l.out)
|
||||
l.printMissing(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *dependencyListCmd) dependencyStatus(dep *chartutil.Dependency) string {
|
||||
filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*")
|
||||
archives, err := filepath.Glob(filepath.Join(l.chartpath, "charts", filename))
|
||||
if err != nil {
|
||||
return "bad pattern"
|
||||
} else if len(archives) > 1 {
|
||||
return "too many matches"
|
||||
} else if len(archives) == 1 {
|
||||
archive := archives[0]
|
||||
if _, err := os.Stat(archive); err == nil {
|
||||
c, err := chartutil.Load(archive)
|
||||
if err != nil {
|
||||
return "corrupt"
|
||||
}
|
||||
if c.Metadata.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(l.chartpath, "charts", dep.Name)
|
||||
if fi, err := os.Stat(folder); err != nil {
|
||||
return "missing"
|
||||
} else if !fi.IsDir() {
|
||||
return "mispackaged"
|
||||
}
|
||||
|
||||
c, err := chartutil.Load(folder)
|
||||
if err != nil {
|
||||
return "corrupt"
|
||||
}
|
||||
|
||||
if c.Metadata.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"
|
||||
}
|
||||
|
||||
// printRequirements prints all of the requirements in the yaml file.
|
||||
func (l *dependencyListCmd) printRequirements(reqs *chartutil.Requirements, out io.Writer) {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = 80
|
||||
table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS")
|
||||
for _, row := range reqs.Dependencies {
|
||||
table.AddRow(row.Name, row.Version, row.Repository, l.dependencyStatus(row))
|
||||
}
|
||||
fmt.Fprintln(out, table)
|
||||
}
|
||||
|
||||
// printMissing prints warnings about charts that are present on disk, but are not in the requirements.
|
||||
func (l *dependencyListCmd) printMissing(reqs *chartutil.Requirements) {
|
||||
folder := filepath.Join(l.chartpath, "charts/*")
|
||||
files, err := filepath.Glob(folder)
|
||||
if err != nil {
|
||||
fmt.Fprintln(l.out, err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
fi, err := os.Stat(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(l.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 := chartutil.Load(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(l.out, "WARNING: %q is not a chart.\n", f)
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for _, d := range reqs.Dependencies {
|
||||
if d.Name == c.Metadata.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
fmt.Fprintf(l.out, "WARNING: %q is not in requirements.yaml.\n", f)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
85
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_build.go
generated
vendored
Normal file
85
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_build.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
)
|
||||
|
||||
const dependencyBuildDesc = `
|
||||
Build out the charts/ directory from the requirements.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'.
|
||||
`
|
||||
|
||||
type dependencyBuildCmd struct {
|
||||
out io.Writer
|
||||
chartpath string
|
||||
verify bool
|
||||
keyring string
|
||||
helmhome helmpath.Home
|
||||
}
|
||||
|
||||
func newDependencyBuildCmd(out io.Writer) *cobra.Command {
|
||||
dbc := &dependencyBuildCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "build [flags] CHART",
|
||||
Short: "rebuild the charts/ directory based on the requirements.lock file",
|
||||
Long: dependencyBuildDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
dbc.helmhome = settings.Home
|
||||
dbc.chartpath = "."
|
||||
|
||||
if len(args) > 0 {
|
||||
dbc.chartpath = args[0]
|
||||
}
|
||||
return dbc.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&dbc.verify, "verify", false, "verify the packages against signatures")
|
||||
f.StringVar(&dbc.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (d *dependencyBuildCmd) run() error {
|
||||
man := &downloader.Manager{
|
||||
Out: d.out,
|
||||
ChartPath: d.chartpath,
|
||||
HelmHome: d.helmhome,
|
||||
Keyring: d.keyring,
|
||||
Getters: getter.All(settings),
|
||||
}
|
||||
if d.verify {
|
||||
man.Verify = downloader.VerifyIfPossible
|
||||
}
|
||||
|
||||
return man.Build()
|
||||
}
|
120
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_build_test.go
generated
vendored
Normal file
120
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_build_test.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/provenance"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/repo/repotest"
|
||||
)
|
||||
|
||||
func TestDependencyBuildCmd(t *testing.T) {
|
||||
hh, err := tempHelmHome(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.RemoveAll(hh.String())
|
||||
cleanup()
|
||||
}()
|
||||
|
||||
settings.Home = hh
|
||||
|
||||
srv := repotest.NewServer(hh.String())
|
||||
defer srv.Stop()
|
||||
_, err = srv.CopyCharts("testdata/testcharts/*.tgz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
chartname := "depbuild"
|
||||
if err := createTestingChart(hh.String(), chartname, srv.URL()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
dbc := &dependencyBuildCmd{out: out}
|
||||
dbc.helmhome = helmpath.Home(hh)
|
||||
dbc.chartpath = filepath.Join(hh.String(), chartname)
|
||||
|
||||
// In the first pass, we basically want the same results as an update.
|
||||
if err := dbc.run(); err != nil {
|
||||
output := out.String()
|
||||
t.Logf("Output: %s", output)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
output := out.String()
|
||||
if !strings.Contains(output, `update from the "test" chart repository`) {
|
||||
t.Errorf("Repo did not get updated\n%s", output)
|
||||
}
|
||||
|
||||
// Make sure the actual file got downloaded.
|
||||
expect := filepath.Join(hh.String(), 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(hh.String(), chartname, "requirements.lock")
|
||||
if _, err := os.Stat(lockfile); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.RemoveAll(expect); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := dbc.run(); err != nil {
|
||||
output := out.String()
|
||||
t.Logf("Output: %s", output)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now repeat the test that the dependency exists.
|
||||
expect = filepath.Join(hh.String(), chartname, "charts/reqtest-0.1.0.tgz")
|
||||
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(dbc.helmhome.CacheIndex("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)
|
||||
}
|
||||
if v := reqver.Version; v != "0.1.0" {
|
||||
t.Errorf("mismatched versions. Expected %q, got %q", "0.1.0", v)
|
||||
}
|
||||
|
||||
}
|
58
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_test.go
generated
vendored
Normal file
58
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_test.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
||||
func TestDependencyListCmd(t *testing.T) {
|
||||
|
||||
tests := []releaseCase{
|
||||
{
|
||||
name: "No such chart",
|
||||
args: []string{"/no/such/chart"},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
name: "No requirements.yaml",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
expected: "WARNING: no requirements at ",
|
||||
},
|
||||
{
|
||||
name: "Requirements in chart dir",
|
||||
args: []string{"testdata/testcharts/reqtest"},
|
||||
expected: "NAME \tVERSION\tREPOSITORY \tSTATUS \n" +
|
||||
"reqsubchart \t0.1.0 \thttps://example.com/charts\tunpacked\n" +
|
||||
"reqsubchart2\t0.2.0 \thttps://example.com/charts\tunpacked\n" +
|
||||
"reqsubchart3\t>=0.1.0\thttps://example.com/charts\tok \n\n",
|
||||
},
|
||||
{
|
||||
name: "Requirements in chart archive",
|
||||
args: []string{"testdata/testcharts/reqtest-0.1.0.tgz"},
|
||||
expected: "NAME \tVERSION\tREPOSITORY \tSTATUS \nreqsubchart \t0.1.0 \thttps://example.com/charts\tmissing\nreqsubchart2\t0.2.0 \thttps://example.com/charts\tmissing\n",
|
||||
},
|
||||
}
|
||||
|
||||
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newDependencyListCmd(out)
|
||||
})
|
||||
}
|
105
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_update.go
generated
vendored
Normal file
105
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_update.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
)
|
||||
|
||||
const dependencyUpDesc = `
|
||||
Update the on-disk dependencies to mirror the requirements.yaml file.
|
||||
|
||||
This command verifies that the required charts, as expressed in 'requirements.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 requirements to an exact version.
|
||||
|
||||
Dependencies are not required to be represented in 'requirements.yaml'. For that
|
||||
reason, an update command will not remove charts unless they are (a) present
|
||||
in the requirements.yaml file, but (b) at the wrong version.
|
||||
`
|
||||
|
||||
// dependencyUpdateCmd describes a 'helm dependency update'
|
||||
type dependencyUpdateCmd struct {
|
||||
out io.Writer
|
||||
chartpath string
|
||||
helmhome helmpath.Home
|
||||
verify bool
|
||||
keyring string
|
||||
skipRefresh bool
|
||||
}
|
||||
|
||||
// newDependencyUpdateCmd creates a new dependency update command.
|
||||
func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
|
||||
duc := &dependencyUpdateCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "update [flags] CHART",
|
||||
Aliases: []string{"up"},
|
||||
Short: "update charts/ based on the contents of requirements.yaml",
|
||||
Long: dependencyUpDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cp := "."
|
||||
if len(args) > 0 {
|
||||
cp = args[0]
|
||||
}
|
||||
|
||||
var err error
|
||||
duc.chartpath, err = filepath.Abs(cp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
duc.helmhome = settings.Home
|
||||
|
||||
return duc.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&duc.verify, "verify", false, "verify the packages against signatures")
|
||||
f.StringVar(&duc.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
|
||||
f.BoolVar(&duc.skipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// run runs the full dependency update process.
|
||||
func (d *dependencyUpdateCmd) run() error {
|
||||
man := &downloader.Manager{
|
||||
Out: d.out,
|
||||
ChartPath: d.chartpath,
|
||||
HelmHome: d.helmhome,
|
||||
Keyring: d.keyring,
|
||||
SkipUpdate: d.skipRefresh,
|
||||
Getters: getter.All(settings),
|
||||
}
|
||||
if d.verify {
|
||||
man.Verify = downloader.VerifyAlways
|
||||
}
|
||||
if settings.Debug {
|
||||
man.Debug = true
|
||||
}
|
||||
return man.Update()
|
||||
}
|
273
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_update_test.go
generated
vendored
Normal file
273
src/vendor/github.com/kubernetes/helm/cmd/helm/dependency_update_test.go
generated
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/proto/hapi/chart"
|
||||
"k8s.io/helm/pkg/provenance"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/repo/repotest"
|
||||
)
|
||||
|
||||
func TestDependencyUpdateCmd(t *testing.T) {
|
||||
hh, err := tempHelmHome(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.RemoveAll(hh.String())
|
||||
cleanup()
|
||||
}()
|
||||
|
||||
settings.Home = hh
|
||||
|
||||
srv := repotest.NewServer(hh.String())
|
||||
defer srv.Stop()
|
||||
copied, err := srv.CopyCharts("testdata/testcharts/*.tgz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("Copied charts:\n%s", strings.Join(copied, "\n"))
|
||||
t.Logf("Listening on directory %s", srv.Root())
|
||||
|
||||
chartname := "depup"
|
||||
if err := createTestingChart(hh.String(), chartname, srv.URL()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
duc := &dependencyUpdateCmd{out: out}
|
||||
duc.helmhome = helmpath.Home(hh)
|
||||
duc.chartpath = filepath.Join(hh.String(), chartname)
|
||||
|
||||
if err := duc.run(); err != nil {
|
||||
output := out.String()
|
||||
t.Logf("Output: %s", output)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
output := out.String()
|
||||
// This is written directly to stdout, so we have to capture as is.
|
||||
if !strings.Contains(output, `update from the "test" chart repository`) {
|
||||
t.Errorf("Repo did not get updated\n%s", output)
|
||||
}
|
||||
|
||||
// Make sure the actual file got downloaded.
|
||||
expect := filepath.Join(hh.String(), 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(duc.helmhome.CacheIndex("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.
|
||||
reqfile := &chartutil.Requirements{
|
||||
Dependencies: []*chartutil.Dependency{
|
||||
{Name: "reqtest", Version: "0.1.0", Repository: srv.URL()},
|
||||
{Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()},
|
||||
},
|
||||
}
|
||||
dir := filepath.Join(hh.String(), chartname)
|
||||
if err := writeRequirements(dir, reqfile); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := duc.run(); err != nil {
|
||||
output := out.String()
|
||||
t.Logf("Output: %s", output)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// In this second run, we should see compressedchart-0.3.0.tgz, and not
|
||||
// the 0.1.0 version.
|
||||
expect = filepath.Join(hh.String(), chartname, "charts/compressedchart-0.3.0.tgz")
|
||||
if _, err := os.Stat(expect); err != nil {
|
||||
t.Fatalf("Expected %q: %s", expect, err)
|
||||
}
|
||||
dontExpect := filepath.Join(hh.String(), chartname, "charts/compressedchart-0.1.0.tgz")
|
||||
if _, err := os.Stat(dontExpect); err == nil {
|
||||
t.Fatalf("Unexpected %q", dontExpect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) {
|
||||
hh, err := tempHelmHome(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.RemoveAll(hh.String())
|
||||
cleanup()
|
||||
}()
|
||||
|
||||
settings.Home = hh
|
||||
|
||||
srv := repotest.NewServer(hh.String())
|
||||
defer srv.Stop()
|
||||
copied, err := srv.CopyCharts("testdata/testcharts/*.tgz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("Copied charts:\n%s", strings.Join(copied, "\n"))
|
||||
t.Logf("Listening on directory %s", srv.Root())
|
||||
|
||||
chartname := "depup"
|
||||
if err := createTestingChart(hh.String(), chartname, srv.URL()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
duc := &dependencyUpdateCmd{out: out}
|
||||
duc.helmhome = helmpath.Home(hh)
|
||||
duc.chartpath = filepath.Join(hh.String(), chartname)
|
||||
duc.skipRefresh = true
|
||||
|
||||
if err := duc.run(); err == nil {
|
||||
t.Fatal("Expected failure to find the repo with skipRefresh")
|
||||
}
|
||||
|
||||
output := out.String()
|
||||
// This is written directly to stdout, so we have to capture as is.
|
||||
if strings.Contains(output, `update from the "test" chart repository`) {
|
||||
t.Errorf("Repo was unexpectedly updated\n%s", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
|
||||
hh, err := tempHelmHome(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.RemoveAll(hh.String())
|
||||
cleanup()
|
||||
}()
|
||||
|
||||
settings.Home = hh
|
||||
|
||||
srv := repotest.NewServer(hh.String())
|
||||
defer srv.Stop()
|
||||
copied, err := srv.CopyCharts("testdata/testcharts/*.tgz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("Copied charts:\n%s", strings.Join(copied, "\n"))
|
||||
t.Logf("Listening on directory %s", srv.Root())
|
||||
|
||||
chartname := "depupdelete"
|
||||
if err := createTestingChart(hh.String(), chartname, srv.URL()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
duc := &dependencyUpdateCmd{out: out}
|
||||
duc.helmhome = helmpath.Home(hh)
|
||||
duc.chartpath = filepath.Join(hh.String(), chartname)
|
||||
|
||||
if err := duc.run(); err != nil {
|
||||
output := out.String()
|
||||
t.Logf("Output: %s", output)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Chart repo is down
|
||||
srv.Stop()
|
||||
|
||||
if err := duc.run(); err == nil {
|
||||
output := out.String()
|
||||
t.Logf("Output: %s", output)
|
||||
t.Fatal("Expected error, got nil")
|
||||
}
|
||||
|
||||
// Make sure charts dir still has dependencies
|
||||
files, err := ioutil.ReadDir(filepath.Join(duc.chartpath, "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(duc.chartpath, "tmpcharts")); !os.IsNotExist(err) {
|
||||
t.Fatalf("tmpcharts dir still exists")
|
||||
}
|
||||
}
|
||||
|
||||
// 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(dest, name, baseURL string) error {
|
||||
cfile := &chart.Metadata{
|
||||
Name: name,
|
||||
Version: "1.2.3",
|
||||
}
|
||||
dir := filepath.Join(dest, name)
|
||||
_, err := chartutil.Create(cfile, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := &chartutil.Requirements{
|
||||
Dependencies: []*chartutil.Dependency{
|
||||
{Name: "reqtest", Version: "0.1.0", Repository: baseURL},
|
||||
{Name: "compressedchart", Version: "0.1.0", Repository: baseURL},
|
||||
},
|
||||
}
|
||||
return writeRequirements(dir, req)
|
||||
}
|
||||
|
||||
func writeRequirements(dir string, req *chartutil.Requirements) error {
|
||||
data, err := yaml.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filepath.Join(dir, "requirements.yaml"), data, 0655)
|
||||
}
|
80
src/vendor/github.com/kubernetes/helm/cmd/helm/docs.go
generated
vendored
Normal file
80
src/vendor/github.com/kubernetes/helm/cmd/helm/docs.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"github.com/spf13/cobra/doc"
|
||||
)
|
||||
|
||||
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.
|
||||
|
||||
$ helm docs markdown -dir mydocs/
|
||||
`
|
||||
|
||||
type docsCmd struct {
|
||||
out io.Writer
|
||||
dest string
|
||||
docTypeString string
|
||||
topCmd *cobra.Command
|
||||
}
|
||||
|
||||
func newDocsCmd(out io.Writer) *cobra.Command {
|
||||
dc := &docsCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "docs",
|
||||
Short: "Generate documentation as markdown or man pages",
|
||||
Long: docsDesc,
|
||||
Hidden: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
dc.topCmd = cmd.Root()
|
||||
return dc.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.StringVar(&dc.dest, "dir", "./", "directory to which documentation is written")
|
||||
f.StringVar(&dc.docTypeString, "type", "markdown", "the type of documentation to generate (markdown, man, bash)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (d *docsCmd) run() error {
|
||||
switch d.docTypeString {
|
||||
case "markdown", "mdown", "md":
|
||||
return doc.GenMarkdownTree(d.topCmd, d.dest)
|
||||
case "man":
|
||||
manHdr := &doc.GenManHeader{Title: "HELM", Section: "1"}
|
||||
return doc.GenManTree(d.topCmd, manHdr, d.dest)
|
||||
case "bash":
|
||||
return d.topCmd.GenBashCompletionFile(filepath.Join(d.dest, "completions.bash"))
|
||||
default:
|
||||
return fmt.Errorf("unknown doc type %q. Try 'markdown' or 'man'", d.docTypeString)
|
||||
}
|
||||
}
|
186
src/vendor/github.com/kubernetes/helm/cmd/helm/fetch.go
generated
vendored
Normal file
186
src/vendor/github.com/kubernetes/helm/cmd/helm/fetch.go
generated
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/spf13/cobra"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
const fetchDesc = `
|
||||
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.
|
||||
`
|
||||
|
||||
type fetchCmd struct {
|
||||
untar bool
|
||||
untardir string
|
||||
chartRef string
|
||||
destdir string
|
||||
version string
|
||||
repoURL string
|
||||
username string
|
||||
password string
|
||||
|
||||
verify bool
|
||||
verifyLater bool
|
||||
keyring string
|
||||
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
|
||||
devel bool
|
||||
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func newFetchCmd(out io.Writer) *cobra.Command {
|
||||
fch := &fetchCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "fetch [flags] [chart URL | repo/chartname] [...]",
|
||||
Short: "download a chart from a repository and (optionally) unpack it in local directory",
|
||||
Long: fetchDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("need at least one argument, url or repo/name of the chart")
|
||||
}
|
||||
|
||||
if fch.version == "" && fch.devel {
|
||||
debug("setting version to >0.0.0-0")
|
||||
fch.version = ">0.0.0-0"
|
||||
}
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
fch.chartRef = args[i]
|
||||
if err := fch.run(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&fch.untar, "untar", false, "if set to true, will untar the chart after downloading it")
|
||||
f.StringVar(&fch.untardir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded")
|
||||
f.BoolVar(&fch.verify, "verify", false, "verify the package against its signature")
|
||||
f.BoolVar(&fch.verifyLater, "prov", false, "fetch the provenance file, but don't perform verification")
|
||||
f.StringVar(&fch.version, "version", "", "specific version of a chart. Without this, the latest version is fetched")
|
||||
f.StringVar(&fch.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
|
||||
f.StringVarP(&fch.destdir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this")
|
||||
f.StringVar(&fch.repoURL, "repo", "", "chart repository url where to locate the requested chart")
|
||||
f.StringVar(&fch.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
|
||||
f.StringVar(&fch.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
|
||||
f.StringVar(&fch.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
|
||||
f.BoolVar(&fch.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
|
||||
f.StringVar(&fch.username, "username", "", "chart repository username")
|
||||
f.StringVar(&fch.password, "password", "", "chart repository password")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (f *fetchCmd) run() error {
|
||||
c := downloader.ChartDownloader{
|
||||
HelmHome: settings.Home,
|
||||
Out: f.out,
|
||||
Keyring: f.keyring,
|
||||
Verify: downloader.VerifyNever,
|
||||
Getters: getter.All(settings),
|
||||
Username: f.username,
|
||||
Password: f.password,
|
||||
}
|
||||
|
||||
if f.verify {
|
||||
c.Verify = downloader.VerifyAlways
|
||||
} else if f.verifyLater {
|
||||
c.Verify = downloader.VerifyLater
|
||||
}
|
||||
|
||||
// If untar is set, we fetch to a tempdir, then untar and copy after
|
||||
// verification.
|
||||
dest := f.destdir
|
||||
if f.untar {
|
||||
var err error
|
||||
dest, err = ioutil.TempDir("", "helm-")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to untar: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(dest)
|
||||
}
|
||||
|
||||
if f.repoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthRepoURL(f.repoURL, f.username, f.password, f.chartRef, f.version, f.certFile, f.keyFile, f.caFile, getter.All(settings))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.chartRef = chartURL
|
||||
}
|
||||
|
||||
saved, v, err := c.DownloadTo(f.chartRef, f.version, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.verify {
|
||||
fmt.Fprintf(f.out, "Verification: %v\n", v)
|
||||
}
|
||||
|
||||
// After verification, untar the chart into the requested directory.
|
||||
if f.untar {
|
||||
ud := f.untardir
|
||||
if !filepath.IsAbs(ud) {
|
||||
ud = filepath.Join(f.destdir, ud)
|
||||
}
|
||||
if fi, err := os.Stat(ud); err != nil {
|
||||
if err := os.MkdirAll(ud, 0755); err != nil {
|
||||
return fmt.Errorf("Failed to untar (mkdir): %s", err)
|
||||
}
|
||||
|
||||
} else if !fi.IsDir() {
|
||||
return fmt.Errorf("Failed to untar: %s is not a directory", ud)
|
||||
}
|
||||
|
||||
return chartutil.ExpandFile(ud, saved)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// defaultKeyring returns the expanded path to the default keyring.
|
||||
func defaultKeyring() string {
|
||||
return os.ExpandEnv("$HOME/.gnupg/pubring.gpg")
|
||||
}
|
179
src/vendor/github.com/kubernetes/helm/cmd/helm/fetch_test.go
generated
vendored
Normal file
179
src/vendor/github.com/kubernetes/helm/cmd/helm/fetch_test.go
generated
vendored
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/repo/repotest"
|
||||
)
|
||||
|
||||
func TestFetchCmd(t *testing.T) {
|
||||
hh, err := tempHelmHome(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.RemoveAll(hh.String())
|
||||
cleanup()
|
||||
}()
|
||||
srv := repotest.NewServer(hh.String())
|
||||
defer srv.Stop()
|
||||
|
||||
settings.Home = hh
|
||||
|
||||
// all flags will get "--home=TMDIR -d outdir" appended.
|
||||
tests := []struct {
|
||||
name string
|
||||
chart string
|
||||
flags []string
|
||||
fail bool
|
||||
failExpect string
|
||||
expectFile string
|
||||
expectDir bool
|
||||
expectVerify bool
|
||||
}{
|
||||
{
|
||||
name: "Basic chart fetch",
|
||||
chart: "test/signtest",
|
||||
expectFile: "./signtest-0.1.0.tgz",
|
||||
},
|
||||
{
|
||||
name: "Chart fetch with version",
|
||||
chart: "test/signtest",
|
||||
flags: []string{"--version", "0.1.0"},
|
||||
expectFile: "./signtest-0.1.0.tgz",
|
||||
},
|
||||
{
|
||||
name: "Fail chart fetch with non-existent version",
|
||||
chart: "test/signtest",
|
||||
flags: []string{"--version", "99.1.0"},
|
||||
fail: true,
|
||||
failExpect: "no such chart",
|
||||
},
|
||||
{
|
||||
name: "Fail fetching non-existent chart",
|
||||
chart: "test/nosuchthing",
|
||||
failExpect: "Failed to fetch",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Fetch and verify",
|
||||
chart: "test/signtest",
|
||||
flags: []string{"--verify", "--keyring", "testdata/helm-test-key.pub"},
|
||||
expectFile: "./signtest-0.1.0.tgz",
|
||||
expectVerify: true,
|
||||
},
|
||||
{
|
||||
name: "Fetch and fail verify",
|
||||
chart: "test/reqtest",
|
||||
flags: []string{"--verify", "--keyring", "testdata/helm-test-key.pub"},
|
||||
failExpect: "Failed to fetch provenance",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Fetch and untar",
|
||||
chart: "test/signtest",
|
||||
flags: []string{"--untar", "--untardir", "signtest"},
|
||||
expectFile: "./signtest",
|
||||
expectDir: true,
|
||||
},
|
||||
{
|
||||
name: "Fetch, verify, untar",
|
||||
chart: "test/signtest",
|
||||
flags: []string{"--verify", "--keyring", "testdata/helm-test-key.pub", "--untar", "--untardir", "signtest"},
|
||||
expectFile: "./signtest",
|
||||
expectDir: true,
|
||||
expectVerify: true,
|
||||
},
|
||||
{
|
||||
name: "Chart fetch using repo URL",
|
||||
chart: "signtest",
|
||||
expectFile: "./signtest-0.1.0.tgz",
|
||||
flags: []string{"--repo", srv.URL()},
|
||||
},
|
||||
{
|
||||
name: "Fail fetching non-existent chart on repo URL",
|
||||
chart: "someChart",
|
||||
flags: []string{"--repo", srv.URL()},
|
||||
failExpect: "Failed to fetch chart",
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "Specific version chart fetch using repo URL",
|
||||
chart: "signtest",
|
||||
expectFile: "./signtest-0.1.0.tgz",
|
||||
flags: []string{"--repo", srv.URL(), "--version", "0.1.0"},
|
||||
},
|
||||
{
|
||||
name: "Specific version chart fetch using repo URL",
|
||||
chart: "signtest",
|
||||
flags: []string{"--repo", srv.URL(), "--version", "0.2.0"},
|
||||
failExpect: "Failed to fetch chart version",
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := srv.CopyCharts("testdata/testcharts/*.tgz*"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := srv.LinkIndices(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
outdir := filepath.Join(hh.String(), "testout")
|
||||
os.RemoveAll(outdir)
|
||||
os.Mkdir(outdir, 0755)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
cmd := newFetchCmd(buf)
|
||||
tt.flags = append(tt.flags, "-d", outdir)
|
||||
cmd.ParseFlags(tt.flags)
|
||||
if err := cmd.RunE(cmd, []string{tt.chart}); err != nil {
|
||||
if tt.fail {
|
||||
continue
|
||||
}
|
||||
t.Errorf("%q reported error: %s", tt.name, err)
|
||||
continue
|
||||
}
|
||||
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(buf.String()) {
|
||||
t.Errorf("%q: expected match for regex %s, got %s", tt.name, verificationRegex, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
89
src/vendor/github.com/kubernetes/helm/cmd/helm/get.go
generated
vendored
Normal file
89
src/vendor/github.com/kubernetes/helm/cmd/helm/get.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
||||
var getHelp = `
|
||||
This command shows the details of a named release.
|
||||
|
||||
It can be used to get extended information about the release, including:
|
||||
|
||||
- The values used to generate the release
|
||||
- The chart used to generate the release
|
||||
- The generated manifest file
|
||||
|
||||
By default, this prints a human readable collection of information about the
|
||||
chart, the supplied values, and the generated manifest file.
|
||||
`
|
||||
|
||||
var errReleaseRequired = errors.New("release name is required")
|
||||
|
||||
type getCmd struct {
|
||||
release string
|
||||
out io.Writer
|
||||
client helm.Interface
|
||||
version int32
|
||||
}
|
||||
|
||||
func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
get := &getCmd{
|
||||
out: out,
|
||||
client: client,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [flags] RELEASE_NAME",
|
||||
Short: "download a named release",
|
||||
Long: getHelp,
|
||||
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errReleaseRequired
|
||||
}
|
||||
get.release = args[0]
|
||||
if get.client == nil {
|
||||
get.client = newClient()
|
||||
}
|
||||
return get.run()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Int32Var(&get.version, "revision", 0, "get the named release with revision")
|
||||
|
||||
cmd.AddCommand(addFlagsTLS(newGetValuesCmd(nil, out)))
|
||||
cmd.AddCommand(addFlagsTLS(newGetManifestCmd(nil, out)))
|
||||
cmd.AddCommand(addFlagsTLS(newGetHooksCmd(nil, out)))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// getCmd is the command that implements 'helm get'
|
||||
func (g *getCmd) run() error {
|
||||
res, err := g.client.ReleaseContent(g.release, helm.ContentReleaseVersion(g.version))
|
||||
if err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
return printRelease(g.out, res.Release)
|
||||
}
|
75
src/vendor/github.com/kubernetes/helm/cmd/helm/get_hooks.go
generated
vendored
Normal file
75
src/vendor/github.com/kubernetes/helm/cmd/helm/get_hooks.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
||||
const getHooksHelp = `
|
||||
This command downloads hooks for a given release.
|
||||
|
||||
Hooks are formatted in YAML and separated by the YAML '---\n' separator.
|
||||
`
|
||||
|
||||
type getHooksCmd struct {
|
||||
release string
|
||||
out io.Writer
|
||||
client helm.Interface
|
||||
version int32
|
||||
}
|
||||
|
||||
func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
ghc := &getHooksCmd{
|
||||
out: out,
|
||||
client: client,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "hooks [flags] RELEASE_NAME",
|
||||
Short: "download all hooks for a named release",
|
||||
Long: getHooksHelp,
|
||||
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errReleaseRequired
|
||||
}
|
||||
ghc.release = args[0]
|
||||
ghc.client = ensureHelmClient(ghc.client)
|
||||
return ghc.run()
|
||||
},
|
||||
}
|
||||
cmd.Flags().Int32Var(&ghc.version, "revision", 0, "get the named release with revision")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (g *getHooksCmd) run() error {
|
||||
res, err := g.client.ReleaseContent(g.release, helm.ContentReleaseVersion(g.version))
|
||||
if err != nil {
|
||||
fmt.Fprintln(g.out, g.release)
|
||||
return prettyError(err)
|
||||
}
|
||||
|
||||
for _, hook := range res.Release.Hooks {
|
||||
fmt.Fprintf(g.out, "---\n# %s\n%s", hook.Name, hook.Manifest)
|
||||
}
|
||||
return nil
|
||||
}
|
47
src/vendor/github.com/kubernetes/helm/cmd/helm/get_hooks_test.go
generated
vendored
Normal file
47
src/vendor/github.com/kubernetes/helm/cmd/helm/get_hooks_test.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestGetHooks(t *testing.T) {
|
||||
tests := []releaseCase{
|
||||
{
|
||||
name: "get hooks with release",
|
||||
args: []string{"aeneas"},
|
||||
expected: helm.MockHookTemplate,
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}),
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"})},
|
||||
},
|
||||
{
|
||||
name: "get hooks without args",
|
||||
args: []string{},
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newGetHooksCmd(c, out)
|
||||
})
|
||||
}
|
75
src/vendor/github.com/kubernetes/helm/cmd/helm/get_manifest.go
generated
vendored
Normal file
75
src/vendor/github.com/kubernetes/helm/cmd/helm/get_manifest.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
||||
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.
|
||||
`
|
||||
|
||||
type getManifestCmd struct {
|
||||
release string
|
||||
out io.Writer
|
||||
client helm.Interface
|
||||
version int32
|
||||
}
|
||||
|
||||
func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
get := &getManifestCmd{
|
||||
out: out,
|
||||
client: client,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "manifest [flags] RELEASE_NAME",
|
||||
Short: "download the manifest for a named release",
|
||||
Long: getManifestHelp,
|
||||
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errReleaseRequired
|
||||
}
|
||||
get.release = args[0]
|
||||
get.client = ensureHelmClient(get.client)
|
||||
return get.run()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Int32Var(&get.version, "revision", 0, "get the named release with revision")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// getManifest implements 'helm get manifest'
|
||||
func (g *getManifestCmd) run() error {
|
||||
res, err := g.client.ReleaseContent(g.release, helm.ContentReleaseVersion(g.version))
|
||||
if err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
fmt.Fprintln(g.out, res.Release.Manifest)
|
||||
return nil
|
||||
}
|
47
src/vendor/github.com/kubernetes/helm/cmd/helm/get_manifest_test.go
generated
vendored
Normal file
47
src/vendor/github.com/kubernetes/helm/cmd/helm/get_manifest_test.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestGetManifest(t *testing.T) {
|
||||
tests := []releaseCase{
|
||||
{
|
||||
name: "get manifest with release",
|
||||
args: []string{"juno"},
|
||||
expected: helm.MockManifest,
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "juno"}),
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "juno"})},
|
||||
},
|
||||
{
|
||||
name: "get manifest without args",
|
||||
args: []string{},
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newGetManifestCmd(c, out)
|
||||
})
|
||||
}
|
48
src/vendor/github.com/kubernetes/helm/cmd/helm/get_test.go
generated
vendored
Normal file
48
src/vendor/github.com/kubernetes/helm/cmd/helm/get_test.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestGetCmd(t *testing.T) {
|
||||
tests := []releaseCase{
|
||||
{
|
||||
name: "get with a release",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"}),
|
||||
args: []string{"thomas-guide"},
|
||||
expected: "REVISION: 1\nRELEASED: (.*)\nCHART: foo-0.1.0-beta.1\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nHOOKS:\n---\n# pre-install-hook\n" + helm.MockHookTemplate + "\nMANIFEST:",
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
},
|
||||
{
|
||||
name: "get requires release name arg",
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
cmd := func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newGetCmd(c, out)
|
||||
}
|
||||
runReleaseCases(t, tests, cmd)
|
||||
}
|
89
src/vendor/github.com/kubernetes/helm/cmd/helm/get_values.go
generated
vendored
Normal file
89
src/vendor/github.com/kubernetes/helm/cmd/helm/get_values.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
||||
var getValuesHelp = `
|
||||
This command downloads a values file for a given release.
|
||||
`
|
||||
|
||||
type getValuesCmd struct {
|
||||
release string
|
||||
allValues bool
|
||||
out io.Writer
|
||||
client helm.Interface
|
||||
version int32
|
||||
}
|
||||
|
||||
func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
get := &getValuesCmd{
|
||||
out: out,
|
||||
client: client,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "values [flags] RELEASE_NAME",
|
||||
Short: "download the values file for a named release",
|
||||
Long: getValuesHelp,
|
||||
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errReleaseRequired
|
||||
}
|
||||
get.release = args[0]
|
||||
get.client = ensureHelmClient(get.client)
|
||||
return get.run()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Int32Var(&get.version, "revision", 0, "get the named release with revision")
|
||||
cmd.Flags().BoolVarP(&get.allValues, "all", "a", false, "dump all (computed) values")
|
||||
return cmd
|
||||
}
|
||||
|
||||
// getValues implements 'helm get values'
|
||||
func (g *getValuesCmd) run() error {
|
||||
res, err := g.client.ReleaseContent(g.release, helm.ContentReleaseVersion(g.version))
|
||||
if err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
|
||||
// If the user wants all values, compute the values and return.
|
||||
if g.allValues {
|
||||
cfg, err := chartutil.CoalesceValues(res.Release.Chart, res.Release.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfgStr, err := cfg.YAML()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(g.out, cfgStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Fprintln(g.out, res.Release.Config.Raw)
|
||||
return nil
|
||||
}
|
47
src/vendor/github.com/kubernetes/helm/cmd/helm/get_values_test.go
generated
vendored
Normal file
47
src/vendor/github.com/kubernetes/helm/cmd/helm/get_values_test.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestGetValuesCmd(t *testing.T) {
|
||||
tests := []releaseCase{
|
||||
{
|
||||
name: "get values with a release",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"}),
|
||||
args: []string{"thomas-guide"},
|
||||
expected: "name: \"value\"",
|
||||
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
},
|
||||
{
|
||||
name: "get values requires release name arg",
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
cmd := func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newGetValuesCmd(c, out)
|
||||
}
|
||||
runReleaseCases(t, tests, cmd)
|
||||
}
|
310
src/vendor/github.com/kubernetes/helm/cmd/helm/helm.go
generated
vendored
Normal file
310
src/vendor/github.com/kubernetes/helm/cmd/helm/helm.go
generated
vendored
Normal file
@ -0,0 +1,310 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 "k8s.io/helm/cmd/helm"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
|
||||
// Import to initialize client auth plugins.
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
helm_env "k8s.io/helm/pkg/helm/environment"
|
||||
"k8s.io/helm/pkg/helm/portforwarder"
|
||||
"k8s.io/helm/pkg/kube"
|
||||
"k8s.io/helm/pkg/tlsutil"
|
||||
)
|
||||
|
||||
var (
|
||||
tlsCaCertFile string // path to TLS CA certificate file
|
||||
tlsCertFile string // path to TLS certificate file
|
||||
tlsKeyFile string // path to TLS key file
|
||||
tlsVerify bool // enable TLS and verify remote certificates
|
||||
tlsEnable bool // enable TLS
|
||||
|
||||
tlsCaCertDefault = "$HELM_HOME/ca.pem"
|
||||
tlsCertDefault = "$HELM_HOME/cert.pem"
|
||||
tlsKeyDefault = "$HELM_HOME/key.pem"
|
||||
|
||||
tillerTunnel *kube.Tunnel
|
||||
settings helm_env.EnvSettings
|
||||
)
|
||||
|
||||
var globalUsage = `The Kubernetes package manager
|
||||
|
||||
To begin working with Helm, run the 'helm init' command:
|
||||
|
||||
$ helm init
|
||||
|
||||
This will install Tiller to your running Kubernetes cluster.
|
||||
It will also set up any necessary local configuration.
|
||||
|
||||
Common actions from this point include:
|
||||
|
||||
- helm search: search for charts
|
||||
- helm fetch: download a chart to your local directory to view
|
||||
- helm install: upload the chart to Kubernetes
|
||||
- helm list: list releases of charts
|
||||
|
||||
Environment:
|
||||
$HELM_HOME set an alternative location for Helm files. By default, these are stored in ~/.helm
|
||||
$HELM_HOST set an alternative Tiller host. The format is host:port
|
||||
$HELM_NO_PLUGINS disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.
|
||||
$TILLER_NAMESPACE set an alternative Tiller namespace (default "kube-system")
|
||||
$KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config")
|
||||
`
|
||||
|
||||
func newRootCmd(args []string) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "helm",
|
||||
Short: "The Helm package manager for Kubernetes.",
|
||||
Long: globalUsage,
|
||||
SilenceUsage: true,
|
||||
PersistentPreRun: func(*cobra.Command, []string) {
|
||||
tlsCaCertFile = os.ExpandEnv(tlsCaCertFile)
|
||||
tlsCertFile = os.ExpandEnv(tlsCertFile)
|
||||
tlsKeyFile = os.ExpandEnv(tlsKeyFile)
|
||||
},
|
||||
PersistentPostRun: func(*cobra.Command, []string) {
|
||||
teardown()
|
||||
},
|
||||
}
|
||||
flags := cmd.PersistentFlags()
|
||||
|
||||
settings.AddFlags(flags)
|
||||
|
||||
out := cmd.OutOrStdout()
|
||||
|
||||
cmd.AddCommand(
|
||||
// chart commands
|
||||
newCreateCmd(out),
|
||||
newDependencyCmd(out),
|
||||
newFetchCmd(out),
|
||||
newInspectCmd(out),
|
||||
newLintCmd(out),
|
||||
newPackageCmd(out),
|
||||
newRepoCmd(out),
|
||||
newSearchCmd(out),
|
||||
newServeCmd(out),
|
||||
newVerifyCmd(out),
|
||||
|
||||
// release commands
|
||||
addFlagsTLS(newDeleteCmd(nil, out)),
|
||||
addFlagsTLS(newGetCmd(nil, out)),
|
||||
addFlagsTLS(newHistoryCmd(nil, out)),
|
||||
addFlagsTLS(newInstallCmd(nil, out)),
|
||||
addFlagsTLS(newListCmd(nil, out)),
|
||||
addFlagsTLS(newRollbackCmd(nil, out)),
|
||||
addFlagsTLS(newStatusCmd(nil, out)),
|
||||
addFlagsTLS(newUpgradeCmd(nil, out)),
|
||||
|
||||
addFlagsTLS(newReleaseTestCmd(nil, out)),
|
||||
addFlagsTLS(newResetCmd(nil, out)),
|
||||
addFlagsTLS(newVersionCmd(nil, out)),
|
||||
|
||||
newCompletionCmd(out),
|
||||
newHomeCmd(out),
|
||||
newInitCmd(out),
|
||||
newPluginCmd(out),
|
||||
newTemplateCmd(out),
|
||||
|
||||
// Hidden documentation generator command: 'helm docs'
|
||||
newDocsCmd(out),
|
||||
|
||||
// Deprecated
|
||||
markDeprecated(newRepoUpdateCmd(out), "use 'helm repo update'\n"),
|
||||
)
|
||||
|
||||
flags.Parse(args)
|
||||
|
||||
// set defaults from environment
|
||||
settings.Init(flags)
|
||||
|
||||
// Find and add plugins
|
||||
loadPlugins(cmd, out)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Tell gRPC not to log to console.
|
||||
grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
|
||||
}
|
||||
|
||||
func main() {
|
||||
cmd := newRootCmd(os.Args[1:])
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func markDeprecated(cmd *cobra.Command, notice string) *cobra.Command {
|
||||
cmd.Deprecated = notice
|
||||
return cmd
|
||||
}
|
||||
|
||||
func setupConnection() error {
|
||||
if settings.TillerHost == "" {
|
||||
config, client, err := getKubeClient(settings.KubeContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tunnel, err := portforwarder.New(settings.TillerNamespace, client, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.TillerHost = fmt.Sprintf("127.0.0.1:%d", tunnel.Local)
|
||||
debug("Created tunnel using local port: '%d'\n", tunnel.Local)
|
||||
}
|
||||
|
||||
// Set up the gRPC config.
|
||||
debug("SERVER: %q\n", settings.TillerHost)
|
||||
|
||||
// Plugin support.
|
||||
return nil
|
||||
}
|
||||
|
||||
func teardown() {
|
||||
if tillerTunnel != nil {
|
||||
tillerTunnel.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func checkArgsLength(argsReceived int, requiredArgs ...string) error {
|
||||
expectedNum := len(requiredArgs)
|
||||
if argsReceived != expectedNum {
|
||||
arg := "arguments"
|
||||
if expectedNum == 1 {
|
||||
arg = "argument"
|
||||
}
|
||||
return fmt.Errorf("This command needs %v %s: %s", expectedNum, arg, strings.Join(requiredArgs, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// prettyError unwraps or rewrites certain errors to make them more user-friendly.
|
||||
func prettyError(err error) error {
|
||||
// Add this check can prevent the object creation if err is nil.
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
// If it's grpc's error, make it more user-friendly.
|
||||
if s, ok := status.FromError(err); ok {
|
||||
return fmt.Errorf(s.Message())
|
||||
}
|
||||
// Else return the original error.
|
||||
return err
|
||||
}
|
||||
|
||||
// configForContext creates a Kubernetes REST client configuration for a given kubeconfig context.
|
||||
func configForContext(context string) (*rest.Config, error) {
|
||||
config, err := kube.GetConfig(context).ClientConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get Kubernetes config for context %q: %s", context, err)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// getKubeClient creates a Kubernetes config and client for a given kubeconfig context.
|
||||
func getKubeClient(context string) (*rest.Config, kubernetes.Interface, error) {
|
||||
config, err := configForContext(context)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not get Kubernetes client: %s", err)
|
||||
}
|
||||
return config, client, nil
|
||||
}
|
||||
|
||||
// getInternalKubeClient creates a Kubernetes config and an "internal" client for a given kubeconfig context.
|
||||
//
|
||||
// Prefer the similar getKubeClient if you don't need to use such an internal client.
|
||||
func getInternalKubeClient(context string) (internalclientset.Interface, error) {
|
||||
config, err := configForContext(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := internalclientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get Kubernetes client: %s", err)
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// ensureHelmClient returns a new helm client impl. if h is not nil.
|
||||
func ensureHelmClient(h helm.Interface) helm.Interface {
|
||||
if h != nil {
|
||||
return h
|
||||
}
|
||||
return newClient()
|
||||
}
|
||||
|
||||
func newClient() helm.Interface {
|
||||
options := []helm.Option{helm.Host(settings.TillerHost), helm.ConnectTimeout(settings.TillerConnectionTimeout)}
|
||||
|
||||
if tlsVerify || tlsEnable {
|
||||
if tlsCaCertFile == "" {
|
||||
tlsCaCertFile = settings.Home.TLSCaCert()
|
||||
}
|
||||
if tlsCertFile == "" {
|
||||
tlsCertFile = settings.Home.TLSCert()
|
||||
}
|
||||
if tlsKeyFile == "" {
|
||||
tlsKeyFile = settings.Home.TLSKey()
|
||||
}
|
||||
debug("Key=%q, Cert=%q, CA=%q\n", tlsKeyFile, tlsCertFile, tlsCaCertFile)
|
||||
tlsopts := tlsutil.Options{KeyFile: tlsKeyFile, CertFile: tlsCertFile, InsecureSkipVerify: true}
|
||||
if tlsVerify {
|
||||
tlsopts.CaCertFile = tlsCaCertFile
|
||||
tlsopts.InsecureSkipVerify = false
|
||||
}
|
||||
tlscfg, err := tlsutil.ClientConfig(tlsopts)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
options = append(options, helm.WithTLS(tlscfg))
|
||||
}
|
||||
return helm.NewClient(options...)
|
||||
}
|
||||
|
||||
// addFlagsTLS adds the flags for supporting client side TLS to the
|
||||
// helm command (only those that invoke communicate to Tiller.)
|
||||
func addFlagsTLS(cmd *cobra.Command) *cobra.Command {
|
||||
|
||||
// add flags
|
||||
cmd.Flags().StringVar(&tlsCaCertFile, "tls-ca-cert", tlsCaCertDefault, "path to TLS CA certificate file")
|
||||
cmd.Flags().StringVar(&tlsCertFile, "tls-cert", tlsCertDefault, "path to TLS certificate file")
|
||||
cmd.Flags().StringVar(&tlsKeyFile, "tls-key", tlsKeyDefault, "path to TLS key file")
|
||||
cmd.Flags().BoolVar(&tlsVerify, "tls-verify", false, "enable TLS for request and verify remote")
|
||||
cmd.Flags().BoolVar(&tlsEnable, "tls", false, "enable TLS for request")
|
||||
return cmd
|
||||
}
|
238
src/vendor/github.com/kubernetes/helm/cmd/helm/helm_test.go
generated
vendored
Normal file
238
src/vendor/github.com/kubernetes/helm/cmd/helm/helm_test.go
generated
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
// releaseCmd is a command that works with a FakeClient
|
||||
type releaseCmd func(c *helm.FakeClient, out io.Writer) *cobra.Command
|
||||
|
||||
// runReleaseCases runs a set of release cases through the given releaseCmd.
|
||||
func runReleaseCases(t *testing.T, tests []releaseCase, rcmd releaseCmd) {
|
||||
var buf bytes.Buffer
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &helm.FakeClient{Rels: tt.rels}
|
||||
cmd := rcmd(c, &buf)
|
||||
cmd.ParseFlags(tt.flags)
|
||||
err := cmd.RunE(cmd, tt.args)
|
||||
if (err != nil) != tt.err {
|
||||
t.Errorf("expected error, got '%v'", err)
|
||||
}
|
||||
re := regexp.MustCompile(tt.expected)
|
||||
if !re.Match(buf.Bytes()) {
|
||||
t.Errorf("expected\n%q\ngot\n%q", tt.expected, buf.String())
|
||||
}
|
||||
buf.Reset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// releaseCase describes a test case that works with releases.
|
||||
type releaseCase struct {
|
||||
name string
|
||||
args []string
|
||||
flags []string
|
||||
// expected is the string to be matched. This supports regular expressions.
|
||||
expected string
|
||||
err bool
|
||||
resp *release.Release
|
||||
// Rels are the available releases at the start of the test.
|
||||
rels []*release.Release
|
||||
}
|
||||
|
||||
// tempHelmHome sets up a Helm Home in a temp dir.
|
||||
//
|
||||
// This does not clean up the directory. You must do that yourself.
|
||||
// You must also set helmHome yourself.
|
||||
func tempHelmHome(t *testing.T) (helmpath.Home, error) {
|
||||
oldhome := settings.Home
|
||||
dir, err := ioutil.TempDir("", "helm_home-")
|
||||
if err != nil {
|
||||
return helmpath.Home("n/"), err
|
||||
}
|
||||
|
||||
settings.Home = helmpath.Home(dir)
|
||||
if err := ensureTestHome(settings.Home, t); err != nil {
|
||||
return helmpath.Home("n/"), err
|
||||
}
|
||||
settings.Home = oldhome
|
||||
return helmpath.Home(dir), nil
|
||||
}
|
||||
|
||||
// ensureTestHome creates a home directory like ensureHome, but without remote references.
|
||||
//
|
||||
// t is used only for logging.
|
||||
func ensureTestHome(home helmpath.Home, t *testing.T) error {
|
||||
configDirectories := []string{home.String(), home.Repository(), home.Cache(), home.LocalRepository(), home.Plugins(), home.Starters()}
|
||||
for _, p := range configDirectories {
|
||||
if fi, err := os.Stat(p); err != nil {
|
||||
if err := os.MkdirAll(p, 0755); err != nil {
|
||||
return fmt.Errorf("Could not create %s: %s", p, err)
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
return fmt.Errorf("%s must be a directory", p)
|
||||
}
|
||||
}
|
||||
|
||||
repoFile := home.RepositoryFile()
|
||||
if fi, err := os.Stat(repoFile); err != nil {
|
||||
rf := repo.NewRepoFile()
|
||||
rf.Add(&repo.Entry{
|
||||
Name: "charts",
|
||||
URL: "http://example.com/foo",
|
||||
Cache: "charts-index.yaml",
|
||||
}, &repo.Entry{
|
||||
Name: "local",
|
||||
URL: "http://localhost.com:7743/foo",
|
||||
Cache: "local-index.yaml",
|
||||
})
|
||||
if err := rf.WriteFile(repoFile, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if fi.IsDir() {
|
||||
return fmt.Errorf("%s must be a file, not a directory", repoFile)
|
||||
}
|
||||
if r, err := repo.LoadRepositoriesFile(repoFile); err == repo.ErrRepoOutOfDate {
|
||||
t.Log("Updating repository file format...")
|
||||
if err := r.WriteFile(repoFile, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
localRepoIndexFile := home.LocalRepository(localRepositoryIndexFile)
|
||||
if fi, err := os.Stat(localRepoIndexFile); err != nil {
|
||||
i := repo.NewIndexFile()
|
||||
if err := i.WriteFile(localRepoIndexFile, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//TODO: take this out and replace with helm update functionality
|
||||
os.Symlink(localRepoIndexFile, home.CacheIndex("local"))
|
||||
} else if fi.IsDir() {
|
||||
return fmt.Errorf("%s must be a file, not a directory", localRepoIndexFile)
|
||||
}
|
||||
|
||||
t.Logf("$HELM_HOME has been configured at %s.\n", settings.Home.String())
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func TestRootCmd(t *testing.T) {
|
||||
cleanup := resetEnv()
|
||||
defer cleanup()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
envars map[string]string
|
||||
home string
|
||||
}{
|
||||
{
|
||||
name: "defaults",
|
||||
args: []string{"home"},
|
||||
home: filepath.Join(os.Getenv("HOME"), "/.helm"),
|
||||
},
|
||||
{
|
||||
name: "with --home set",
|
||||
args: []string{"--home", "/foo"},
|
||||
home: "/foo",
|
||||
},
|
||||
{
|
||||
name: "subcommands with --home set",
|
||||
args: []string{"home", "--home", "/foo"},
|
||||
home: "/foo",
|
||||
},
|
||||
{
|
||||
name: "with $HELM_HOME set",
|
||||
args: []string{"home"},
|
||||
envars: map[string]string{"HELM_HOME": "/bar"},
|
||||
home: "/bar",
|
||||
},
|
||||
{
|
||||
name: "subcommands with $HELM_HOME set",
|
||||
args: []string{"home"},
|
||||
envars: map[string]string{"HELM_HOME": "/bar"},
|
||||
home: "/bar",
|
||||
},
|
||||
{
|
||||
name: "with $HELM_HOME and --home set",
|
||||
args: []string{"home", "--home", "/foo"},
|
||||
envars: map[string]string{"HELM_HOME": "/bar"},
|
||||
home: "/foo",
|
||||
},
|
||||
}
|
||||
|
||||
// ensure not set locally
|
||||
os.Unsetenv("HELM_HOME")
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer os.Unsetenv("HELM_HOME")
|
||||
|
||||
for k, v := range tt.envars {
|
||||
os.Setenv(k, v)
|
||||
}
|
||||
|
||||
cmd := newRootCmd(tt.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetArgs(tt.args)
|
||||
cmd.Run = func(*cobra.Command, []string) {}
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if settings.Home.String() != tt.home {
|
||||
t.Errorf("expected home %q, got %q", tt.home, settings.Home)
|
||||
}
|
||||
homeFlag := cmd.Flag("home").Value.String()
|
||||
homeFlag = os.ExpandEnv(homeFlag)
|
||||
if homeFlag != tt.home {
|
||||
t.Errorf("expected home %q, got %q", tt.home, homeFlag)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func resetEnv() func() {
|
||||
origSettings := settings
|
||||
origEnv := os.Environ()
|
||||
return func() {
|
||||
settings = origSettings
|
||||
for _, pair := range origEnv {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
os.Setenv(kv[0], kv[1])
|
||||
}
|
||||
}
|
||||
}
|
172
src/vendor/github.com/kubernetes/helm/cmd/helm/history.go
generated
vendored
Normal file
172
src/vendor/github.com/kubernetes/helm/cmd/helm/history.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/chart"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
"k8s.io/helm/pkg/timeconv"
|
||||
)
|
||||
|
||||
type releaseInfo struct {
|
||||
Revision int32 `json:"revision"`
|
||||
Updated string `json:"updated"`
|
||||
Status string `json:"status"`
|
||||
Chart string `json:"chart"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type releaseHistory []releaseInfo
|
||||
|
||||
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 --max=4
|
||||
REVISION UPDATED STATUS CHART DESCRIPTION
|
||||
1 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Initial install
|
||||
2 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Upgraded successfully
|
||||
3 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Rolled back to 2
|
||||
4 Mon Oct 3 10:15:13 2016 DEPLOYED alpine-0.1.0 Upgraded successfully
|
||||
`
|
||||
|
||||
type historyCmd struct {
|
||||
max int32
|
||||
rls string
|
||||
out io.Writer
|
||||
helmc helm.Interface
|
||||
colWidth uint
|
||||
outputFormat string
|
||||
}
|
||||
|
||||
func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command {
|
||||
his := &historyCmd{out: w, helmc: c}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "history [flags] RELEASE_NAME",
|
||||
Long: historyHelp,
|
||||
Short: "fetch release history",
|
||||
Aliases: []string{"hist"},
|
||||
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
switch {
|
||||
case len(args) == 0:
|
||||
return errReleaseRequired
|
||||
case his.helmc == nil:
|
||||
his.helmc = newClient()
|
||||
}
|
||||
his.rls = args[0]
|
||||
return his.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.Int32Var(&his.max, "max", 256, "maximum number of revision to include in history")
|
||||
f.UintVar(&his.colWidth, "col-width", 60, "specifies the max column width of output")
|
||||
f.StringVarP(&his.outputFormat, "output", "o", "table", "prints the output in the specified format (json|table|yaml)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (cmd *historyCmd) run() error {
|
||||
r, err := cmd.helmc.ReleaseHistory(cmd.rls, helm.WithMaxHistory(cmd.max))
|
||||
if err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
if len(r.Releases) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
releaseHistory := getReleaseHistory(r.Releases)
|
||||
|
||||
var history []byte
|
||||
var formattingError error
|
||||
|
||||
switch cmd.outputFormat {
|
||||
case "yaml":
|
||||
history, formattingError = yaml.Marshal(releaseHistory)
|
||||
case "json":
|
||||
history, formattingError = json.Marshal(releaseHistory)
|
||||
case "table":
|
||||
history = formatAsTable(releaseHistory, cmd.colWidth)
|
||||
default:
|
||||
return fmt.Errorf("unknown output format %q", cmd.outputFormat)
|
||||
}
|
||||
|
||||
if formattingError != nil {
|
||||
return prettyError(formattingError)
|
||||
}
|
||||
|
||||
fmt.Fprintln(cmd.out, string(history))
|
||||
return nil
|
||||
}
|
||||
|
||||
func getReleaseHistory(rls []*release.Release) (history releaseHistory) {
|
||||
for i := len(rls) - 1; i >= 0; i-- {
|
||||
r := rls[i]
|
||||
c := formatChartname(r.Chart)
|
||||
t := timeconv.String(r.Info.LastDeployed)
|
||||
s := r.Info.Status.Code.String()
|
||||
v := r.Version
|
||||
d := r.Info.Description
|
||||
|
||||
rInfo := releaseInfo{
|
||||
Revision: v,
|
||||
Updated: t,
|
||||
Status: s,
|
||||
Chart: c,
|
||||
Description: d,
|
||||
}
|
||||
history = append(history, rInfo)
|
||||
}
|
||||
|
||||
return history
|
||||
}
|
||||
|
||||
func formatAsTable(releases releaseHistory, colWidth uint) []byte {
|
||||
tbl := uitable.New()
|
||||
|
||||
tbl.MaxColWidth = colWidth
|
||||
tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "DESCRIPTION")
|
||||
for i := 0; i <= len(releases)-1; i++ {
|
||||
r := releases[i]
|
||||
tbl.AddRow(r.Revision, r.Updated, r.Status, r.Chart, r.Description)
|
||||
}
|
||||
return tbl.Bytes()
|
||||
}
|
||||
|
||||
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/kubernetes/helm/issues/1347
|
||||
return "MISSING"
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", c.Metadata.Name, c.Metadata.Version)
|
||||
}
|
85
src/vendor/github.com/kubernetes/helm/cmd/helm/history_test.go
generated
vendored
Normal file
85
src/vendor/github.com/kubernetes/helm/cmd/helm/history_test.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
rpb "k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestHistoryCmd(t *testing.T) {
|
||||
mk := func(name string, vers int32, code rpb.Status_Code) *rpb.Release {
|
||||
return helm.ReleaseMock(&helm.MockReleaseOptions{
|
||||
Name: name,
|
||||
Version: vers,
|
||||
StatusCode: code,
|
||||
})
|
||||
}
|
||||
|
||||
tests := []releaseCase{
|
||||
{
|
||||
name: "get history for release",
|
||||
args: []string{"angry-bird"},
|
||||
rels: []*rpb.Release{
|
||||
mk("angry-bird", 4, rpb.Status_DEPLOYED),
|
||||
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
|
||||
mk("angry-bird", 2, rpb.Status_SUPERSEDED),
|
||||
mk("angry-bird", 1, rpb.Status_SUPERSEDED),
|
||||
},
|
||||
expected: "REVISION\tUPDATED \tSTATUS \tCHART \tDESCRIPTION \n1 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n2 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\tRelease mock\n",
|
||||
},
|
||||
{
|
||||
name: "get history with max limit set",
|
||||
args: []string{"angry-bird"},
|
||||
flags: []string{"--max", "2"},
|
||||
rels: []*rpb.Release{
|
||||
mk("angry-bird", 4, rpb.Status_DEPLOYED),
|
||||
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
|
||||
},
|
||||
expected: "REVISION\tUPDATED \tSTATUS \tCHART \tDESCRIPTION \n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\tRelease mock\n",
|
||||
},
|
||||
{
|
||||
name: "get history with yaml output format",
|
||||
args: []string{"angry-bird"},
|
||||
flags: []string{"--output", "yaml"},
|
||||
rels: []*rpb.Release{
|
||||
mk("angry-bird", 4, rpb.Status_DEPLOYED),
|
||||
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
|
||||
},
|
||||
expected: "- chart: foo-0.1.0-beta.1\n description: Release mock\n revision: 3\n status: SUPERSEDED\n updated: (.*)\n- chart: foo-0.1.0-beta.1\n description: Release mock\n revision: 4\n status: DEPLOYED\n updated: (.*)\n\n",
|
||||
},
|
||||
{
|
||||
name: "get history with json output format",
|
||||
args: []string{"angry-bird"},
|
||||
flags: []string{"--output", "json"},
|
||||
rels: []*rpb.Release{
|
||||
mk("angry-bird", 4, rpb.Status_DEPLOYED),
|
||||
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
|
||||
},
|
||||
expected: `[{"revision":3,"updated":".*","status":"SUPERSEDED","chart":"foo\-0.1.0-beta.1","description":"Release mock"},{"revision":4,"updated":".*","status":"DEPLOYED","chart":"foo\-0.1.0-beta.1","description":"Release mock"}]\n`,
|
||||
},
|
||||
}
|
||||
|
||||
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newHistoryCmd(c, out)
|
||||
})
|
||||
}
|
51
src/vendor/github.com/kubernetes/helm/cmd/helm/home.go
generated
vendored
Normal file
51
src/vendor/github.com/kubernetes/helm/cmd/helm/home.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
)
|
||||
|
||||
var longHomeHelp = `
|
||||
This command displays the location of HELM_HOME. This is where
|
||||
any helm configuration files live.
|
||||
`
|
||||
|
||||
func newHomeCmd(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "home",
|
||||
Short: "displays the location of HELM_HOME",
|
||||
Long: longHomeHelp,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
h := settings.Home
|
||||
fmt.Fprintln(out, h)
|
||||
if settings.Debug {
|
||||
fmt.Fprintf(out, "Repository: %s\n", h.Repository())
|
||||
fmt.Fprintf(out, "RepositoryFile: %s\n", h.RepositoryFile())
|
||||
fmt.Fprintf(out, "Cache: %s\n", h.Cache())
|
||||
fmt.Fprintf(out, "Stable CacheIndex: %s\n", h.CacheIndex("stable"))
|
||||
fmt.Fprintf(out, "Starters: %s\n", h.Starters())
|
||||
fmt.Fprintf(out, "LocalRepository: %s\n", h.LocalRepository())
|
||||
fmt.Fprintf(out, "Plugins: %s\n", h.Plugins())
|
||||
}
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
493
src/vendor/github.com/kubernetes/helm/cmd/helm/init.go
generated
vendored
Normal file
493
src/vendor/github.com/kubernetes/helm/cmd/helm/init.go
generated
vendored
Normal file
@ -0,0 +1,493 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/helm/cmd/helm/installer"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/helm/portforwarder"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
const initDesc = `
|
||||
This command installs Tiller (the Helm server-side component) onto your
|
||||
Kubernetes Cluster and sets up local configuration in $HELM_HOME (default ~/.helm/).
|
||||
|
||||
As with the rest of the Helm commands, 'helm init' discovers Kubernetes clusters
|
||||
by reading $KUBECONFIG (default '~/.kube/config') and using the default context.
|
||||
|
||||
To set up just a local environment, use '--client-only'. That will configure
|
||||
$HELM_HOME, but not attempt to connect to a Kubernetes cluster and install the Tiller
|
||||
deployment.
|
||||
|
||||
When installing Tiller, 'helm init' will attempt to install the latest released
|
||||
version. You can specify an alternative image with '--tiller-image'. For those
|
||||
frequently working on the latest code, the flag '--canary-image' will install
|
||||
the latest pre-release version of Tiller (e.g. the HEAD commit in the GitHub
|
||||
repository on the master branch).
|
||||
|
||||
To dump a manifest containing the Tiller deployment YAML, combine the
|
||||
'--dry-run' and '--debug' flags.
|
||||
`
|
||||
|
||||
const (
|
||||
stableRepository = "stable"
|
||||
localRepository = "local"
|
||||
localRepositoryIndexFile = "index.yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
stableRepositoryURL = "https://kubernetes-charts.storage.googleapis.com"
|
||||
// This is the IPv4 loopback, not localhost, because we have to force IPv4
|
||||
// for Dockerized Helm: https://github.com/kubernetes/helm/issues/1410
|
||||
localRepositoryURL = "http://127.0.0.1:8879/charts"
|
||||
)
|
||||
|
||||
type initCmd struct {
|
||||
image string
|
||||
clientOnly bool
|
||||
canary bool
|
||||
upgrade bool
|
||||
namespace string
|
||||
dryRun bool
|
||||
forceUpgrade bool
|
||||
skipRefresh bool
|
||||
out io.Writer
|
||||
client helm.Interface
|
||||
home helmpath.Home
|
||||
opts installer.Options
|
||||
kubeClient kubernetes.Interface
|
||||
serviceAccount string
|
||||
maxHistory int
|
||||
replicas int
|
||||
wait bool
|
||||
}
|
||||
|
||||
func newInitCmd(out io.Writer) *cobra.Command {
|
||||
i := &initCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "initialize Helm on both client and server",
|
||||
Long: initDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 0 {
|
||||
return errors.New("This command does not accept arguments")
|
||||
}
|
||||
i.namespace = settings.TillerNamespace
|
||||
i.home = settings.Home
|
||||
i.client = ensureHelmClient(i.client)
|
||||
|
||||
return i.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.StringVarP(&i.image, "tiller-image", "i", "", "override Tiller image")
|
||||
f.BoolVar(&i.canary, "canary-image", false, "use the canary Tiller image")
|
||||
f.BoolVar(&i.upgrade, "upgrade", false, "upgrade if Tiller is already installed")
|
||||
f.BoolVar(&i.forceUpgrade, "force-upgrade", false, "force upgrade of Tiller to the current helm version")
|
||||
f.BoolVarP(&i.clientOnly, "client-only", "c", false, "if set does not install Tiller")
|
||||
f.BoolVar(&i.dryRun, "dry-run", false, "do not install local or remote")
|
||||
f.BoolVar(&i.skipRefresh, "skip-refresh", false, "do not refresh (download) the local repository cache")
|
||||
f.BoolVar(&i.wait, "wait", false, "block until Tiller is running and ready to receive requests")
|
||||
|
||||
f.BoolVar(&tlsEnable, "tiller-tls", false, "install Tiller with TLS enabled")
|
||||
f.BoolVar(&tlsVerify, "tiller-tls-verify", false, "install Tiller with TLS enabled and to verify remote certificates")
|
||||
f.StringVar(&tlsKeyFile, "tiller-tls-key", "", "path to TLS key file to install with Tiller")
|
||||
f.StringVar(&tlsCertFile, "tiller-tls-cert", "", "path to TLS certificate file to install with Tiller")
|
||||
f.StringVar(&tlsCaCertFile, "tls-ca-cert", "", "path to CA root certificate")
|
||||
|
||||
f.StringVar(&stableRepositoryURL, "stable-repo-url", stableRepositoryURL, "URL for stable repository")
|
||||
f.StringVar(&localRepositoryURL, "local-repo-url", localRepositoryURL, "URL for local repository")
|
||||
|
||||
f.BoolVar(&i.opts.EnableHostNetwork, "net-host", false, "install Tiller with net=host")
|
||||
f.StringVar(&i.serviceAccount, "service-account", "", "name of service account")
|
||||
f.IntVar(&i.maxHistory, "history-max", 0, "limit the maximum number of revisions saved per release. Use 0 for no limit.")
|
||||
f.IntVar(&i.replicas, "replicas", 1, "amount of tiller instances to run on the cluster")
|
||||
|
||||
f.StringVar(&i.opts.NodeSelectors, "node-selectors", "", "labels to specify the node on which Tiller is installed (app=tiller,helm=rocks)")
|
||||
f.VarP(&i.opts.Output, "output", "o", "skip installation and output Tiller's manifest in specified format (json or yaml)")
|
||||
f.StringArrayVar(&i.opts.Values, "override", []string{}, "override values for the Tiller Deployment manifest (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// tlsOptions sanitizes the tls flags as well as checks for the existence of required
|
||||
// tls files indicated by those flags, if any.
|
||||
func (i *initCmd) tlsOptions() error {
|
||||
i.opts.EnableTLS = tlsEnable || tlsVerify
|
||||
i.opts.VerifyTLS = tlsVerify
|
||||
|
||||
if i.opts.EnableTLS {
|
||||
missing := func(file string) bool {
|
||||
_, err := os.Stat(file)
|
||||
return os.IsNotExist(err)
|
||||
}
|
||||
if i.opts.TLSKeyFile = tlsKeyFile; i.opts.TLSKeyFile == "" || missing(i.opts.TLSKeyFile) {
|
||||
return errors.New("missing required TLS key file")
|
||||
}
|
||||
if i.opts.TLSCertFile = tlsCertFile; i.opts.TLSCertFile == "" || missing(i.opts.TLSCertFile) {
|
||||
return errors.New("missing required TLS certificate file")
|
||||
}
|
||||
if i.opts.VerifyTLS {
|
||||
if i.opts.TLSCaCertFile = tlsCaCertFile; i.opts.TLSCaCertFile == "" || missing(i.opts.TLSCaCertFile) {
|
||||
return errors.New("missing required TLS CA file")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// run initializes local config and installs Tiller to Kubernetes cluster.
|
||||
func (i *initCmd) run() error {
|
||||
if err := i.tlsOptions(); err != nil {
|
||||
return err
|
||||
}
|
||||
i.opts.Namespace = i.namespace
|
||||
i.opts.UseCanary = i.canary
|
||||
i.opts.ImageSpec = i.image
|
||||
i.opts.ForceUpgrade = i.forceUpgrade
|
||||
i.opts.ServiceAccount = i.serviceAccount
|
||||
i.opts.MaxHistory = i.maxHistory
|
||||
i.opts.Replicas = i.replicas
|
||||
|
||||
writeYAMLManifest := func(apiVersion, kind, body string, first, last bool) error {
|
||||
w := i.out
|
||||
if !first {
|
||||
// YAML starting document boundary marker
|
||||
if _, err := fmt.Fprintln(w, "---"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := fmt.Fprintln(w, "apiVersion:", apiVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprintln(w, "kind:", kind); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fmt.Fprint(w, body); err != nil {
|
||||
return err
|
||||
}
|
||||
if !last {
|
||||
return nil
|
||||
}
|
||||
// YAML ending document boundary marker
|
||||
_, err := fmt.Fprintln(w, "...")
|
||||
return err
|
||||
}
|
||||
if len(i.opts.Output) > 0 {
|
||||
var body string
|
||||
var err error
|
||||
const tm = `{"apiVersion":"extensions/v1beta1","kind":"Deployment",`
|
||||
if body, err = installer.DeploymentManifest(&i.opts); err != nil {
|
||||
return err
|
||||
}
|
||||
switch i.opts.Output.String() {
|
||||
case "json":
|
||||
var out bytes.Buffer
|
||||
jsonb, err := yaml.ToJSON([]byte(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := bytes.NewBuffer(make([]byte, 0, len(tm)+len(jsonb)-1))
|
||||
buf.WriteString(tm)
|
||||
// Drop the opening object delimiter ('{').
|
||||
buf.Write(jsonb[1:])
|
||||
if err := json.Indent(&out, buf.Bytes(), "", " "); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = i.out.Write(out.Bytes()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
case "yaml":
|
||||
if err := writeYAMLManifest("extensions/v1beta1", "Deployment", body, true, false); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unknown output format: %q", i.opts.Output)
|
||||
}
|
||||
}
|
||||
if settings.Debug {
|
||||
|
||||
var body string
|
||||
var err error
|
||||
|
||||
// write Deployment manifest
|
||||
if body, err = installer.DeploymentManifest(&i.opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeYAMLManifest("extensions/v1beta1", "Deployment", body, true, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write Service manifest
|
||||
if body, err = installer.ServiceManifest(i.namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeYAMLManifest("v1", "Service", body, false, !i.opts.EnableTLS); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write Secret manifest
|
||||
if i.opts.EnableTLS {
|
||||
if body, err = installer.SecretManifest(&i.opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeYAMLManifest("v1", "Secret", body, false, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i.dryRun {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := ensureDirectories(i.home, i.out); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureDefaultRepos(i.home, i.out, i.skipRefresh); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureRepoFileFormat(i.home.RepositoryFile(), i.out); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(i.out, "$HELM_HOME has been configured at %s.\n", settings.Home)
|
||||
|
||||
if !i.clientOnly {
|
||||
if i.kubeClient == nil {
|
||||
_, c, err := getKubeClient(settings.KubeContext)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get kubernetes client: %s", err)
|
||||
}
|
||||
i.kubeClient = c
|
||||
}
|
||||
if err := installer.Install(i.kubeClient, &i.opts); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return fmt.Errorf("error installing: %s", err)
|
||||
}
|
||||
if i.upgrade {
|
||||
if err := installer.Upgrade(i.kubeClient, &i.opts); err != nil {
|
||||
return fmt.Errorf("error when upgrading: %s", err)
|
||||
}
|
||||
if err := i.ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been upgraded to the current version.")
|
||||
} else {
|
||||
fmt.Fprintln(i.out, "Warning: Tiller is already installed in the cluster.\n"+
|
||||
"(Use --client-only to suppress this message, or --upgrade to upgrade Tiller to the current version.)")
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been installed into your Kubernetes Cluster.\n\n"+
|
||||
"Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.\n"+
|
||||
"For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation")
|
||||
}
|
||||
if err := i.ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(i.out, "Not installing Tiller due to 'client-only' flag having been set")
|
||||
}
|
||||
|
||||
fmt.Fprintln(i.out, "Happy Helming!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *initCmd) ping() error {
|
||||
if i.wait {
|
||||
_, kubeClient, err := getKubeClient(settings.KubeContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !watchTillerUntilReady(settings.TillerNamespace, kubeClient, settings.TillerConnectionTimeout) {
|
||||
return fmt.Errorf("tiller was not found. polling deadline exceeded")
|
||||
}
|
||||
|
||||
// establish a connection to Tiller now that we've effectively guaranteed it's available
|
||||
if err := setupConnection(); err != nil {
|
||||
return err
|
||||
}
|
||||
i.client = newClient()
|
||||
if err := i.client.PingTiller(); err != nil {
|
||||
return fmt.Errorf("could not ping Tiller: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ensureDirectories checks to see if $HELM_HOME exists.
|
||||
//
|
||||
// If $HELM_HOME does not exist, this function will create it.
|
||||
func ensureDirectories(home helmpath.Home, out io.Writer) error {
|
||||
configDirectories := []string{
|
||||
home.String(),
|
||||
home.Repository(),
|
||||
home.Cache(),
|
||||
home.LocalRepository(),
|
||||
home.Plugins(),
|
||||
home.Starters(),
|
||||
home.Archive(),
|
||||
}
|
||||
for _, p := range configDirectories {
|
||||
if fi, err := os.Stat(p); err != nil {
|
||||
fmt.Fprintf(out, "Creating %s \n", p)
|
||||
if err := os.MkdirAll(p, 0755); err != nil {
|
||||
return fmt.Errorf("Could not create %s: %s", p, err)
|
||||
}
|
||||
} else if !fi.IsDir() {
|
||||
return fmt.Errorf("%s must be a directory", p)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) error {
|
||||
repoFile := home.RepositoryFile()
|
||||
if fi, err := os.Stat(repoFile); err != nil {
|
||||
fmt.Fprintf(out, "Creating %s \n", repoFile)
|
||||
f := repo.NewRepoFile()
|
||||
sr, err := initStableRepo(home.CacheIndex(stableRepository), out, skipRefresh, home)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lr, err := initLocalRepo(home.LocalRepository(localRepositoryIndexFile), home.CacheIndex("local"), out, home)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Add(sr)
|
||||
f.Add(lr)
|
||||
if err := f.WriteFile(repoFile, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if fi.IsDir() {
|
||||
return fmt.Errorf("%s must be a file, not a directory", repoFile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initStableRepo(cacheFile string, out io.Writer, skipRefresh bool, home helmpath.Home) (*repo.Entry, error) {
|
||||
fmt.Fprintf(out, "Adding %s repo with URL: %s \n", stableRepository, stableRepositoryURL)
|
||||
c := repo.Entry{
|
||||
Name: stableRepository,
|
||||
URL: stableRepositoryURL,
|
||||
Cache: cacheFile,
|
||||
}
|
||||
r, err := repo.NewChartRepository(&c, getter.All(settings))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if skipRefresh {
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// In this case, the cacheFile is always absolute. So passing empty string
|
||||
// is safe.
|
||||
if err := r.DownloadIndexFile(""); err != nil {
|
||||
return nil, fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", stableRepositoryURL, err.Error())
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func initLocalRepo(indexFile, cacheFile string, out io.Writer, home helmpath.Home) (*repo.Entry, error) {
|
||||
if fi, err := os.Stat(indexFile); err != nil {
|
||||
fmt.Fprintf(out, "Adding %s repo with URL: %s \n", localRepository, localRepositoryURL)
|
||||
i := repo.NewIndexFile()
|
||||
if err := i.WriteFile(indexFile, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//TODO: take this out and replace with helm update functionality
|
||||
if err := createLink(indexFile, cacheFile, home); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if fi.IsDir() {
|
||||
return nil, fmt.Errorf("%s must be a file, not a directory", indexFile)
|
||||
}
|
||||
|
||||
return &repo.Entry{
|
||||
Name: localRepository,
|
||||
URL: localRepositoryURL,
|
||||
Cache: cacheFile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ensureRepoFileFormat(file string, out io.Writer) error {
|
||||
r, err := repo.LoadRepositoriesFile(file)
|
||||
if err == repo.ErrRepoOutOfDate {
|
||||
fmt.Fprintln(out, "Updating repository file format...")
|
||||
if err := r.WriteFile(file, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// watchTillerUntilReady waits for the tiller pod to become available. This is useful in situations where we
|
||||
// want to wait before we call New().
|
||||
//
|
||||
// Returns true if it exists. If the timeout was reached and it could not find the pod, it returns false.
|
||||
func watchTillerUntilReady(namespace string, client kubernetes.Interface, timeout int64) bool {
|
||||
deadlinePollingChan := time.NewTimer(time.Duration(timeout) * time.Second).C
|
||||
checkTillerPodTicker := time.NewTicker(500 * time.Millisecond)
|
||||
doneChan := make(chan bool)
|
||||
|
||||
defer checkTillerPodTicker.Stop()
|
||||
|
||||
go func() {
|
||||
for range checkTillerPodTicker.C {
|
||||
_, err := portforwarder.GetTillerPodName(client.CoreV1(), namespace)
|
||||
if err == nil {
|
||||
doneChan <- true
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-deadlinePollingChan:
|
||||
return false
|
||||
case <-doneChan:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
357
src/vendor/github.com/kubernetes/helm/cmd/helm/init_test.go
generated
vendored
Normal file
357
src/vendor/github.com/kubernetes/helm/cmd/helm/init_test.go
generated
vendored
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
testcore "k8s.io/client-go/testing"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"k8s.io/helm/cmd/helm/installer"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
)
|
||||
|
||||
func TestInitCmd(t *testing.T) {
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(home)
|
||||
|
||||
var buf bytes.Buffer
|
||||
fc := fake.NewSimpleClientset()
|
||||
cmd := &initCmd{
|
||||
out: &buf,
|
||||
home: helmpath.Home(home),
|
||||
kubeClient: fc,
|
||||
namespace: v1.NamespaceDefault,
|
||||
}
|
||||
if err := cmd.run(); err != nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
actions := fc.Actions()
|
||||
if len(actions) != 2 {
|
||||
t.Errorf("Expected 2 actions, got %d", len(actions))
|
||||
}
|
||||
if !actions[0].Matches("create", "deployments") {
|
||||
t.Errorf("unexpected action: %v, expected create deployment", actions[0])
|
||||
}
|
||||
if !actions[1].Matches("create", "services") {
|
||||
t.Errorf("unexpected action: %v, expected create service", actions[1])
|
||||
}
|
||||
expected := "Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster."
|
||||
if !strings.Contains(buf.String(), expected) {
|
||||
t.Errorf("expected %q, got %q", expected, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCmd_exists(t *testing.T) {
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(home)
|
||||
|
||||
var buf bytes.Buffer
|
||||
fc := fake.NewSimpleClientset(&v1beta1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
Name: "tiller-deploy",
|
||||
},
|
||||
})
|
||||
fc.PrependReactor("*", "*", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, apierrors.NewAlreadyExists(v1.Resource("deployments"), "1")
|
||||
})
|
||||
cmd := &initCmd{
|
||||
out: &buf,
|
||||
home: helmpath.Home(home),
|
||||
kubeClient: fc,
|
||||
namespace: v1.NamespaceDefault,
|
||||
}
|
||||
if err := cmd.run(); err != nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
expected := "Warning: Tiller is already installed in the cluster.\n" +
|
||||
"(Use --client-only to suppress this message, or --upgrade to upgrade Tiller to the current version.)"
|
||||
if !strings.Contains(buf.String(), expected) {
|
||||
t.Errorf("expected %q, got %q", expected, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCmd_clientOnly(t *testing.T) {
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(home)
|
||||
|
||||
var buf bytes.Buffer
|
||||
fc := fake.NewSimpleClientset()
|
||||
cmd := &initCmd{
|
||||
out: &buf,
|
||||
home: helmpath.Home(home),
|
||||
kubeClient: fc,
|
||||
clientOnly: true,
|
||||
namespace: v1.NamespaceDefault,
|
||||
}
|
||||
if err := cmd.run(); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if len(fc.Actions()) != 0 {
|
||||
t.Error("expected client call")
|
||||
}
|
||||
expected := "Not installing Tiller due to 'client-only' flag having been set"
|
||||
if !strings.Contains(buf.String(), expected) {
|
||||
t.Errorf("expected %q, got %q", expected, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCmd_dryRun(t *testing.T) {
|
||||
// This is purely defensive in this case.
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.Remove(home)
|
||||
cleanup()
|
||||
}()
|
||||
|
||||
settings.Debug = true
|
||||
|
||||
var buf bytes.Buffer
|
||||
fc := fake.NewSimpleClientset()
|
||||
cmd := &initCmd{
|
||||
out: &buf,
|
||||
home: helmpath.Home(home),
|
||||
kubeClient: fc,
|
||||
clientOnly: true,
|
||||
dryRun: true,
|
||||
namespace: v1.NamespaceDefault,
|
||||
}
|
||||
if err := cmd.run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := len(fc.Actions()); got != 0 {
|
||||
t.Errorf("expected no server calls, got %d", got)
|
||||
}
|
||||
|
||||
docs := bytes.Split(buf.Bytes(), []byte("\n---"))
|
||||
if got, want := len(docs), 2; got != want {
|
||||
t.Fatalf("Expected document count of %d, got %d", want, got)
|
||||
}
|
||||
for _, doc := range docs {
|
||||
var y map[string]interface{}
|
||||
if err := yaml.Unmarshal(doc, &y); err != nil {
|
||||
t.Errorf("Expected parseable YAML, got %q\n\t%s", doc, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureHome(t *testing.T) {
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(home)
|
||||
|
||||
b := bytes.NewBuffer(nil)
|
||||
hh := helmpath.Home(home)
|
||||
settings.Home = hh
|
||||
if err := ensureDirectories(hh, b); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := ensureDefaultRepos(hh, b, false); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := ensureDefaultRepos(hh, b, true); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := ensureRepoFileFormat(hh.RepositoryFile(), b); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
expectedDirs := []string{hh.String(), hh.Repository(), hh.Cache(), hh.LocalRepository()}
|
||||
for _, dir := range expectedDirs {
|
||||
if fi, err := os.Stat(dir); err != nil {
|
||||
t.Errorf("%s", err)
|
||||
} else if !fi.IsDir() {
|
||||
t.Errorf("%s is not a directory", fi)
|
||||
}
|
||||
}
|
||||
|
||||
if fi, err := os.Stat(hh.RepositoryFile()); err != nil {
|
||||
t.Error(err)
|
||||
} else if fi.IsDir() {
|
||||
t.Errorf("%s should not be a directory", fi)
|
||||
}
|
||||
|
||||
if fi, err := os.Stat(hh.LocalRepository(localRepositoryIndexFile)); err != nil {
|
||||
t.Errorf("%s", err)
|
||||
} else if fi.IsDir() {
|
||||
t.Errorf("%s should not be a directory", fi)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCmd_tlsOptions(t *testing.T) {
|
||||
const testDir = "../../testdata"
|
||||
|
||||
// tls certificates in testDir
|
||||
var (
|
||||
testCaCertFile = filepath.Join(testDir, "ca.pem")
|
||||
testCertFile = filepath.Join(testDir, "crt.pem")
|
||||
testKeyFile = filepath.Join(testDir, "key.pem")
|
||||
)
|
||||
|
||||
// these tests verify the effects of permuting the "--tls" and "--tls-verify" flags
|
||||
// and the install options yieled as a result of (*initCmd).tlsOptions()
|
||||
// during helm init.
|
||||
var tests = []struct {
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
enable bool
|
||||
verify bool
|
||||
describe string
|
||||
}{
|
||||
{ // --tls and --tls-verify specified (--tls=true,--tls-verify=true)
|
||||
certFile: testCertFile,
|
||||
keyFile: testKeyFile,
|
||||
caFile: testCaCertFile,
|
||||
enable: true,
|
||||
verify: true,
|
||||
describe: "--tls and --tls-verify specified (--tls=true,--tls-verify=true)",
|
||||
},
|
||||
{ // --tls-verify implies --tls (--tls=false,--tls-verify=true)
|
||||
certFile: testCertFile,
|
||||
keyFile: testKeyFile,
|
||||
caFile: testCaCertFile,
|
||||
enable: false,
|
||||
verify: true,
|
||||
describe: "--tls-verify implies --tls (--tls=false,--tls-verify=true)",
|
||||
},
|
||||
{ // no --tls-verify (--tls=true,--tls-verify=false)
|
||||
certFile: testCertFile,
|
||||
keyFile: testKeyFile,
|
||||
caFile: "",
|
||||
enable: true,
|
||||
verify: false,
|
||||
describe: "no --tls-verify (--tls=true,--tls-verify=false)",
|
||||
},
|
||||
{ // tls is disabled (--tls=false,--tls-verify=false)
|
||||
certFile: "",
|
||||
keyFile: "",
|
||||
caFile: "",
|
||||
enable: false,
|
||||
verify: false,
|
||||
describe: "tls is disabled (--tls=false,--tls-verify=false)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
// emulate tls file specific flags
|
||||
tlsCaCertFile, tlsCertFile, tlsKeyFile = tt.caFile, tt.certFile, tt.keyFile
|
||||
|
||||
// emulate tls enable/verify flags
|
||||
tlsEnable, tlsVerify = tt.enable, tt.verify
|
||||
|
||||
cmd := &initCmd{}
|
||||
if err := cmd.tlsOptions(); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// expected result options
|
||||
expect := installer.Options{
|
||||
TLSCaCertFile: tt.caFile,
|
||||
TLSCertFile: tt.certFile,
|
||||
TLSKeyFile: tt.keyFile,
|
||||
VerifyTLS: tt.verify,
|
||||
EnableTLS: tt.enable || tt.verify,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cmd.opts, expect) {
|
||||
t.Errorf("%s: got %#+v, want %#+v", tt.describe, cmd.opts, expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestInitCmd_output tests that init -o formats are unmarshal-able
|
||||
func TestInitCmd_output(t *testing.T) {
|
||||
// This is purely defensive in this case.
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dbg := settings.Debug
|
||||
settings.Debug = true
|
||||
defer func() {
|
||||
os.Remove(home)
|
||||
settings.Debug = dbg
|
||||
}()
|
||||
fc := fake.NewSimpleClientset()
|
||||
tests := []struct {
|
||||
expectF func([]byte, interface{}) error
|
||||
expectName string
|
||||
}{
|
||||
{
|
||||
json.Unmarshal,
|
||||
"json",
|
||||
},
|
||||
{
|
||||
yaml.Unmarshal,
|
||||
"yaml",
|
||||
},
|
||||
}
|
||||
for _, s := range tests {
|
||||
var buf bytes.Buffer
|
||||
cmd := &initCmd{
|
||||
out: &buf,
|
||||
home: helmpath.Home(home),
|
||||
kubeClient: fc,
|
||||
opts: installer.Options{Output: installer.OutputFormat(s.expectName)},
|
||||
namespace: v1.NamespaceDefault,
|
||||
}
|
||||
if err := cmd.run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := len(fc.Actions()); got != 0 {
|
||||
t.Errorf("expected no server calls, got %d", got)
|
||||
}
|
||||
d := &v1beta1.Deployment{}
|
||||
if err = s.expectF(buf.Bytes(), &d); err != nil {
|
||||
t.Errorf("error unmarshalling init %s output %s %s", s.expectName, err, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
29
src/vendor/github.com/kubernetes/helm/cmd/helm/init_unix.go
generated
vendored
Normal file
29
src/vendor/github.com/kubernetes/helm/cmd/helm/init_unix.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
)
|
||||
|
||||
func createLink(indexFile, cacheFile string, home helmpath.Home) error {
|
||||
return os.Symlink(indexFile, cacheFile)
|
||||
}
|
29
src/vendor/github.com/kubernetes/helm/cmd/helm/init_windows.go
generated
vendored
Normal file
29
src/vendor/github.com/kubernetes/helm/cmd/helm/init_windows.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
)
|
||||
|
||||
func createLink(indexFile, cacheFile string, home helmpath.Home) error {
|
||||
return os.Link(indexFile, cacheFile)
|
||||
}
|
264
src/vendor/github.com/kubernetes/helm/cmd/helm/inspect.go
generated
vendored
Normal file
264
src/vendor/github.com/kubernetes/helm/cmd/helm/inspect.go
generated
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/ghodss/yaml"
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/kubernetes/pkg/util/slice"
|
||||
)
|
||||
|
||||
const inspectDesc = `
|
||||
This command inspects a chart and displays information. It takes a chart reference
|
||||
('stable/drupal'), a full path to a directory or packaged chart, or a URL.
|
||||
|
||||
Inspect prints the contents of the Chart.yaml file and the values.yaml file.
|
||||
`
|
||||
|
||||
const inspectValuesDesc = `
|
||||
This command inspects a chart (directory, file, or URL) and displays the contents
|
||||
of the values.yaml file
|
||||
`
|
||||
|
||||
const inspectChartDesc = `
|
||||
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
|
||||
`
|
||||
|
||||
type inspectCmd struct {
|
||||
chartpath string
|
||||
output string
|
||||
verify bool
|
||||
keyring string
|
||||
out io.Writer
|
||||
version string
|
||||
repoURL string
|
||||
username string
|
||||
password string
|
||||
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
}
|
||||
|
||||
const (
|
||||
chartOnly = "chart"
|
||||
valuesOnly = "values"
|
||||
readmeOnly = "readme"
|
||||
all = "all"
|
||||
)
|
||||
|
||||
var readmeFileNames = []string{"readme.md", "readme.txt", "readme"}
|
||||
|
||||
func newInspectCmd(out io.Writer) *cobra.Command {
|
||||
insp := &inspectCmd{
|
||||
out: out,
|
||||
output: all,
|
||||
}
|
||||
|
||||
inspectCommand := &cobra.Command{
|
||||
Use: "inspect [CHART]",
|
||||
Short: "inspect a chart",
|
||||
Long: inspectDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "chart name"); err != nil {
|
||||
return err
|
||||
}
|
||||
cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring,
|
||||
insp.certFile, insp.keyFile, insp.caFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
insp.chartpath = cp
|
||||
return insp.run()
|
||||
},
|
||||
}
|
||||
|
||||
valuesSubCmd := &cobra.Command{
|
||||
Use: "values [CHART]",
|
||||
Short: "shows inspect values",
|
||||
Long: inspectValuesDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
insp.output = valuesOnly
|
||||
if err := checkArgsLength(len(args), "chart name"); err != nil {
|
||||
return err
|
||||
}
|
||||
cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring,
|
||||
insp.certFile, insp.keyFile, insp.caFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
insp.chartpath = cp
|
||||
return insp.run()
|
||||
},
|
||||
}
|
||||
|
||||
chartSubCmd := &cobra.Command{
|
||||
Use: "chart [CHART]",
|
||||
Short: "shows inspect chart",
|
||||
Long: inspectChartDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
insp.output = chartOnly
|
||||
if err := checkArgsLength(len(args), "chart name"); err != nil {
|
||||
return err
|
||||
}
|
||||
cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring,
|
||||
insp.certFile, insp.keyFile, insp.caFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
insp.chartpath = cp
|
||||
return insp.run()
|
||||
},
|
||||
}
|
||||
|
||||
readmeSubCmd := &cobra.Command{
|
||||
Use: "readme [CHART]",
|
||||
Short: "shows inspect readme",
|
||||
Long: readmeChartDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
insp.output = readmeOnly
|
||||
if err := checkArgsLength(len(args), "chart name"); err != nil {
|
||||
return err
|
||||
}
|
||||
cp, err := locateChartPath(insp.repoURL, insp.username, insp.password, args[0], insp.version, insp.verify, insp.keyring,
|
||||
insp.certFile, insp.keyFile, insp.caFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
insp.chartpath = cp
|
||||
return insp.run()
|
||||
},
|
||||
}
|
||||
|
||||
cmds := []*cobra.Command{inspectCommand, readmeSubCmd, valuesSubCmd, chartSubCmd}
|
||||
vflag := "verify"
|
||||
vdesc := "verify the provenance data for this chart"
|
||||
for _, subCmd := range cmds {
|
||||
subCmd.Flags().BoolVar(&insp.verify, vflag, false, vdesc)
|
||||
}
|
||||
|
||||
kflag := "keyring"
|
||||
kdesc := "path to the keyring containing public verification keys"
|
||||
kdefault := defaultKeyring()
|
||||
for _, subCmd := range cmds {
|
||||
subCmd.Flags().StringVar(&insp.keyring, kflag, kdefault, kdesc)
|
||||
}
|
||||
|
||||
verflag := "version"
|
||||
verdesc := "version of the chart. By default, the newest chart is shown"
|
||||
for _, subCmd := range cmds {
|
||||
subCmd.Flags().StringVar(&insp.version, verflag, "", verdesc)
|
||||
}
|
||||
|
||||
repoURL := "repo"
|
||||
repoURLdesc := "chart repository url where to locate the requested chart"
|
||||
for _, subCmd := range cmds {
|
||||
subCmd.Flags().StringVar(&insp.repoURL, repoURL, "", repoURLdesc)
|
||||
}
|
||||
|
||||
username := "username"
|
||||
usernamedesc := "chart repository username where to locate the requested chart"
|
||||
inspectCommand.Flags().StringVar(&insp.username, username, "", usernamedesc)
|
||||
valuesSubCmd.Flags().StringVar(&insp.username, username, "", usernamedesc)
|
||||
chartSubCmd.Flags().StringVar(&insp.username, username, "", usernamedesc)
|
||||
|
||||
password := "password"
|
||||
passworddesc := "chart repository password where to locate the requested chart"
|
||||
inspectCommand.Flags().StringVar(&insp.password, password, "", passworddesc)
|
||||
valuesSubCmd.Flags().StringVar(&insp.password, password, "", passworddesc)
|
||||
chartSubCmd.Flags().StringVar(&insp.password, password, "", passworddesc)
|
||||
|
||||
certFile := "cert-file"
|
||||
certFiledesc := "verify certificates of HTTPS-enabled servers using this CA bundle"
|
||||
for _, subCmd := range cmds {
|
||||
subCmd.Flags().StringVar(&insp.certFile, certFile, "", certFiledesc)
|
||||
}
|
||||
|
||||
keyFile := "key-file"
|
||||
keyFiledesc := "identify HTTPS client using this SSL key file"
|
||||
for _, subCmd := range cmds {
|
||||
subCmd.Flags().StringVar(&insp.keyFile, keyFile, "", keyFiledesc)
|
||||
}
|
||||
|
||||
caFile := "ca-file"
|
||||
caFiledesc := "chart repository url where to locate the requested chart"
|
||||
for _, subCmd := range cmds {
|
||||
subCmd.Flags().StringVar(&insp.caFile, caFile, "", caFiledesc)
|
||||
}
|
||||
|
||||
for _, subCmd := range cmds[1:] {
|
||||
inspectCommand.AddCommand(subCmd)
|
||||
}
|
||||
|
||||
return inspectCommand
|
||||
}
|
||||
|
||||
func (i *inspectCmd) run() error {
|
||||
chrt, err := chartutil.Load(i.chartpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cf, err := yaml.Marshal(chrt.Metadata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.output == chartOnly || i.output == all {
|
||||
fmt.Fprintln(i.out, string(cf))
|
||||
}
|
||||
|
||||
if (i.output == valuesOnly || i.output == all) && chrt.Values != nil {
|
||||
if i.output == all {
|
||||
fmt.Fprintln(i.out, "---")
|
||||
}
|
||||
fmt.Fprintln(i.out, chrt.Values.Raw)
|
||||
}
|
||||
|
||||
if i.output == readmeOnly || i.output == all {
|
||||
if i.output == all {
|
||||
fmt.Fprintln(i.out, "---")
|
||||
}
|
||||
readme := findReadme(chrt.Files)
|
||||
if readme == nil {
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintln(i.out, string(readme.Value))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func findReadme(files []*any.Any) (file *any.Any) {
|
||||
for _, file := range files {
|
||||
if slice.ContainsString(readmeFileNames, strings.ToLower(file.TypeUrl), nil) {
|
||||
return file
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
80
src/vendor/github.com/kubernetes/helm/cmd/helm/inspect_test.go
generated
vendored
Normal file
80
src/vendor/github.com/kubernetes/helm/cmd/helm/inspect_test.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInspect(t *testing.T) {
|
||||
b := bytes.NewBuffer(nil)
|
||||
|
||||
insp := &inspectCmd{
|
||||
chartpath: "testdata/testcharts/alpine",
|
||||
output: all,
|
||||
out: b,
|
||||
}
|
||||
insp.run()
|
||||
|
||||
// Load the data from the textfixture directly.
|
||||
cdata, err := ioutil.ReadFile("testdata/testcharts/alpine/Chart.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := ioutil.ReadFile("testdata/testcharts/alpine/values.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
readmeData, err := ioutil.ReadFile("testdata/testcharts/alpine/README.md")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
parts := strings.SplitN(b.String(), "---", 3)
|
||||
if len(parts) != 3 {
|
||||
t.Fatalf("Expected 2 parts, got %d", len(parts))
|
||||
}
|
||||
|
||||
expect := []string{
|
||||
strings.Replace(strings.TrimSpace(string(cdata)), "\r", "", -1),
|
||||
strings.Replace(strings.TrimSpace(string(data)), "\r", "", -1),
|
||||
strings.Replace(strings.TrimSpace(string(readmeData)), "\r", "", -1),
|
||||
}
|
||||
|
||||
// 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.Replace(strings.TrimSpace(got), "\r", "", -1)
|
||||
if got != expect[i] {
|
||||
t.Errorf("Expected\n%q\nGot\n%q\n", expect[i], got)
|
||||
}
|
||||
}
|
||||
|
||||
// Regression tests for missing values. See issue #1024.
|
||||
b.Reset()
|
||||
insp = &inspectCmd{
|
||||
chartpath: "testdata/testcharts/novals",
|
||||
output: "values",
|
||||
out: b,
|
||||
}
|
||||
insp.run()
|
||||
if b.Len() != 0 {
|
||||
t.Errorf("expected empty values buffer, got %q", b.String())
|
||||
}
|
||||
}
|
530
src/vendor/github.com/kubernetes/helm/cmd/helm/install.go
generated
vendored
Normal file
530
src/vendor/github.com/kubernetes/helm/cmd/helm/install.go
generated
vendored
Normal file
@ -0,0 +1,530 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/kube"
|
||||
"k8s.io/helm/pkg/proto/hapi/chart"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/strvals"
|
||||
)
|
||||
|
||||
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'.
|
||||
|
||||
$ helm install -f myvalues.yaml ./redis
|
||||
|
||||
or
|
||||
|
||||
$ helm install --set name=prod ./redis
|
||||
|
||||
or
|
||||
|
||||
$ helm install --set-string long_int=1234567890 ./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 ./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 ./redis
|
||||
|
||||
|
||||
To check the generated manifests of a release without installing the chart,
|
||||
the '--debug' and '--dry-run' flags can be combined. This will still require a
|
||||
round-trip to the Tiller server.
|
||||
|
||||
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 stable/mariadb
|
||||
2. By path to a packaged chart: helm install ./nginx-1.2.3.tgz
|
||||
3. By path to an unpacked chart directory: helm install ./nginx
|
||||
4. By absolute URL: helm install https://example.com/charts/nginx-1.2.3.tgz
|
||||
5. By chart reference and repo url: helm install --repo https://example.com/charts/ nginx
|
||||
|
||||
CHART REFERENCES
|
||||
|
||||
A chart reference is a convenient way of reference a chart in a chart repository.
|
||||
|
||||
When you use a chart reference with a repo prefix ('stable/mariadb'), Helm will look in the local
|
||||
configuration for a chart repository named 'stable', and will then look for a
|
||||
chart in that repository whose name is 'mariadb'. It will install the latest
|
||||
version of that chart unless you also 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'.
|
||||
`
|
||||
|
||||
type installCmd struct {
|
||||
name string
|
||||
namespace string
|
||||
valueFiles valueFiles
|
||||
chartPath string
|
||||
dryRun bool
|
||||
disableHooks bool
|
||||
replace bool
|
||||
verify bool
|
||||
keyring string
|
||||
out io.Writer
|
||||
client helm.Interface
|
||||
values []string
|
||||
stringValues []string
|
||||
nameTemplate string
|
||||
version string
|
||||
timeout int64
|
||||
wait bool
|
||||
repoURL string
|
||||
username string
|
||||
password string
|
||||
devel bool
|
||||
depUp bool
|
||||
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
}
|
||||
|
||||
type valueFiles []string
|
||||
|
||||
func (v *valueFiles) String() string {
|
||||
return fmt.Sprint(*v)
|
||||
}
|
||||
|
||||
func (v *valueFiles) Type() string {
|
||||
return "valueFiles"
|
||||
}
|
||||
|
||||
func (v *valueFiles) Set(value string) error {
|
||||
for _, filePath := range strings.Split(value, ",") {
|
||||
*v = append(*v, filePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
inst := &installCmd{
|
||||
out: out,
|
||||
client: c,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "install [CHART]",
|
||||
Short: "install a chart archive",
|
||||
Long: installDesc,
|
||||
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "chart name"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
debug("Original chart version: %q", inst.version)
|
||||
if inst.version == "" && inst.devel {
|
||||
debug("setting version to >0.0.0-0")
|
||||
inst.version = ">0.0.0-0"
|
||||
}
|
||||
|
||||
cp, err := locateChartPath(inst.repoURL, inst.username, inst.password, args[0], inst.version, inst.verify, inst.keyring,
|
||||
inst.certFile, inst.keyFile, inst.caFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inst.chartPath = cp
|
||||
inst.client = ensureHelmClient(inst.client)
|
||||
return inst.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.VarP(&inst.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
|
||||
f.StringVarP(&inst.name, "name", "n", "", "release name. If unspecified, it will autogenerate one for you")
|
||||
f.StringVar(&inst.namespace, "namespace", "", "namespace to install the release into. Defaults to the current kube config namespace.")
|
||||
f.BoolVar(&inst.dryRun, "dry-run", false, "simulate an install")
|
||||
f.BoolVar(&inst.disableHooks, "no-hooks", false, "prevent hooks from running during install")
|
||||
f.BoolVar(&inst.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production")
|
||||
f.StringArrayVar(&inst.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
f.StringArrayVar(&inst.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
f.StringVar(&inst.nameTemplate, "name-template", "", "specify template used to name the release")
|
||||
f.BoolVar(&inst.verify, "verify", false, "verify the package before installing it")
|
||||
f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
|
||||
f.StringVar(&inst.version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed")
|
||||
f.Int64Var(&inst.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
f.BoolVar(&inst.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout")
|
||||
f.StringVar(&inst.repoURL, "repo", "", "chart repository url where to locate the requested chart")
|
||||
f.StringVar(&inst.username, "username", "", "chart repository username where to locate the requested chart")
|
||||
f.StringVar(&inst.password, "password", "", "chart repository password where to locate the requested chart")
|
||||
f.StringVar(&inst.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
|
||||
f.StringVar(&inst.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
|
||||
f.StringVar(&inst.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
|
||||
f.BoolVar(&inst.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
|
||||
f.BoolVar(&inst.depUp, "dep-up", false, "run helm dependency update before installing the chart")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (i *installCmd) run() error {
|
||||
debug("CHART PATH: %s\n", i.chartPath)
|
||||
|
||||
if i.namespace == "" {
|
||||
i.namespace = defaultNamespace()
|
||||
}
|
||||
|
||||
rawVals, err := vals(i.valueFiles, i.values, i.stringValues)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If template is specified, try to run the template.
|
||||
if i.nameTemplate != "" {
|
||||
i.name, err = generateName(i.nameTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Print the final name so the user knows what the final name of the release is.
|
||||
fmt.Printf("FINAL NAME: %s\n", i.name)
|
||||
}
|
||||
|
||||
// Check chart requirements to make sure all dependencies are present in /charts
|
||||
chartRequested, err := chartutil.Load(i.chartPath)
|
||||
if err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
|
||||
if req, err := chartutil.LoadRequirements(chartRequested); err == 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/kubernetes/helm/issues/2209
|
||||
if err := checkDependencies(chartRequested, req); err != nil {
|
||||
if i.depUp {
|
||||
man := &downloader.Manager{
|
||||
Out: i.out,
|
||||
ChartPath: i.chartPath,
|
||||
HelmHome: settings.Home,
|
||||
Keyring: defaultKeyring(),
|
||||
SkipUpdate: false,
|
||||
Getters: getter.All(settings),
|
||||
}
|
||||
if err := man.Update(); err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
} else {
|
||||
return prettyError(err)
|
||||
}
|
||||
|
||||
}
|
||||
} else if err != chartutil.ErrRequirementsNotFound {
|
||||
return fmt.Errorf("cannot load requirements: %v", err)
|
||||
}
|
||||
|
||||
res, err := i.client.InstallReleaseFromChart(
|
||||
chartRequested,
|
||||
i.namespace,
|
||||
helm.ValueOverrides(rawVals),
|
||||
helm.ReleaseName(i.name),
|
||||
helm.InstallDryRun(i.dryRun),
|
||||
helm.InstallReuseName(i.replace),
|
||||
helm.InstallDisableHooks(i.disableHooks),
|
||||
helm.InstallTimeout(i.timeout),
|
||||
helm.InstallWait(i.wait))
|
||||
if err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
|
||||
rel := res.GetRelease()
|
||||
if rel == nil {
|
||||
return nil
|
||||
}
|
||||
i.printRelease(rel)
|
||||
|
||||
// If this is a dry run, we can't display status.
|
||||
if i.dryRun {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Print the status like status command does
|
||||
status, err := i.client.ReleaseStatus(rel.Name)
|
||||
if err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
PrintStatus(i.out, status)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merges source and destination map, preferring values from the source map
|
||||
func mergeValues(dest map[string]interface{}, src map[string]interface{}) map[string]interface{} {
|
||||
for k, v := range src {
|
||||
// If the key doesn't exist already, then just set the key to that value
|
||||
if _, exists := dest[k]; !exists {
|
||||
dest[k] = v
|
||||
continue
|
||||
}
|
||||
nextMap, ok := v.(map[string]interface{})
|
||||
// If it isn't another map, overwrite the value
|
||||
if !ok {
|
||||
dest[k] = v
|
||||
continue
|
||||
}
|
||||
// If the key doesn't exist already, then just set the key to that value
|
||||
if _, exists := dest[k]; !exists {
|
||||
dest[k] = nextMap
|
||||
continue
|
||||
}
|
||||
// Edge case: If the key exists in the destination, but isn't a map
|
||||
destMap, isMap := dest[k].(map[string]interface{})
|
||||
// If the source map has a map for this key, prefer it
|
||||
if !isMap {
|
||||
dest[k] = v
|
||||
continue
|
||||
}
|
||||
// If we got to this point, it is a map in both, so merge them
|
||||
dest[k] = mergeValues(destMap, nextMap)
|
||||
}
|
||||
return dest
|
||||
}
|
||||
|
||||
// vals merges values from files specified via -f/--values and
|
||||
// directly via --set or --set-string, marshaling them to YAML
|
||||
func vals(valueFiles valueFiles, values []string, stringValues []string) ([]byte, error) {
|
||||
base := map[string]interface{}{}
|
||||
|
||||
// User specified a values files via -f/--values
|
||||
for _, filePath := range valueFiles {
|
||||
currentMap := map[string]interface{}{}
|
||||
|
||||
var bytes []byte
|
||||
var err error
|
||||
if strings.TrimSpace(filePath) == "-" {
|
||||
bytes, err = ioutil.ReadAll(os.Stdin)
|
||||
} else {
|
||||
bytes, err = readFile(filePath)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(bytes, ¤tMap); err != nil {
|
||||
return []byte{}, fmt.Errorf("failed to parse %s: %s", filePath, err)
|
||||
}
|
||||
// Merge with the previous map
|
||||
base = mergeValues(base, currentMap)
|
||||
}
|
||||
|
||||
// User specified a value via --set
|
||||
for _, value := range values {
|
||||
if err := strvals.ParseInto(value, base); err != nil {
|
||||
return []byte{}, fmt.Errorf("failed parsing --set data: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// User specified a value via --set-string
|
||||
for _, value := range stringValues {
|
||||
if err := strvals.ParseIntoString(value, base); err != nil {
|
||||
return []byte{}, fmt.Errorf("failed parsing --set-string data: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return yaml.Marshal(base)
|
||||
}
|
||||
|
||||
// printRelease prints info about a release if the Debug is true.
|
||||
func (i *installCmd) printRelease(rel *release.Release) {
|
||||
if rel == nil {
|
||||
return
|
||||
}
|
||||
// TODO: Switch to text/template like everything else.
|
||||
fmt.Fprintf(i.out, "NAME: %s\n", rel.Name)
|
||||
if settings.Debug {
|
||||
printRelease(i.out, rel)
|
||||
}
|
||||
}
|
||||
|
||||
// locateChartPath 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:
|
||||
// - current working directory
|
||||
// - if path is absolute or begins with '.', error out here
|
||||
// - chart repos in $HELM_HOME
|
||||
// - URL
|
||||
//
|
||||
// If 'verify' is true, this will attempt to also verify the chart.
|
||||
func locateChartPath(repoURL, username, password, name, version string, verify bool, keyring,
|
||||
certFile, keyFile, caFile string) (string, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
version = strings.TrimSpace(version)
|
||||
if fi, err := os.Stat(name); err == nil {
|
||||
abs, err := filepath.Abs(name)
|
||||
if err != nil {
|
||||
return abs, err
|
||||
}
|
||||
if verify {
|
||||
if fi.IsDir() {
|
||||
return "", errors.New("cannot verify a directory")
|
||||
}
|
||||
if _, err := downloader.VerifyChart(abs, keyring); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return abs, nil
|
||||
}
|
||||
if filepath.IsAbs(name) || strings.HasPrefix(name, ".") {
|
||||
return name, fmt.Errorf("path %q not found", name)
|
||||
}
|
||||
|
||||
crepo := filepath.Join(settings.Home.Repository(), name)
|
||||
if _, err := os.Stat(crepo); err == nil {
|
||||
return filepath.Abs(crepo)
|
||||
}
|
||||
|
||||
dl := downloader.ChartDownloader{
|
||||
HelmHome: settings.Home,
|
||||
Out: os.Stdout,
|
||||
Keyring: keyring,
|
||||
Getters: getter.All(settings),
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
if verify {
|
||||
dl.Verify = downloader.VerifyAlways
|
||||
}
|
||||
if repoURL != "" {
|
||||
chartURL, err := repo.FindChartInAuthRepoURL(repoURL, username, password, name, version,
|
||||
certFile, keyFile, caFile, getter.All(settings))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name = chartURL
|
||||
}
|
||||
|
||||
if _, err := os.Stat(settings.Home.Archive()); os.IsNotExist(err) {
|
||||
os.MkdirAll(settings.Home.Archive(), 0744)
|
||||
}
|
||||
|
||||
filename, _, err := dl.DownloadTo(name, version, settings.Home.Archive())
|
||||
if err == nil {
|
||||
lname, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return filename, err
|
||||
}
|
||||
debug("Fetched %s to %s\n", name, filename)
|
||||
return lname, nil
|
||||
} else if settings.Debug {
|
||||
return filename, err
|
||||
}
|
||||
|
||||
return filename, fmt.Errorf("failed to download %q (hint: running `helm repo update` may help)", name)
|
||||
}
|
||||
|
||||
func generateName(nameTemplate string) (string, error) {
|
||||
t, err := template.New("name-template").Funcs(sprig.TxtFuncMap()).Parse(nameTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var b bytes.Buffer
|
||||
err = t.Execute(&b, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
func defaultNamespace() string {
|
||||
if ns, _, err := kube.GetConfig(settings.KubeContext).Namespace(); err == nil {
|
||||
return ns
|
||||
}
|
||||
return "default"
|
||||
}
|
||||
|
||||
func checkDependencies(ch *chart.Chart, reqs *chartutil.Requirements) error {
|
||||
missing := []string{}
|
||||
|
||||
deps := ch.GetDependencies()
|
||||
for _, r := range reqs.Dependencies {
|
||||
found := false
|
||||
for _, d := range deps {
|
||||
if d.Metadata.Name == r.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
missing = append(missing, r.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
return fmt.Errorf("found in requirements.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//readFile load a file from the local directory or a remote file with a url.
|
||||
func readFile(filePath string) ([]byte, error) {
|
||||
u, _ := url.Parse(filePath)
|
||||
p := getter.All(settings)
|
||||
|
||||
// FIXME: maybe someone handle other protocols like ftp.
|
||||
getterConstructor, err := p.ByScheme(u.Scheme)
|
||||
|
||||
if err != nil {
|
||||
return ioutil.ReadFile(filePath)
|
||||
}
|
||||
|
||||
getter, err := getterConstructor(filePath, "", "", "")
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
data, err := getter.Get(filePath)
|
||||
return data.Bytes(), err
|
||||
}
|
283
src/vendor/github.com/kubernetes/helm/cmd/helm/install_test.go
generated
vendored
Normal file
283
src/vendor/github.com/kubernetes/helm/cmd/helm/install_test.go
generated
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
||||
func TestInstall(t *testing.T) {
|
||||
tests := []releaseCase{
|
||||
// Install, base case
|
||||
{
|
||||
name: "basic install",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: strings.Split("--name aeneas", " "),
|
||||
expected: "aeneas",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}),
|
||||
},
|
||||
// Install, no hooks
|
||||
{
|
||||
name: "install without hooks",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: strings.Split("--name aeneas --no-hooks", " "),
|
||||
expected: "aeneas",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}),
|
||||
},
|
||||
// Install, values from cli
|
||||
{
|
||||
name: "install with values",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: strings.Split("--name virgil --set foo=bar", " "),
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
|
||||
expected: "virgil",
|
||||
},
|
||||
// Install, values from cli via multiple --set
|
||||
{
|
||||
name: "install with multiple values",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: strings.Split("--name virgil --set foo=bar --set bar=foo", " "),
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
|
||||
expected: "virgil",
|
||||
},
|
||||
// Install, values from yaml
|
||||
{
|
||||
name: "install with values",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: strings.Split("--name virgil -f testdata/testcharts/alpine/extra_values.yaml", " "),
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
|
||||
expected: "virgil",
|
||||
},
|
||||
// Install, values from multiple yaml
|
||||
{
|
||||
name: "install with values",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: strings.Split("--name virgil -f testdata/testcharts/alpine/extra_values.yaml -f testdata/testcharts/alpine/more_values.yaml", " "),
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
|
||||
expected: "virgil",
|
||||
},
|
||||
// Install, no charts
|
||||
{
|
||||
name: "install with no chart specified",
|
||||
args: []string{},
|
||||
err: true,
|
||||
},
|
||||
// Install, re-use name
|
||||
{
|
||||
name: "install and replace release",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: strings.Split("--name aeneas --replace", " "),
|
||||
expected: "aeneas",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "aeneas"}),
|
||||
},
|
||||
// Install, with timeout
|
||||
{
|
||||
name: "install with a timeout",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: strings.Split("--name foobar --timeout 120", " "),
|
||||
expected: "foobar",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "foobar"}),
|
||||
},
|
||||
// Install, with wait
|
||||
{
|
||||
name: "install with a wait",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: strings.Split("--name apollo --wait", " "),
|
||||
expected: "apollo",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "apollo"}),
|
||||
},
|
||||
// Install, using the name-template
|
||||
{
|
||||
name: "install with name-template",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: []string{"--name-template", "{{upper \"foobar\"}}"},
|
||||
expected: "FOOBAR",
|
||||
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "FOOBAR"}),
|
||||
},
|
||||
// Install, perform chart verification along the way.
|
||||
{
|
||||
name: "install with verification, missing provenance",
|
||||
args: []string{"testdata/testcharts/compressedchart-0.1.0.tgz"},
|
||||
flags: strings.Split("--verify --keyring testdata/helm-test-key.pub", " "),
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
name: "install with verification, directory instead of file",
|
||||
args: []string{"testdata/testcharts/signtest"},
|
||||
flags: strings.Split("--verify --keyring testdata/helm-test-key.pub", " "),
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
name: "install with verification, valid",
|
||||
args: []string{"testdata/testcharts/signtest-0.1.0.tgz"},
|
||||
flags: strings.Split("--verify --keyring testdata/helm-test-key.pub", " "),
|
||||
},
|
||||
// Install, chart with missing dependencies in /charts
|
||||
{
|
||||
name: "install chart with missing dependencies",
|
||||
args: []string{"testdata/testcharts/chart-missing-deps"},
|
||||
err: true,
|
||||
},
|
||||
// Install, chart with bad requirements.yaml in /charts
|
||||
{
|
||||
name: "install chart with bad requirements.yaml",
|
||||
args: []string{"testdata/testcharts/chart-bad-requirements"},
|
||||
err: true,
|
||||
},
|
||||
}
|
||||
|
||||
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newInstallCmd(c, out)
|
||||
})
|
||||
}
|
||||
|
||||
type nameTemplateTestCase struct {
|
||||
tpl string
|
||||
expected string
|
||||
expectedErrorStr string
|
||||
}
|
||||
|
||||
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 := generateName(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 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 := mergeValues(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 = mergeValues(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 = mergeValues(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 = mergeValues(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)
|
||||
}
|
||||
}
|
372
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/install.go
generated
vendored
Normal file
372
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/install.go
generated
vendored
Normal file
@ -0,0 +1,372 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 "k8s.io/helm/cmd/helm/installer"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/ghodss/yaml"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
extensionsclient "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
|
||||
"k8s.io/helm/pkg/version"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
)
|
||||
|
||||
// Install uses Kubernetes client to install Tiller.
|
||||
//
|
||||
// Returns an error if the command failed.
|
||||
func Install(client kubernetes.Interface, opts *Options) error {
|
||||
if err := createDeployment(client.ExtensionsV1beta1(), opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := createService(client.CoreV1(), opts.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.tls() {
|
||||
if err := createSecret(client.CoreV1(), opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Upgrade uses Kubernetes client to upgrade Tiller to current version.
|
||||
//
|
||||
// Returns an error if the command failed.
|
||||
func Upgrade(client kubernetes.Interface, opts *Options) error {
|
||||
obj, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tillerImage := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if semverCompare(tillerImage) == -1 && !opts.ForceUpgrade {
|
||||
return errors.New("current Tiller version is newer, use --force-upgrade to downgrade")
|
||||
}
|
||||
obj.Spec.Template.Spec.Containers[0].Image = opts.selectImage()
|
||||
obj.Spec.Template.Spec.Containers[0].ImagePullPolicy = opts.pullPolicy()
|
||||
obj.Spec.Template.Spec.ServiceAccountName = opts.ServiceAccount
|
||||
if _, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Update(obj); err != nil {
|
||||
return err
|
||||
}
|
||||
// If the service does not exists that would mean we are upgrading from a Tiller version
|
||||
// that didn't deploy the service, so install it.
|
||||
_, err = client.CoreV1().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
return createService(client.CoreV1(), opts.Namespace)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// semverCompare returns whether the client's version is older, equal or newer than the given image's version.
|
||||
func semverCompare(image string) int {
|
||||
split := strings.Split(image, ":")
|
||||
if len(split) < 2 {
|
||||
// If we don't know the version, we consider the client version newer.
|
||||
return 1
|
||||
}
|
||||
tillerVersion, err := semver.NewVersion(split[1])
|
||||
if err != nil {
|
||||
// same thing with unparsable tiller versions (e.g. canary releases).
|
||||
return 1
|
||||
}
|
||||
clientVersion, err := semver.NewVersion(version.Version)
|
||||
if err != nil {
|
||||
// aaaaaand same thing with unparsable helm versions (e.g. canary releases).
|
||||
return 1
|
||||
}
|
||||
return clientVersion.Compare(tillerVersion)
|
||||
}
|
||||
|
||||
// createDeployment creates the Tiller Deployment resource.
|
||||
func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error {
|
||||
obj, err := deployment(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = client.Deployments(obj.Namespace).Create(obj)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
// deployment gets the deployment object that installs Tiller.
|
||||
func deployment(opts *Options) (*v1beta1.Deployment, error) {
|
||||
return generateDeployment(opts)
|
||||
}
|
||||
|
||||
// createService creates the Tiller service resource
|
||||
func createService(client corev1.ServicesGetter, namespace string) error {
|
||||
obj := service(namespace)
|
||||
_, err := client.Services(obj.Namespace).Create(obj)
|
||||
return err
|
||||
}
|
||||
|
||||
// service gets the service object that installs Tiller.
|
||||
func service(namespace string) *v1.Service {
|
||||
return generateService(namespace)
|
||||
}
|
||||
|
||||
// DeploymentManifest gets the manifest (as a string) that describes the Tiller Deployment
|
||||
// resource.
|
||||
func DeploymentManifest(opts *Options) (string, error) {
|
||||
obj, err := deployment(opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf, err := yaml.Marshal(obj)
|
||||
return string(buf), err
|
||||
}
|
||||
|
||||
// ServiceManifest gets the manifest (as a string) that describes the Tiller Service
|
||||
// resource.
|
||||
func ServiceManifest(namespace string) (string, error) {
|
||||
obj := service(namespace)
|
||||
buf, err := yaml.Marshal(obj)
|
||||
return string(buf), err
|
||||
}
|
||||
|
||||
func generateLabels(labels map[string]string) map[string]string {
|
||||
labels["app"] = "helm"
|
||||
return labels
|
||||
}
|
||||
|
||||
// parseNodeSelectors parses a comma delimited list of key=values pairs into a map.
|
||||
func parseNodeSelectorsInto(labels string, m map[string]string) error {
|
||||
kv := strings.Split(labels, ",")
|
||||
for _, v := range kv {
|
||||
el := strings.Split(v, "=")
|
||||
if len(el) == 2 {
|
||||
m[el[0]] = el[1]
|
||||
} else {
|
||||
return fmt.Errorf("invalid nodeSelector label: %q", kv)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
|
||||
labels := generateLabels(map[string]string{"name": "tiller"})
|
||||
nodeSelectors := map[string]string{}
|
||||
if len(opts.NodeSelectors) > 0 {
|
||||
err := parseNodeSelectorsInto(opts.NodeSelectors, nodeSelectors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d := &v1beta1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: opts.Namespace,
|
||||
Name: deploymentName,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1beta1.DeploymentSpec{
|
||||
Replicas: opts.getReplicas(),
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
ServiceAccountName: opts.ServiceAccount,
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "tiller",
|
||||
Image: opts.selectImage(),
|
||||
ImagePullPolicy: opts.pullPolicy(),
|
||||
Ports: []v1.ContainerPort{
|
||||
{ContainerPort: 44134, Name: "tiller"},
|
||||
{ContainerPort: 44135, Name: "http"},
|
||||
},
|
||||
Env: []v1.EnvVar{
|
||||
{Name: "TILLER_NAMESPACE", Value: opts.Namespace},
|
||||
{Name: "TILLER_HISTORY_MAX", Value: fmt.Sprintf("%d", opts.MaxHistory)},
|
||||
},
|
||||
LivenessProbe: &v1.Probe{
|
||||
Handler: v1.Handler{
|
||||
HTTPGet: &v1.HTTPGetAction{
|
||||
Path: "/liveness",
|
||||
Port: intstr.FromInt(44135),
|
||||
},
|
||||
},
|
||||
InitialDelaySeconds: 1,
|
||||
TimeoutSeconds: 1,
|
||||
},
|
||||
ReadinessProbe: &v1.Probe{
|
||||
Handler: v1.Handler{
|
||||
HTTPGet: &v1.HTTPGetAction{
|
||||
Path: "/readiness",
|
||||
Port: intstr.FromInt(44135),
|
||||
},
|
||||
},
|
||||
InitialDelaySeconds: 1,
|
||||
TimeoutSeconds: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
HostNetwork: opts.EnableHostNetwork,
|
||||
NodeSelector: nodeSelectors,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if opts.tls() {
|
||||
const certsDir = "/etc/certs"
|
||||
|
||||
var tlsVerify, tlsEnable = "", "1"
|
||||
if opts.VerifyTLS {
|
||||
tlsVerify = "1"
|
||||
}
|
||||
|
||||
// Mount secret to "/etc/certs"
|
||||
d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, v1.VolumeMount{
|
||||
Name: "tiller-certs",
|
||||
ReadOnly: true,
|
||||
MountPath: certsDir,
|
||||
})
|
||||
// Add environment variable required for enabling TLS
|
||||
d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, []v1.EnvVar{
|
||||
{Name: "TILLER_TLS_VERIFY", Value: tlsVerify},
|
||||
{Name: "TILLER_TLS_ENABLE", Value: tlsEnable},
|
||||
{Name: "TILLER_TLS_CERTS", Value: certsDir},
|
||||
}...)
|
||||
// Add secret volume to deployment
|
||||
d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, v1.Volume{
|
||||
Name: "tiller-certs",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: "tiller-secret",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
// if --override values were specified, ultimately convert values and deployment to maps,
|
||||
// merge them and convert back to Deployment
|
||||
if len(opts.Values) > 0 {
|
||||
// base deployment struct
|
||||
var dd v1beta1.Deployment
|
||||
// get YAML from original deployment
|
||||
dy, err := yaml.Marshal(d)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error marshalling base Tiller Deployment: %s", err)
|
||||
}
|
||||
// convert deployment YAML to values
|
||||
dv, err := chartutil.ReadValues(dy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error converting Deployment manifest: %s ", err)
|
||||
}
|
||||
dm := dv.AsMap()
|
||||
// merge --set values into our map
|
||||
sm, err := opts.valuesMap(dm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error merging --set values into Deployment manifest")
|
||||
}
|
||||
finalY, err := yaml.Marshal(sm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error marshalling merged map to YAML: %s ", err)
|
||||
}
|
||||
// convert merged values back into deployment
|
||||
err = yaml.Unmarshal(finalY, &dd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error unmarshalling Values to Deployment manifest: %s ", err)
|
||||
}
|
||||
d = &dd
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func generateService(namespace string) *v1.Service {
|
||||
labels := generateLabels(map[string]string{"name": "tiller"})
|
||||
s := &v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: serviceName,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "tiller",
|
||||
Port: 44134,
|
||||
TargetPort: intstr.FromString("tiller"),
|
||||
},
|
||||
},
|
||||
Selector: labels,
|
||||
},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SecretManifest gets the manifest (as a string) that describes the Tiller Secret resource.
|
||||
func SecretManifest(opts *Options) (string, error) {
|
||||
o, err := generateSecret(opts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf, err := yaml.Marshal(o)
|
||||
return string(buf), err
|
||||
}
|
||||
|
||||
// createSecret creates the Tiller secret resource.
|
||||
func createSecret(client corev1.SecretsGetter, opts *Options) error {
|
||||
o, err := generateSecret(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = client.Secrets(o.Namespace).Create(o)
|
||||
return err
|
||||
}
|
||||
|
||||
// generateSecret builds the secret object that hold Tiller secrets.
|
||||
func generateSecret(opts *Options) (*v1.Secret, error) {
|
||||
|
||||
labels := generateLabels(map[string]string{"name": "tiller"})
|
||||
secret := &v1.Secret{
|
||||
Type: v1.SecretTypeOpaque,
|
||||
Data: make(map[string][]byte),
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: secretName,
|
||||
Labels: labels,
|
||||
Namespace: opts.Namespace,
|
||||
},
|
||||
}
|
||||
var err error
|
||||
if secret.Data["tls.key"], err = read(opts.TLSKeyFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if secret.Data["tls.crt"], err = read(opts.TLSCertFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.VerifyTLS {
|
||||
if secret.Data["ca.crt"], err = read(opts.TLSCaCertFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
func read(path string) (b []byte, err error) { return ioutil.ReadFile(path) }
|
740
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/install_test.go
generated
vendored
Normal file
740
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/install_test.go
generated
vendored
Normal file
@ -0,0 +1,740 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 "k8s.io/helm/cmd/helm/installer"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
testcore "k8s.io/client-go/testing"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/version"
|
||||
)
|
||||
|
||||
func TestDeploymentManifest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
image string
|
||||
canary bool
|
||||
expect string
|
||||
imagePullPolicy v1.PullPolicy
|
||||
}{
|
||||
{"default", "", false, "gcr.io/kubernetes-helm/tiller:" + version.Version, "IfNotPresent"},
|
||||
{"canary", "example.com/tiller", true, "gcr.io/kubernetes-helm/tiller:canary", "Always"},
|
||||
{"custom", "example.com/tiller:latest", false, "example.com/tiller:latest", "IfNotPresent"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
o, err := DeploymentManifest(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary})
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error %q", tt.name, err)
|
||||
}
|
||||
var dep v1beta1.Deployment
|
||||
if err := yaml.Unmarshal([]byte(o), &dep); err != nil {
|
||||
t.Fatalf("%s: error %q", tt.name, err)
|
||||
}
|
||||
|
||||
if got := dep.Spec.Template.Spec.Containers[0].Image; got != tt.expect {
|
||||
t.Errorf("%s: expected image %q, got %q", tt.name, tt.expect, got)
|
||||
}
|
||||
|
||||
if got := dep.Spec.Template.Spec.Containers[0].ImagePullPolicy; got != tt.imagePullPolicy {
|
||||
t.Errorf("%s: expected imagePullPolicy %q, got %q", tt.name, tt.imagePullPolicy, got)
|
||||
}
|
||||
|
||||
if got := dep.Spec.Template.Spec.Containers[0].Env[0].Value; got != v1.NamespaceDefault {
|
||||
t.Errorf("%s: expected namespace %q, got %q", tt.name, v1.NamespaceDefault, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeploymentManifestForServiceAccount(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
image string
|
||||
canary bool
|
||||
expect string
|
||||
imagePullPolicy v1.PullPolicy
|
||||
serviceAccount string
|
||||
}{
|
||||
{"withSA", "", false, "gcr.io/kubernetes-helm/tiller:latest", "IfNotPresent", "service-account"},
|
||||
{"withoutSA", "", false, "gcr.io/kubernetes-helm/tiller:latest", "IfNotPresent", ""},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
o, err := DeploymentManifest(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary, ServiceAccount: tt.serviceAccount})
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error %q", tt.name, err)
|
||||
}
|
||||
|
||||
var d v1beta1.Deployment
|
||||
if err := yaml.Unmarshal([]byte(o), &d); err != nil {
|
||||
t.Fatalf("%s: error %q", tt.name, err)
|
||||
}
|
||||
if got := d.Spec.Template.Spec.ServiceAccountName; got != tt.serviceAccount {
|
||||
t.Errorf("%s: expected service account value %q, got %q", tt.name, tt.serviceAccount, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeploymentManifest_WithTLS(t *testing.T) {
|
||||
tests := []struct {
|
||||
opts Options
|
||||
name string
|
||||
enable string
|
||||
verify string
|
||||
}{
|
||||
{
|
||||
Options{Namespace: v1.NamespaceDefault, EnableTLS: true, VerifyTLS: true},
|
||||
"tls enable (true), tls verify (true)",
|
||||
"1",
|
||||
"1",
|
||||
},
|
||||
{
|
||||
Options{Namespace: v1.NamespaceDefault, EnableTLS: true, VerifyTLS: false},
|
||||
"tls enable (true), tls verify (false)",
|
||||
"1",
|
||||
"",
|
||||
},
|
||||
{
|
||||
Options{Namespace: v1.NamespaceDefault, EnableTLS: false, VerifyTLS: true},
|
||||
"tls enable (false), tls verify (true)",
|
||||
"1",
|
||||
"1",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
o, err := DeploymentManifest(&tt.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error %q", tt.name, err)
|
||||
}
|
||||
|
||||
var d v1beta1.Deployment
|
||||
if err := yaml.Unmarshal([]byte(o), &d); err != nil {
|
||||
t.Fatalf("%s: error %q", tt.name, err)
|
||||
}
|
||||
// verify environment variable in deployment reflect the use of tls being enabled.
|
||||
if got := d.Spec.Template.Spec.Containers[0].Env[2].Value; got != tt.verify {
|
||||
t.Errorf("%s: expected tls verify env value %q, got %q", tt.name, tt.verify, got)
|
||||
}
|
||||
if got := d.Spec.Template.Spec.Containers[0].Env[3].Value; got != tt.enable {
|
||||
t.Errorf("%s: expected tls enable env value %q, got %q", tt.name, tt.enable, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceManifest(t *testing.T) {
|
||||
o, err := ServiceManifest(v1.NamespaceDefault)
|
||||
if err != nil {
|
||||
t.Fatalf("error %q", err)
|
||||
}
|
||||
var svc v1.Service
|
||||
if err := yaml.Unmarshal([]byte(o), &svc); err != nil {
|
||||
t.Fatalf("error %q", err)
|
||||
}
|
||||
|
||||
if got := svc.ObjectMeta.Namespace; got != v1.NamespaceDefault {
|
||||
t.Errorf("expected namespace %s, got %s", v1.NamespaceDefault, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecretManifest(t *testing.T) {
|
||||
o, err := SecretManifest(&Options{
|
||||
VerifyTLS: true,
|
||||
EnableTLS: true,
|
||||
Namespace: v1.NamespaceDefault,
|
||||
TLSKeyFile: tlsTestFile(t, "key.pem"),
|
||||
TLSCertFile: tlsTestFile(t, "crt.pem"),
|
||||
TLSCaCertFile: tlsTestFile(t, "ca.pem"),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("error %q", err)
|
||||
}
|
||||
|
||||
var obj v1.Secret
|
||||
if err := yaml.Unmarshal([]byte(o), &obj); err != nil {
|
||||
t.Fatalf("error %q", err)
|
||||
}
|
||||
|
||||
if got := obj.ObjectMeta.Namespace; got != v1.NamespaceDefault {
|
||||
t.Errorf("expected namespace %s, got %s", v1.NamespaceDefault, got)
|
||||
}
|
||||
if _, ok := obj.Data["tls.key"]; !ok {
|
||||
t.Errorf("missing 'tls.key' in generated secret object")
|
||||
}
|
||||
if _, ok := obj.Data["tls.crt"]; !ok {
|
||||
t.Errorf("missing 'tls.crt' in generated secret object")
|
||||
}
|
||||
if _, ok := obj.Data["ca.crt"]; !ok {
|
||||
t.Errorf("missing 'ca.crt' in generated secret object")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstall(t *testing.T) {
|
||||
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
|
||||
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment)
|
||||
l := obj.GetLabels()
|
||||
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
|
||||
t.Errorf("expected labels = '', got '%s'", l)
|
||||
}
|
||||
i := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if i != image {
|
||||
t.Errorf("expected image = '%s', got '%s'", image, i)
|
||||
}
|
||||
ports := len(obj.Spec.Template.Spec.Containers[0].Ports)
|
||||
if ports != 2 {
|
||||
t.Errorf("expected ports = 2, got '%d'", ports)
|
||||
}
|
||||
replicas := obj.Spec.Replicas
|
||||
if int(*replicas) != 1 {
|
||||
t.Errorf("expected replicas = 1, got '%d'", replicas)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.CreateAction).GetObject().(*v1.Service)
|
||||
l := obj.GetLabels()
|
||||
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
|
||||
t.Errorf("expected labels = '', got '%s'", l)
|
||||
}
|
||||
n := obj.ObjectMeta.Namespace
|
||||
if n != v1.NamespaceDefault {
|
||||
t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image}
|
||||
if err := Install(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 2 {
|
||||
t.Errorf("unexpected actions: %v, expected 2 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallHA(t *testing.T) {
|
||||
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
|
||||
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment)
|
||||
replicas := obj.Spec.Replicas
|
||||
if int(*replicas) != 2 {
|
||||
t.Errorf("expected replicas = 2, got '%d'", replicas)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
|
||||
opts := &Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: image,
|
||||
Replicas: 2,
|
||||
}
|
||||
if err := Install(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstall_WithTLS(t *testing.T) {
|
||||
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
|
||||
name := "tiller-secret"
|
||||
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment)
|
||||
l := obj.GetLabels()
|
||||
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
|
||||
t.Errorf("expected labels = '', got '%s'", l)
|
||||
}
|
||||
i := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if i != image {
|
||||
t.Errorf("expected image = '%s', got '%s'", image, i)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.CreateAction).GetObject().(*v1.Service)
|
||||
l := obj.GetLabels()
|
||||
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
|
||||
t.Errorf("expected labels = '', got '%s'", l)
|
||||
}
|
||||
n := obj.ObjectMeta.Namespace
|
||||
if n != v1.NamespaceDefault {
|
||||
t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("create", "secrets", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.CreateAction).GetObject().(*v1.Secret)
|
||||
if l := obj.GetLabels(); reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
|
||||
t.Errorf("expected labels = '', got '%s'", l)
|
||||
}
|
||||
if n := obj.ObjectMeta.Namespace; n != v1.NamespaceDefault {
|
||||
t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n)
|
||||
}
|
||||
if s := obj.ObjectMeta.Name; s != name {
|
||||
t.Errorf("expected name = '%s', got '%s'", name, s)
|
||||
}
|
||||
if _, ok := obj.Data["tls.key"]; !ok {
|
||||
t.Errorf("missing 'tls.key' in generated secret object")
|
||||
}
|
||||
if _, ok := obj.Data["tls.crt"]; !ok {
|
||||
t.Errorf("missing 'tls.crt' in generated secret object")
|
||||
}
|
||||
if _, ok := obj.Data["ca.crt"]; !ok {
|
||||
t.Errorf("missing 'ca.crt' in generated secret object")
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
|
||||
opts := &Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: image,
|
||||
EnableTLS: true,
|
||||
VerifyTLS: true,
|
||||
TLSKeyFile: tlsTestFile(t, "key.pem"),
|
||||
TLSCertFile: tlsTestFile(t, "crt.pem"),
|
||||
TLSCaCertFile: tlsTestFile(t, "ca.pem"),
|
||||
}
|
||||
|
||||
if err := Install(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 3 {
|
||||
t.Errorf("unexpected actions: %v, expected 3 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstall_canary(t *testing.T) {
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment)
|
||||
i := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if i != "gcr.io/kubernetes-helm/tiller:canary" {
|
||||
t.Errorf("expected canary image, got '%s'", i)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.CreateAction).GetObject().(*v1.Service)
|
||||
return true, obj, nil
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: v1.NamespaceDefault, UseCanary: true}
|
||||
if err := Install(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 2 {
|
||||
t.Errorf("unexpected actions: %v, expected 2 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpgrade(t *testing.T) {
|
||||
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
|
||||
serviceAccount := "newServiceAccount"
|
||||
existingDeployment, _ := deployment(&Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: "imageToReplace:v1.0.0",
|
||||
ServiceAccount: "serviceAccountToReplace",
|
||||
UseCanary: false,
|
||||
})
|
||||
existingService := service(v1.NamespaceDefault)
|
||||
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingDeployment, nil
|
||||
})
|
||||
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
|
||||
i := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if i != image {
|
||||
t.Errorf("expected image = '%s', got '%s'", image, i)
|
||||
}
|
||||
sa := obj.Spec.Template.Spec.ServiceAccountName
|
||||
if sa != serviceAccount {
|
||||
t.Errorf("expected serviceAccountName = '%s', got '%s'", serviceAccount, sa)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingService, nil
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image, ServiceAccount: serviceAccount}
|
||||
if err := Upgrade(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 3 {
|
||||
t.Errorf("unexpected actions: %v, expected 3 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpgrade_serviceNotFound(t *testing.T) {
|
||||
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
|
||||
|
||||
existingDeployment, _ := deployment(&Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: "imageToReplace",
|
||||
UseCanary: false,
|
||||
})
|
||||
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingDeployment, nil
|
||||
})
|
||||
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
|
||||
i := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if i != image {
|
||||
t.Errorf("expected image = '%s', got '%s'", image, i)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, apierrors.NewNotFound(v1.Resource("services"), "1")
|
||||
})
|
||||
fc.AddReactor("create", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.CreateAction).GetObject().(*v1.Service)
|
||||
n := obj.ObjectMeta.Namespace
|
||||
if n != v1.NamespaceDefault {
|
||||
t.Errorf("expected namespace = '%s', got '%s'", v1.NamespaceDefault, n)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image}
|
||||
if err := Upgrade(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 4 {
|
||||
t.Errorf("unexpected actions: %v, expected 4 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUgrade_newerVersion(t *testing.T) {
|
||||
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
|
||||
serviceAccount := "newServiceAccount"
|
||||
existingDeployment, _ := deployment(&Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: "imageToReplace:v100.5.0",
|
||||
ServiceAccount: "serviceAccountToReplace",
|
||||
UseCanary: false,
|
||||
})
|
||||
existingService := service(v1.NamespaceDefault)
|
||||
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingDeployment, nil
|
||||
})
|
||||
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
|
||||
i := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if i != image {
|
||||
t.Errorf("expected image = '%s', got '%s'", image, i)
|
||||
}
|
||||
sa := obj.Spec.Template.Spec.ServiceAccountName
|
||||
if sa != serviceAccount {
|
||||
t.Errorf("expected serviceAccountName = '%s', got '%s'", serviceAccount, sa)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingService, nil
|
||||
})
|
||||
|
||||
opts := &Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: image,
|
||||
ServiceAccount: serviceAccount,
|
||||
ForceUpgrade: false,
|
||||
}
|
||||
if err := Upgrade(fc, opts); err == nil {
|
||||
t.Errorf("Expected error because the deployed version is newer")
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 1 {
|
||||
t.Errorf("unexpected actions: %v, expected 1 action got %d", actions, len(actions))
|
||||
}
|
||||
|
||||
opts = &Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: image,
|
||||
ServiceAccount: serviceAccount,
|
||||
ForceUpgrade: true,
|
||||
}
|
||||
if err := Upgrade(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 4 {
|
||||
t.Errorf("unexpected actions: %v, expected 4 action got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpgrade_identical(t *testing.T) {
|
||||
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
|
||||
serviceAccount := "newServiceAccount"
|
||||
existingDeployment, _ := deployment(&Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: "imageToReplace:v2.0.0",
|
||||
ServiceAccount: "serviceAccountToReplace",
|
||||
UseCanary: false,
|
||||
})
|
||||
existingService := service(v1.NamespaceDefault)
|
||||
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingDeployment, nil
|
||||
})
|
||||
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
|
||||
i := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if i != image {
|
||||
t.Errorf("expected image = '%s', got '%s'", image, i)
|
||||
}
|
||||
sa := obj.Spec.Template.Spec.ServiceAccountName
|
||||
if sa != serviceAccount {
|
||||
t.Errorf("expected serviceAccountName = '%s', got '%s'", serviceAccount, sa)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingService, nil
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image, ServiceAccount: serviceAccount}
|
||||
if err := Upgrade(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 3 {
|
||||
t.Errorf("unexpected actions: %v, expected 3 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpgrade_canaryClient(t *testing.T) {
|
||||
image := "gcr.io/kubernetes-helm/tiller:canary"
|
||||
serviceAccount := "newServiceAccount"
|
||||
existingDeployment, _ := deployment(&Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: "imageToReplace:v1.0.0",
|
||||
ServiceAccount: "serviceAccountToReplace",
|
||||
UseCanary: false,
|
||||
})
|
||||
existingService := service(v1.NamespaceDefault)
|
||||
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingDeployment, nil
|
||||
})
|
||||
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
|
||||
i := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if i != image {
|
||||
t.Errorf("expected image = '%s', got '%s'", image, i)
|
||||
}
|
||||
sa := obj.Spec.Template.Spec.ServiceAccountName
|
||||
if sa != serviceAccount {
|
||||
t.Errorf("expected serviceAccountName = '%s', got '%s'", serviceAccount, sa)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingService, nil
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image, ServiceAccount: serviceAccount}
|
||||
if err := Upgrade(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 3 {
|
||||
t.Errorf("unexpected actions: %v, expected 3 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpgrade_canaryServer(t *testing.T) {
|
||||
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
|
||||
serviceAccount := "newServiceAccount"
|
||||
existingDeployment, _ := deployment(&Options{
|
||||
Namespace: v1.NamespaceDefault,
|
||||
ImageSpec: "imageToReplace:canary",
|
||||
ServiceAccount: "serviceAccountToReplace",
|
||||
UseCanary: false,
|
||||
})
|
||||
existingService := service(v1.NamespaceDefault)
|
||||
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingDeployment, nil
|
||||
})
|
||||
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
|
||||
i := obj.Spec.Template.Spec.Containers[0].Image
|
||||
if i != image {
|
||||
t.Errorf("expected image = '%s', got '%s'", image, i)
|
||||
}
|
||||
sa := obj.Spec.Template.Spec.ServiceAccountName
|
||||
if sa != serviceAccount {
|
||||
t.Errorf("expected serviceAccountName = '%s', got '%s'", serviceAccount, sa)
|
||||
}
|
||||
return true, obj, nil
|
||||
})
|
||||
fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, existingService, nil
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: v1.NamespaceDefault, ImageSpec: image, ServiceAccount: serviceAccount}
|
||||
if err := Upgrade(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 3 {
|
||||
t.Errorf("unexpected actions: %v, expected 3 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func tlsTestFile(t *testing.T, path string) string {
|
||||
const tlsTestDir = "../../../testdata"
|
||||
path = filepath.Join(tlsTestDir, path)
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
t.Fatalf("tls test file %s does not exist", path)
|
||||
}
|
||||
return path
|
||||
}
|
||||
func TestDeploymentManifest_WithNodeSelectors(t *testing.T) {
|
||||
tests := []struct {
|
||||
opts Options
|
||||
name string
|
||||
expect map[string]interface{}
|
||||
}{
|
||||
{
|
||||
Options{Namespace: v1.NamespaceDefault, NodeSelectors: "app=tiller"},
|
||||
"nodeSelector app=tiller",
|
||||
map[string]interface{}{"app": "tiller"},
|
||||
},
|
||||
{
|
||||
Options{Namespace: v1.NamespaceDefault, NodeSelectors: "app=tiller,helm=rocks"},
|
||||
"nodeSelector app=tiller, helm=rocks",
|
||||
map[string]interface{}{"app": "tiller", "helm": "rocks"},
|
||||
},
|
||||
// note: nodeSelector key and value are strings
|
||||
{
|
||||
Options{Namespace: v1.NamespaceDefault, NodeSelectors: "app=tiller,minCoolness=1"},
|
||||
"nodeSelector app=tiller, helm=rocks",
|
||||
map[string]interface{}{"app": "tiller", "minCoolness": "1"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
o, err := DeploymentManifest(&tt.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error %q", tt.name, err)
|
||||
}
|
||||
|
||||
var d v1beta1.Deployment
|
||||
if err := yaml.Unmarshal([]byte(o), &d); err != nil {
|
||||
t.Fatalf("%s: error %q", tt.name, err)
|
||||
}
|
||||
// Verify that environment variables in Deployment reflect the use of TLS being enabled.
|
||||
got := d.Spec.Template.Spec.NodeSelector
|
||||
for k, v := range tt.expect {
|
||||
if got[k] != v {
|
||||
t.Errorf("%s: expected nodeSelector value %q, got %q", tt.name, tt.expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestDeploymentManifest_WithSetValues(t *testing.T) {
|
||||
tests := []struct {
|
||||
opts Options
|
||||
name string
|
||||
expectPath string
|
||||
expect interface{}
|
||||
}{
|
||||
{
|
||||
Options{Namespace: v1.NamespaceDefault, Values: []string{"spec.template.spec.nodeselector.app=tiller"}},
|
||||
"setValues spec.template.spec.nodeSelector.app=tiller",
|
||||
"spec.template.spec.nodeSelector.app",
|
||||
"tiller",
|
||||
},
|
||||
{
|
||||
Options{Namespace: v1.NamespaceDefault, Values: []string{"spec.replicas=2"}},
|
||||
"setValues spec.replicas=2",
|
||||
"spec.replicas",
|
||||
2,
|
||||
},
|
||||
{
|
||||
Options{Namespace: v1.NamespaceDefault, Values: []string{"spec.template.spec.activedeadlineseconds=120"}},
|
||||
"setValues spec.template.spec.activedeadlineseconds=120",
|
||||
"spec.template.spec.activeDeadlineSeconds",
|
||||
120,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
o, err := DeploymentManifest(&tt.opts)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error %q", tt.name, err)
|
||||
}
|
||||
values, err := chartutil.ReadValues([]byte(o))
|
||||
if err != nil {
|
||||
t.Errorf("Error converting Deployment manifest to Values: %s", err)
|
||||
}
|
||||
// path value
|
||||
pv, err := values.PathValue(tt.expectPath)
|
||||
if err != nil {
|
||||
t.Errorf("Error retrieving path value from Deployment Values: %s", err)
|
||||
}
|
||||
|
||||
// convert our expected value to match the result type for comparison
|
||||
ev := tt.expect
|
||||
switch pvt := pv.(type) {
|
||||
case float64:
|
||||
floatType := reflect.TypeOf(float64(0))
|
||||
v := reflect.ValueOf(ev)
|
||||
v = reflect.Indirect(v)
|
||||
if !v.Type().ConvertibleTo(floatType) {
|
||||
t.Fatalf("Error converting expected value %v to float64", v.Type())
|
||||
}
|
||||
fv := v.Convert(floatType)
|
||||
if fv.Float() != pvt {
|
||||
t.Errorf("%s: expected value %q, got %q", tt.name, tt.expect, pv)
|
||||
}
|
||||
default:
|
||||
if pv != tt.expect {
|
||||
t.Errorf("%s: expected value %q, got %q", tt.name, tt.expect, pv)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
164
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/options.go
generated
vendored
Normal file
164
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/options.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 "k8s.io/helm/cmd/helm/installer"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/helm/pkg/strvals"
|
||||
"k8s.io/helm/pkg/version"
|
||||
)
|
||||
|
||||
const defaultImage = "gcr.io/kubernetes-helm/tiller"
|
||||
|
||||
// Options control how to install Tiller into a cluster, upgrade, and uninstall Tiller from a cluster.
|
||||
type Options struct {
|
||||
// EnableTLS instructs Tiller to serve with TLS enabled.
|
||||
//
|
||||
// Implied by VerifyTLS. If set the TLSKey and TLSCert are required.
|
||||
EnableTLS bool
|
||||
|
||||
// VerifyTLS instructs Tiller to serve with TLS enabled verify remote certificates.
|
||||
//
|
||||
// If set TLSKey, TLSCert, TLSCaCert are required.
|
||||
VerifyTLS bool
|
||||
|
||||
// UseCanary indicates that Tiller should deploy using the latest Tiller image.
|
||||
UseCanary bool
|
||||
|
||||
// Namespace is the Kubernetes namespace to use to deploy Tiller.
|
||||
Namespace string
|
||||
|
||||
// ServiceAccount is the Kubernetes service account to add to Tiller.
|
||||
ServiceAccount string
|
||||
|
||||
// Force allows to force upgrading tiller if deployed version is greater than current version
|
||||
ForceUpgrade bool
|
||||
|
||||
// ImageSpec indentifies the image Tiller will use when deployed.
|
||||
//
|
||||
// Valid if and only if UseCanary is false.
|
||||
ImageSpec string
|
||||
|
||||
// TLSKeyFile identifies the file containing the pem encoded TLS private
|
||||
// key Tiller should use.
|
||||
//
|
||||
// Required and valid if and only if EnableTLS or VerifyTLS is set.
|
||||
TLSKeyFile string
|
||||
|
||||
// TLSCertFile identifies the file containing the pem encoded TLS
|
||||
// certificate Tiller should use.
|
||||
//
|
||||
// Required and valid if and only if EnableTLS or VerifyTLS is set.
|
||||
TLSCertFile string
|
||||
|
||||
// TLSCaCertFile identifies the file containing the pem encoded TLS CA
|
||||
// certificate Tiller should use to verify remotes certificates.
|
||||
//
|
||||
// Required and valid if and only if VerifyTLS is set.
|
||||
TLSCaCertFile string
|
||||
|
||||
// EnableHostNetwork installs Tiller with net=host.
|
||||
EnableHostNetwork bool
|
||||
|
||||
// MaxHistory sets the maximum number of release versions stored per release.
|
||||
//
|
||||
// Less than or equal to zero means no limit.
|
||||
MaxHistory int
|
||||
|
||||
// Replicas sets the amount of Tiller replicas to start
|
||||
//
|
||||
// Less than or equals to 1 means 1.
|
||||
Replicas int
|
||||
|
||||
// NodeSelectors determine which nodes Tiller can land on.
|
||||
NodeSelectors string
|
||||
|
||||
// Output dumps the Tiller manifest in the specified format (e.g. JSON) but skips Helm/Tiller installation.
|
||||
Output OutputFormat
|
||||
|
||||
// Set merges additional values into the Tiller Deployment manifest.
|
||||
Values []string
|
||||
}
|
||||
|
||||
func (opts *Options) selectImage() string {
|
||||
switch {
|
||||
case opts.UseCanary:
|
||||
return defaultImage + ":canary"
|
||||
case opts.ImageSpec == "":
|
||||
return fmt.Sprintf("%s:%s", defaultImage, version.Version)
|
||||
default:
|
||||
return opts.ImageSpec
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *Options) pullPolicy() v1.PullPolicy {
|
||||
if opts.UseCanary {
|
||||
return v1.PullAlways
|
||||
}
|
||||
return v1.PullIfNotPresent
|
||||
}
|
||||
|
||||
func (opts *Options) getReplicas() *int32 {
|
||||
replicas := int32(1)
|
||||
if opts.Replicas > 1 {
|
||||
replicas = int32(opts.Replicas)
|
||||
}
|
||||
return &replicas
|
||||
}
|
||||
|
||||
func (opts *Options) tls() bool { return opts.EnableTLS || opts.VerifyTLS }
|
||||
|
||||
// valuesMap returns user set values in map format
|
||||
func (opts *Options) valuesMap(m map[string]interface{}) (map[string]interface{}, error) {
|
||||
for _, skv := range opts.Values {
|
||||
if err := strvals.ParseInto(skv, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// OutputFormat defines valid values for init output (json, yaml)
|
||||
type OutputFormat string
|
||||
|
||||
// String returns the string value of the OutputFormat
|
||||
func (f *OutputFormat) String() string {
|
||||
return string(*f)
|
||||
}
|
||||
|
||||
// Type returns the string value of the OutputFormat
|
||||
func (f *OutputFormat) Type() string {
|
||||
return "OutputFormat"
|
||||
}
|
||||
|
||||
const (
|
||||
fmtJSON OutputFormat = "json"
|
||||
fmtYAML OutputFormat = "yaml"
|
||||
)
|
||||
|
||||
// Set validates and sets the value of the OutputFormat
|
||||
func (f *OutputFormat) Set(s string) error {
|
||||
for _, of := range []OutputFormat{fmtJSON, fmtYAML} {
|
||||
if s == string(of) {
|
||||
*f = of
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("unknown output format %q", s)
|
||||
}
|
71
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/uninstall.go
generated
vendored
Normal file
71
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/uninstall.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 "k8s.io/helm/cmd/helm/installer"
|
||||
|
||||
import (
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
)
|
||||
|
||||
const (
|
||||
deploymentName = "tiller-deploy"
|
||||
serviceName = "tiller-deploy"
|
||||
secretName = "tiller-secret"
|
||||
)
|
||||
|
||||
// Uninstall uses Kubernetes client to uninstall Tiller.
|
||||
func Uninstall(client internalclientset.Interface, opts *Options) error {
|
||||
if err := deleteService(client.Core(), opts.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := deleteDeployment(client, opts.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
return deleteSecret(client.Core(), opts.Namespace)
|
||||
}
|
||||
|
||||
// deleteService deletes the Tiller Service resource
|
||||
func deleteService(client coreclient.ServicesGetter, namespace string) error {
|
||||
err := client.Services(namespace).Delete(serviceName, &metav1.DeleteOptions{})
|
||||
return ingoreNotFound(err)
|
||||
}
|
||||
|
||||
// deleteDeployment deletes the Tiller Deployment resource
|
||||
// We need to use the reaper instead of the kube API because GC for deployment dependents
|
||||
// is not yet supported at the k8s server level (<= 1.5)
|
||||
func deleteDeployment(client internalclientset.Interface, namespace string) error {
|
||||
reaper, _ := kubectl.ReaperFor(extensions.Kind("Deployment"), client)
|
||||
err := reaper.Stop(namespace, deploymentName, 0, nil)
|
||||
return ingoreNotFound(err)
|
||||
}
|
||||
|
||||
// deleteSecret deletes the Tiller Secret resource
|
||||
func deleteSecret(client coreclient.SecretsGetter, namespace string) error {
|
||||
err := client.Secrets(namespace).Delete(secretName, &metav1.DeleteOptions{})
|
||||
return ingoreNotFound(err)
|
||||
}
|
||||
|
||||
func ingoreNotFound(err error) error {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
88
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/uninstall_test.go
generated
vendored
Normal file
88
src/vendor/github.com/kubernetes/helm/cmd/helm/installer/uninstall_test.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 "k8s.io/helm/cmd/helm/installer"
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
testcore "k8s.io/client-go/testing"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||
)
|
||||
|
||||
func TestUninstall(t *testing.T) {
|
||||
fc := &fake.Clientset{}
|
||||
opts := &Options{Namespace: core.NamespaceDefault}
|
||||
if err := Uninstall(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 7 {
|
||||
t.Errorf("unexpected actions: %v, expected 7 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUninstall_serviceNotFound(t *testing.T) {
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("delete", "services", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, apierrors.NewNotFound(schema.GroupResource{Resource: "services"}, "1")
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: core.NamespaceDefault}
|
||||
if err := Uninstall(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 7 {
|
||||
t.Errorf("unexpected actions: %v, expected 7 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUninstall_deploymentNotFound(t *testing.T) {
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("delete", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, apierrors.NewNotFound(core.Resource("deployments"), "1")
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: core.NamespaceDefault}
|
||||
if err := Uninstall(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 7 {
|
||||
t.Errorf("unexpected actions: %v, expected 7 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUninstall_secretNotFound(t *testing.T) {
|
||||
fc := &fake.Clientset{}
|
||||
fc.AddReactor("delete", "secrets", func(action testcore.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, apierrors.NewNotFound(core.Resource("secrets"), "1")
|
||||
})
|
||||
|
||||
opts := &Options{Namespace: core.NamespaceDefault}
|
||||
if err := Uninstall(fc, opts); err != nil {
|
||||
t.Errorf("unexpected error: %#+v", err)
|
||||
}
|
||||
|
||||
if actions := fc.Actions(); len(actions) != 7 {
|
||||
t.Errorf("unexpected actions: %v, expect 7 actions got %d", actions, len(actions))
|
||||
}
|
||||
}
|
208
src/vendor/github.com/kubernetes/helm/cmd/helm/lint.go
generated
vendored
Normal file
208
src/vendor/github.com/kubernetes/helm/cmd/helm/lint.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/lint"
|
||||
"k8s.io/helm/pkg/lint/support"
|
||||
"k8s.io/helm/pkg/strvals"
|
||||
)
|
||||
|
||||
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.
|
||||
`
|
||||
|
||||
type lintCmd struct {
|
||||
valueFiles valueFiles
|
||||
values []string
|
||||
sValues []string
|
||||
namespace string
|
||||
strict bool
|
||||
paths []string
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func newLintCmd(out io.Writer) *cobra.Command {
|
||||
l := &lintCmd{
|
||||
paths: []string{"."},
|
||||
out: out,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "lint [flags] PATH",
|
||||
Short: "examines a chart for possible issues",
|
||||
Long: longLintHelp,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
l.paths = args
|
||||
}
|
||||
return l.run()
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().VarP(&l.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
|
||||
cmd.Flags().StringArrayVar(&l.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
cmd.Flags().StringArrayVar(&l.sValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
cmd.Flags().StringVar(&l.namespace, "namespace", "default", "namespace to install the release into (only used if --install is set)")
|
||||
cmd.Flags().BoolVar(&l.strict, "strict", false, "fail on lint warnings")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
var errLintNoChart = errors.New("No chart found for linting (missing Chart.yaml)")
|
||||
|
||||
func (l *lintCmd) run() error {
|
||||
var lowestTolerance int
|
||||
if l.strict {
|
||||
lowestTolerance = support.WarningSev
|
||||
} else {
|
||||
lowestTolerance = support.ErrorSev
|
||||
}
|
||||
|
||||
// Get the raw values
|
||||
rvals, err := l.vals()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var total int
|
||||
var failures int
|
||||
for _, path := range l.paths {
|
||||
if linter, err := lintChart(path, rvals, l.namespace, l.strict); err != nil {
|
||||
fmt.Println("==> Skipping", path)
|
||||
fmt.Println(err)
|
||||
if err == errLintNoChart {
|
||||
failures = failures + 1
|
||||
}
|
||||
} else {
|
||||
fmt.Println("==> Linting", path)
|
||||
|
||||
if len(linter.Messages) == 0 {
|
||||
fmt.Println("Lint OK")
|
||||
}
|
||||
|
||||
for _, msg := range linter.Messages {
|
||||
fmt.Println(msg)
|
||||
}
|
||||
|
||||
total = total + 1
|
||||
if linter.HighestSeverity >= lowestTolerance {
|
||||
failures = failures + 1
|
||||
}
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("%d chart(s) linted", total)
|
||||
if failures > 0 {
|
||||
return fmt.Errorf("%s, %d chart(s) failed", msg, failures)
|
||||
}
|
||||
|
||||
fmt.Fprintf(l.out, "%s, no failures\n", msg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func lintChart(path string, vals []byte, namespace string, strict bool) (support.Linter, error) {
|
||||
var chartPath string
|
||||
linter := support.Linter{}
|
||||
|
||||
if strings.HasSuffix(path, ".tgz") {
|
||||
tempDir, err := ioutil.TempDir("", "helm-lint")
|
||||
if err != nil {
|
||||
return linter, err
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return linter, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if err = chartutil.Expand(tempDir, file); err != nil {
|
||||
return linter, err
|
||||
}
|
||||
|
||||
lastHyphenIndex := strings.LastIndex(filepath.Base(path), "-")
|
||||
if lastHyphenIndex <= 0 {
|
||||
return linter, fmt.Errorf("unable to parse chart archive %q, missing '-'", filepath.Base(path))
|
||||
}
|
||||
base := filepath.Base(path)[:lastHyphenIndex]
|
||||
chartPath = filepath.Join(tempDir, base)
|
||||
} else {
|
||||
chartPath = path
|
||||
}
|
||||
|
||||
// Guard: Error out of this is not a chart.
|
||||
if _, err := os.Stat(filepath.Join(chartPath, "Chart.yaml")); err != nil {
|
||||
return linter, errLintNoChart
|
||||
}
|
||||
|
||||
return lint.All(chartPath, vals, namespace, strict), nil
|
||||
}
|
||||
|
||||
func (l *lintCmd) vals() ([]byte, error) {
|
||||
base := map[string]interface{}{}
|
||||
|
||||
// User specified a values files via -f/--values
|
||||
for _, filePath := range l.valueFiles {
|
||||
currentMap := map[string]interface{}{}
|
||||
bytes, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(bytes, ¤tMap); err != nil {
|
||||
return []byte{}, fmt.Errorf("failed to parse %s: %s", filePath, err)
|
||||
}
|
||||
// Merge with the previous map
|
||||
base = mergeValues(base, currentMap)
|
||||
}
|
||||
|
||||
// User specified a value via --set
|
||||
for _, value := range l.values {
|
||||
if err := strvals.ParseInto(value, base); err != nil {
|
||||
return []byte{}, fmt.Errorf("failed parsing --set data: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// User specified a value via --set-string
|
||||
for _, value := range l.sValues {
|
||||
if err := strvals.ParseIntoString(value, base); err != nil {
|
||||
return []byte{}, fmt.Errorf("failed parsing --set-string data: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return yaml.Marshal(base)
|
||||
}
|
54
src/vendor/github.com/kubernetes/helm/cmd/helm/lint_test.go
generated
vendored
Normal file
54
src/vendor/github.com/kubernetes/helm/cmd/helm/lint_test.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
)
|
||||
|
||||
var (
|
||||
values = []byte{}
|
||||
namespace = "testNamespace"
|
||||
strict = false
|
||||
archivedChartPath = "testdata/testcharts/compressedchart-0.1.0.tgz"
|
||||
archivedChartPathWithHyphens = "testdata/testcharts/compressedchart-with-hyphens-0.1.0.tgz"
|
||||
invalidArchivedChartPath = "testdata/testcharts/invalidcompressedchart0.1.0.tgz"
|
||||
chartDirPath = "testdata/testcharts/decompressedchart/"
|
||||
chartMissingManifest = "testdata/testcharts/chart-missing-manifest"
|
||||
)
|
||||
|
||||
func TestLintChart(t *testing.T) {
|
||||
if _, err := lintChart(chartDirPath, values, namespace, strict); err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
|
||||
if _, err := lintChart(archivedChartPath, values, namespace, strict); err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
|
||||
if _, err := lintChart(archivedChartPathWithHyphens, values, namespace, strict); err != nil {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
|
||||
if _, err := lintChart(invalidArchivedChartPath, values, namespace, strict); err == nil {
|
||||
t.Errorf("Expected a chart parsing error")
|
||||
}
|
||||
|
||||
if _, err := lintChart(chartMissingManifest, values, namespace, strict); err == nil {
|
||||
t.Errorf("Expected a chart parsing error")
|
||||
}
|
||||
}
|
254
src/vendor/github.com/kubernetes/helm/cmd/helm/list.go
generated
vendored
Normal file
254
src/vendor/github.com/kubernetes/helm/cmd/helm/list.go
generated
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
"k8s.io/helm/pkg/proto/hapi/services"
|
||||
"k8s.io/helm/pkg/timeconv"
|
||||
)
|
||||
|
||||
var listHelp = `
|
||||
This command lists all of the releases.
|
||||
|
||||
By default, it lists only releases that are deployed or failed. Flags like
|
||||
'--deleted' and '--all' will alter this behavior. Such flags can be combined:
|
||||
'--deleted --failed'.
|
||||
|
||||
By default, items are sorted alphabetically. Use the '-d' flag to sort by
|
||||
release date.
|
||||
|
||||
If an argument 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 '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.
|
||||
`
|
||||
|
||||
type listCmd struct {
|
||||
filter string
|
||||
short bool
|
||||
limit int
|
||||
offset string
|
||||
byDate bool
|
||||
sortDesc bool
|
||||
out io.Writer
|
||||
all bool
|
||||
deleted bool
|
||||
deleting bool
|
||||
deployed bool
|
||||
failed bool
|
||||
namespace string
|
||||
superseded bool
|
||||
pending bool
|
||||
client helm.Interface
|
||||
colWidth uint
|
||||
}
|
||||
|
||||
func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
list := &listCmd{
|
||||
out: out,
|
||||
client: client,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list [flags] [FILTER]",
|
||||
Short: "list releases",
|
||||
Long: listHelp,
|
||||
Aliases: []string{"ls"},
|
||||
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
list.filter = strings.Join(args, " ")
|
||||
}
|
||||
if list.client == nil {
|
||||
list.client = newClient()
|
||||
}
|
||||
return list.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVarP(&list.short, "short", "q", false, "output short (quiet) listing format")
|
||||
f.BoolVarP(&list.byDate, "date", "d", false, "sort by release date")
|
||||
f.BoolVarP(&list.sortDesc, "reverse", "r", false, "reverse the sort order")
|
||||
f.IntVarP(&list.limit, "max", "m", 256, "maximum number of releases to fetch")
|
||||
f.StringVarP(&list.offset, "offset", "o", "", "next release name in the list, used to offset from start value")
|
||||
f.BoolVarP(&list.all, "all", "a", false, "show all releases, not just the ones marked DEPLOYED")
|
||||
f.BoolVar(&list.deleted, "deleted", false, "show deleted releases")
|
||||
f.BoolVar(&list.deleting, "deleting", false, "show releases that are currently being deleted")
|
||||
f.BoolVar(&list.deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
|
||||
f.BoolVar(&list.failed, "failed", false, "show failed releases")
|
||||
f.BoolVar(&list.pending, "pending", false, "show pending releases")
|
||||
f.StringVar(&list.namespace, "namespace", "", "show releases within a specific namespace")
|
||||
f.UintVar(&list.colWidth, "col-width", 60, "specifies the max column width of output")
|
||||
|
||||
// TODO: Do we want this as a feature of 'helm list'?
|
||||
//f.BoolVar(&list.superseded, "history", true, "show historical releases")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (l *listCmd) run() error {
|
||||
sortBy := services.ListSort_NAME
|
||||
if l.byDate {
|
||||
sortBy = services.ListSort_LAST_RELEASED
|
||||
}
|
||||
|
||||
sortOrder := services.ListSort_ASC
|
||||
if l.sortDesc {
|
||||
sortOrder = services.ListSort_DESC
|
||||
}
|
||||
|
||||
stats := l.statusCodes()
|
||||
|
||||
res, err := l.client.ListReleases(
|
||||
helm.ReleaseListLimit(l.limit),
|
||||
helm.ReleaseListOffset(l.offset),
|
||||
helm.ReleaseListFilter(l.filter),
|
||||
helm.ReleaseListSort(int32(sortBy)),
|
||||
helm.ReleaseListOrder(int32(sortOrder)),
|
||||
helm.ReleaseListStatuses(stats),
|
||||
helm.ReleaseListNamespace(l.namespace),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
|
||||
if len(res.GetReleases()) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if res.Next != "" && !l.short {
|
||||
fmt.Fprintf(l.out, "\tnext: %s\n", res.Next)
|
||||
}
|
||||
|
||||
rels := filterList(res.Releases)
|
||||
|
||||
if l.short {
|
||||
for _, r := range rels {
|
||||
fmt.Fprintln(l.out, r.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintln(l.out, formatList(rels, l.colWidth))
|
||||
return nil
|
||||
}
|
||||
|
||||
// filterList returns a list scrubbed of old releases.
|
||||
func filterList(rels []*release.Release) []*release.Release {
|
||||
idx := map[string]int32{}
|
||||
|
||||
for _, r := range rels {
|
||||
name, version := r.GetName(), r.GetVersion()
|
||||
if max, ok := idx[name]; ok {
|
||||
// check if we have a greater version already
|
||||
if max > version {
|
||||
continue
|
||||
}
|
||||
}
|
||||
idx[name] = version
|
||||
}
|
||||
|
||||
uniq := make([]*release.Release, 0, len(idx))
|
||||
for _, r := range rels {
|
||||
if idx[r.GetName()] == r.GetVersion() {
|
||||
uniq = append(uniq, r)
|
||||
}
|
||||
}
|
||||
return uniq
|
||||
}
|
||||
|
||||
// statusCodes gets the list of status codes that are to be included in the results.
|
||||
func (l *listCmd) statusCodes() []release.Status_Code {
|
||||
if l.all {
|
||||
return []release.Status_Code{
|
||||
release.Status_UNKNOWN,
|
||||
release.Status_DEPLOYED,
|
||||
release.Status_DELETED,
|
||||
release.Status_DELETING,
|
||||
release.Status_FAILED,
|
||||
release.Status_PENDING_INSTALL,
|
||||
release.Status_PENDING_UPGRADE,
|
||||
release.Status_PENDING_ROLLBACK,
|
||||
}
|
||||
}
|
||||
status := []release.Status_Code{}
|
||||
if l.deployed {
|
||||
status = append(status, release.Status_DEPLOYED)
|
||||
}
|
||||
if l.deleted {
|
||||
status = append(status, release.Status_DELETED)
|
||||
}
|
||||
if l.deleting {
|
||||
status = append(status, release.Status_DELETING)
|
||||
}
|
||||
if l.failed {
|
||||
status = append(status, release.Status_FAILED)
|
||||
}
|
||||
if l.superseded {
|
||||
status = append(status, release.Status_SUPERSEDED)
|
||||
}
|
||||
if l.pending {
|
||||
status = append(status, release.Status_PENDING_INSTALL, release.Status_PENDING_UPGRADE, release.Status_PENDING_ROLLBACK)
|
||||
}
|
||||
|
||||
// Default case.
|
||||
if len(status) == 0 {
|
||||
status = append(status, release.Status_DEPLOYED, release.Status_FAILED)
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func formatList(rels []*release.Release, colWidth uint) string {
|
||||
table := uitable.New()
|
||||
|
||||
table.MaxColWidth = colWidth
|
||||
table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "NAMESPACE")
|
||||
for _, r := range rels {
|
||||
md := r.GetChart().GetMetadata()
|
||||
c := fmt.Sprintf("%s-%s", md.GetName(), md.GetVersion())
|
||||
t := "-"
|
||||
if tspb := r.GetInfo().GetLastDeployed(); tspb != nil {
|
||||
t = timeconv.String(tspb)
|
||||
}
|
||||
s := r.GetInfo().GetStatus().GetCode().String()
|
||||
v := r.GetVersion()
|
||||
n := r.GetNamespace()
|
||||
table.AddRow(r.GetName(), v, t, s, c, n)
|
||||
}
|
||||
return table.String()
|
||||
}
|
128
src/vendor/github.com/kubernetes/helm/cmd/helm/list_test.go
generated
vendored
Normal file
128
src/vendor/github.com/kubernetes/helm/cmd/helm/list_test.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestListCmd(t *testing.T) {
|
||||
tests := []releaseCase{
|
||||
{
|
||||
name: "with a release",
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"}),
|
||||
},
|
||||
expected: "thomas-guide",
|
||||
},
|
||||
{
|
||||
name: "list",
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas"}),
|
||||
},
|
||||
expected: "NAME \tREVISION\tUPDATED \tSTATUS \tCHART \tNAMESPACE\natlas\t1 \t(.*)\tDEPLOYED\tfoo-0.1.0-beta.1\tdefault \n",
|
||||
},
|
||||
{
|
||||
name: "list, one deployed, one failed",
|
||||
flags: []string{"-q"},
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_FAILED}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}),
|
||||
},
|
||||
expected: "thomas-guide\natlas-guide",
|
||||
},
|
||||
{
|
||||
name: "with a release, multiple flags",
|
||||
flags: []string{"--deleted", "--deployed", "--failed", "-q"},
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_DELETED}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}),
|
||||
},
|
||||
// Note: We're really only testing that the flags parsed correctly. Which results are returned
|
||||
// depends on the backend. And until pkg/helm is done, we can't mock this.
|
||||
expected: "thomas-guide\natlas-guide",
|
||||
},
|
||||
{
|
||||
name: "with a release, multiple flags",
|
||||
flags: []string{"--all", "-q"},
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_DELETED}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}),
|
||||
},
|
||||
// See note on previous test.
|
||||
expected: "thomas-guide\natlas-guide",
|
||||
},
|
||||
{
|
||||
name: "with a release, multiple flags, deleting",
|
||||
flags: []string{"--all", "-q"},
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_DELETING}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}),
|
||||
},
|
||||
// See note on previous test.
|
||||
expected: "thomas-guide\natlas-guide",
|
||||
},
|
||||
{
|
||||
name: "namespace defined, multiple flags",
|
||||
flags: []string{"--all", "-q", "--namespace test123"},
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", Namespace: "test123"}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", Namespace: "test321"}),
|
||||
},
|
||||
// See note on previous test.
|
||||
expected: "thomas-guide",
|
||||
},
|
||||
{
|
||||
name: "with a pending release, multiple flags",
|
||||
flags: []string{"--all", "-q"},
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_PENDING_INSTALL}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}),
|
||||
},
|
||||
expected: "thomas-guide\natlas-guide",
|
||||
},
|
||||
{
|
||||
name: "with a pending release, pending flag",
|
||||
flags: []string{"--pending", "-q"},
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_PENDING_INSTALL}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "wild-idea", StatusCode: release.Status_PENDING_UPGRADE}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-maps", StatusCode: release.Status_PENDING_ROLLBACK}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}),
|
||||
},
|
||||
expected: "thomas-guide\nwild-idea\ncrazy-maps",
|
||||
},
|
||||
{
|
||||
name: "with old releases",
|
||||
rels: []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"}),
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide", StatusCode: release.Status_FAILED}),
|
||||
},
|
||||
expected: "thomas-guide",
|
||||
},
|
||||
}
|
||||
|
||||
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newListCmd(c, out)
|
||||
})
|
||||
}
|
160
src/vendor/github.com/kubernetes/helm/cmd/helm/load_plugins.go
generated
vendored
Normal file
160
src/vendor/github.com/kubernetes/helm/cmd/helm/load_plugins.go
generated
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// debug("HELM_PLUGIN_DIRS=%s", settings.PluginDirs())
|
||||
found, err := findPlugins(settings.PluginDirs())
|
||||
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 := plug.PrepareCommand(u)
|
||||
|
||||
prog := exec.Command(main, argv...)
|
||||
prog.Env = os.Environ()
|
||||
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)
|
||||
return fmt.Errorf("plugin %q exited with error", md.Name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
// This passes all the flags to the subcommand.
|
||||
DisableFlagParsing: true,
|
||||
}
|
||||
|
||||
if md.UseTunnel {
|
||||
c.PreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
// Parse the parent flag, but not the local flags.
|
||||
if _, err := processParent(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return setupConnection()
|
||||
}
|
||||
}
|
||||
|
||||
// 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{"--host", "--kube-context", "--home", "--tiller-namespace"}
|
||||
knownArg := func(a string) bool {
|
||||
for _, pre := range kvargs {
|
||||
if strings.HasPrefix(a, pre+"=") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch a := args[i]; a {
|
||||
case "--debug":
|
||||
known = append(known, a)
|
||||
case "--host", "--kube-context", "--home":
|
||||
known = append(known, a, args[i+1])
|
||||
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
|
||||
}
|
237
src/vendor/github.com/kubernetes/helm/cmd/helm/package.go
generated
vendored
Normal file
237
src/vendor/github.com/kubernetes/helm/cmd/helm/package.go
generated
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/proto/hapi/chart"
|
||||
"k8s.io/helm/pkg/provenance"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
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.
|
||||
|
||||
If no path is given, this will look in the present working directory for a
|
||||
Chart.yaml file, and (if found) build the current directory into a chart.
|
||||
|
||||
Versioned chart archives are used by Helm package repositories.
|
||||
`
|
||||
|
||||
type packageCmd struct {
|
||||
save bool
|
||||
sign bool
|
||||
path string
|
||||
key string
|
||||
keyring string
|
||||
version string
|
||||
appVersion string
|
||||
destination string
|
||||
dependencyUpdate bool
|
||||
|
||||
out io.Writer
|
||||
home helmpath.Home
|
||||
}
|
||||
|
||||
func newPackageCmd(out io.Writer) *cobra.Command {
|
||||
pkg := &packageCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "package [flags] [CHART_PATH] [...]",
|
||||
Short: "package a chart directory into a chart archive",
|
||||
Long: packageDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
pkg.home = settings.Home
|
||||
if len(args) == 0 {
|
||||
return fmt.Errorf("need at least one argument, the path to the chart")
|
||||
}
|
||||
if pkg.sign {
|
||||
if pkg.key == "" {
|
||||
return errors.New("--key is required for signing a package")
|
||||
}
|
||||
if pkg.keyring == "" {
|
||||
return errors.New("--keyring is required for signing a package")
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(args); i++ {
|
||||
pkg.path = args[i]
|
||||
if err := pkg.run(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository")
|
||||
f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package")
|
||||
f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true")
|
||||
f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "location of a public keyring")
|
||||
f.StringVar(&pkg.version, "version", "", "set the version on the chart to this semver version")
|
||||
f.StringVar(&pkg.appVersion, "app-version", "", "set the appVersion on the chart to this version")
|
||||
f.StringVarP(&pkg.destination, "destination", "d", ".", "location to write the chart.")
|
||||
f.BoolVarP(&pkg.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (p *packageCmd) run() error {
|
||||
path, err := filepath.Abs(p.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.dependencyUpdate {
|
||||
downloadManager := &downloader.Manager{
|
||||
Out: p.out,
|
||||
ChartPath: path,
|
||||
HelmHome: settings.Home,
|
||||
Keyring: p.keyring,
|
||||
Getters: getter.All(settings),
|
||||
Debug: settings.Debug,
|
||||
}
|
||||
|
||||
if err := downloadManager.Update(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ch, err := chartutil.LoadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If version is set, modify the version.
|
||||
if len(p.version) != 0 {
|
||||
if err := setVersion(ch, p.version); err != nil {
|
||||
return err
|
||||
}
|
||||
debug("Setting version to %s", p.version)
|
||||
}
|
||||
|
||||
if p.appVersion != "" {
|
||||
ch.Metadata.AppVersion = p.appVersion
|
||||
debug("Setting appVersion to %s", p.appVersion)
|
||||
}
|
||||
|
||||
if filepath.Base(path) != ch.Metadata.Name {
|
||||
return fmt.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Metadata.Name)
|
||||
}
|
||||
|
||||
if reqs, err := chartutil.LoadRequirements(ch); err == nil {
|
||||
if err := checkDependencies(ch, reqs); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err != chartutil.ErrRequirementsNotFound {
|
||||
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 {
|
||||
fmt.Fprintf(p.out, "Successfully packaged chart and saved it to: %s\n", name)
|
||||
} else {
|
||||
return fmt.Errorf("Failed to save: %s", err)
|
||||
}
|
||||
|
||||
// Save to $HELM_HOME/local directory. This is second, because we don't want
|
||||
// the case where we saved here, but didn't save to the default destination.
|
||||
if p.save {
|
||||
lr := p.home.LocalRepository()
|
||||
if err := repo.AddChartToLocalRepo(ch, lr); err != nil {
|
||||
return err
|
||||
}
|
||||
debug("Successfully saved %s to %s\n", name, lr)
|
||||
}
|
||||
|
||||
if p.sign {
|
||||
err = p.clearsign(name)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func setVersion(ch *chart.Chart, ver string) error {
|
||||
// Verify that version is a SemVer, 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 *packageCmd) 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
|
||||
}
|
||||
|
||||
debug(sig)
|
||||
|
||||
return ioutil.WriteFile(filename+".prov", []byte(sig), 0755)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
253
src/vendor/github.com/kubernetes/helm/cmd/helm/package_test.go
generated
vendored
Normal file
253
src/vendor/github.com/kubernetes/helm/cmd/helm/package_test.go
generated
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/proto/hapi/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.")
|
||||
}
|
||||
}
|
||||
|
||||
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 --destination toot",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: map[string]string{"destination": "toot"},
|
||||
expect: "",
|
||||
hasfile: "toot/alpine-0.1.0.tgz",
|
||||
},
|
||||
{
|
||||
name: "package --destination does-not-exist",
|
||||
args: []string{"testdata/testcharts/alpine"},
|
||||
flags: map[string]string{"destination": "does-not-exist"},
|
||||
expect: "stat does-not-exist: no such file or directory",
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
}
|
||||
|
||||
// Because these tests are destructive, we run them in a tempdir.
|
||||
origDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tmp, err := ioutil.TempDir("", "helm-package-test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Running tests in %s", tmp)
|
||||
if err := os.Chdir(tmp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := os.Mkdir("toot", 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ensureTestHome(helmpath.Home(tmp), t)
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.Chdir(origDir)
|
||||
os.RemoveAll(tmp)
|
||||
cleanup()
|
||||
}()
|
||||
|
||||
settings.Home = helmpath.Home(tmp)
|
||||
|
||||
for _, tt := range tests {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
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()) {
|
||||
continue
|
||||
}
|
||||
t.Errorf("%q: expected error %q, got %q", tt.name, tt.expect, err)
|
||||
continue
|
||||
}
|
||||
|
||||
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"
|
||||
tmp, _ := ioutil.TempDir("", "helm-package-app-version-")
|
||||
|
||||
thome, err := tempHelmHome(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.RemoveAll(tmp)
|
||||
os.RemoveAll(thome.String())
|
||||
cleanup()
|
||||
}()
|
||||
|
||||
settings.Home = helmpath.Home(thome)
|
||||
|
||||
c := newPackageCmd(&bytes.Buffer{})
|
||||
flags := map[string]string{
|
||||
"destination": tmp,
|
||||
"app-version": expectedAppVersion,
|
||||
}
|
||||
setFlags(c, flags)
|
||||
err = c.RunE(c, []string{"testdata/testcharts/alpine"})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %q", err)
|
||||
}
|
||||
|
||||
chartPath := filepath.Join(tmp, "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 = chartutil.Load(chartPath)
|
||||
if err != nil {
|
||||
t.Errorf("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)
|
||||
}
|
||||
}
|
72
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin.go
generated
vendored
Normal file
72
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const pluginHelp = `
|
||||
Manage client-side Helm plugins.
|
||||
`
|
||||
|
||||
func newPluginCmd(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "plugin",
|
||||
Short: "add, list, or remove Helm plugins",
|
||||
Long: pluginHelp,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newPluginInstallCmd(out),
|
||||
newPluginListCmd(out),
|
||||
newPluginRemoveCmd(out),
|
||||
newPluginUpdateCmd(out),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// runHook will execute a plugin hook.
|
||||
func runHook(p *plugin.Plugin, event string) error {
|
||||
hook := p.Metadata.Hooks.Get(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 fmt.Errorf("plugin %s hook for %q exited with error", event, p.Metadata.Name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
92
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_install.go
generated
vendored
Normal file
92
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_install.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
"k8s.io/helm/pkg/plugin/installer"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type pluginInstallCmd struct {
|
||||
source string
|
||||
version string
|
||||
home helmpath.Home
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
const pluginInstallDesc = `
|
||||
This command allows you to install a plugin from a url to a VCS repo or a local path.
|
||||
|
||||
Example usage:
|
||||
$ helm plugin install https://github.com/technosophos/helm-template
|
||||
`
|
||||
|
||||
func newPluginInstallCmd(out io.Writer) *cobra.Command {
|
||||
pcmd := &pluginInstallCmd{out: out}
|
||||
cmd := &cobra.Command{
|
||||
Use: "install [options] <path|url>...",
|
||||
Short: "install one or more Helm plugins",
|
||||
Long: pluginInstallDesc,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return pcmd.complete(args)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return pcmd.run()
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVar(&pcmd.version, "version", "", "specify a version constraint. If this is not specified, the latest version is installed")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (pcmd *pluginInstallCmd) complete(args []string) error {
|
||||
if err := checkArgsLength(len(args), "plugin"); err != nil {
|
||||
return err
|
||||
}
|
||||
pcmd.source = args[0]
|
||||
pcmd.home = settings.Home
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pcmd *pluginInstallCmd) run() error {
|
||||
installer.Debug = settings.Debug
|
||||
|
||||
i, err := installer.NewForSource(pcmd.source, pcmd.version, pcmd.home)
|
||||
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(pcmd.out, "Installed plugin: %s\n", p.Metadata.Name)
|
||||
return nil
|
||||
}
|
60
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_list.go
generated
vendored
Normal file
60
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_list.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type pluginListCmd struct {
|
||||
home helmpath.Home
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func newPluginListCmd(out io.Writer) *cobra.Command {
|
||||
pcmd := &pluginListCmd{out: out}
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list installed Helm plugins",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
pcmd.home = settings.Home
|
||||
return pcmd.run()
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (pcmd *pluginListCmd) run() error {
|
||||
debug("pluginDirs: %s", settings.PluginDirs())
|
||||
plugins, err := findPlugins(settings.PluginDirs())
|
||||
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(pcmd.out, table)
|
||||
return nil
|
||||
}
|
99
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_remove.go
generated
vendored
Normal file
99
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_remove.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type pluginRemoveCmd struct {
|
||||
names []string
|
||||
home helmpath.Home
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func newPluginRemoveCmd(out io.Writer) *cobra.Command {
|
||||
pcmd := &pluginRemoveCmd{out: out}
|
||||
cmd := &cobra.Command{
|
||||
Use: "remove <plugin>...",
|
||||
Short: "remove one or more Helm plugins",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return pcmd.complete(args)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return pcmd.run()
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (pcmd *pluginRemoveCmd) complete(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("please provide plugin name to remove")
|
||||
}
|
||||
pcmd.names = args
|
||||
pcmd.home = settings.Home
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pcmd *pluginRemoveCmd) run() error {
|
||||
debug("loading installed plugins from %s", settings.PluginDirs())
|
||||
plugins, err := findPlugins(settings.PluginDirs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var errorPlugins []string
|
||||
for _, name := range pcmd.names {
|
||||
if found := findPlugin(plugins, name); found != nil {
|
||||
if err := removePlugin(found); err != nil {
|
||||
errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to remove plugin %s, got error (%v)", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(pcmd.out, "Removed plugin: %s\n", name)
|
||||
}
|
||||
} else {
|
||||
errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name))
|
||||
}
|
||||
}
|
||||
if len(errorPlugins) > 0 {
|
||||
return fmt.Errorf(strings.Join(errorPlugins, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removePlugin(p *plugin.Plugin) error {
|
||||
if err := os.Remove(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
|
||||
}
|
187
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_test.go
generated
vendored
Normal file
187
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_test.go
generated
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func TestManuallyProcessArgs(t *testing.T) {
|
||||
input := []string{
|
||||
"--debug",
|
||||
"--foo", "bar",
|
||||
"--host", "example.com",
|
||||
"--kube-context", "test1",
|
||||
"--home=/tmp",
|
||||
"--tiller-namespace=hello",
|
||||
"command",
|
||||
}
|
||||
|
||||
expectKnown := []string{
|
||||
"--debug", "--host", "example.com", "--kube-context", "test1", "--home=/tmp", "--tiller-namespace=hello",
|
||||
}
|
||||
|
||||
expectUnknown := []string{
|
||||
"--foo", "bar", "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) {
|
||||
cleanup := resetEnv()
|
||||
defer cleanup()
|
||||
|
||||
settings.Home = "testdata/helmhome"
|
||||
|
||||
os.Setenv("HELM_HOME", settings.Home.String())
|
||||
hh := settings.Home
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
cmd := &cobra.Command{}
|
||||
loadPlugins(cmd, out)
|
||||
|
||||
envs := strings.Join([]string{
|
||||
"fullenv",
|
||||
hh.Plugins() + "/fullenv",
|
||||
hh.Plugins(),
|
||||
hh.String(),
|
||||
hh.Repository(),
|
||||
hh.RepositoryFile(),
|
||||
hh.Cache(),
|
||||
hh.LocalRepository(),
|
||||
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
|
||||
}{
|
||||
{"args", "echo args", "This echos args", "-a -b -c\n", []string{"-a", "-b", "-c"}},
|
||||
{"echo", "echo stuff", "This echos stuff", "hello\n", []string{}},
|
||||
{"env", "env stuff", "show the env", hh.String() + "\n", []string{}},
|
||||
{"fullenv", "show env vars", "show all env vars", envs + "\n", []string{}},
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Errorf("Error running %s: %s", 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) {
|
||||
cleanup := resetEnv()
|
||||
defer cleanup()
|
||||
|
||||
settings.Home = "testdata/helmhome"
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupEnv(t *testing.T) {
|
||||
name := "pequod"
|
||||
settings.Home = helmpath.Home("testdata/helmhome")
|
||||
base := filepath.Join(settings.Home.Plugins(), name)
|
||||
settings.Debug = true
|
||||
defer func() {
|
||||
settings.Debug = false
|
||||
}()
|
||||
|
||||
plugin.SetupPluginEnv(settings, name, base)
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
expect string
|
||||
}{
|
||||
{"HELM_PLUGIN_NAME", name},
|
||||
{"HELM_PLUGIN_DIR", base},
|
||||
{"HELM_PLUGIN", settings.Home.Plugins()},
|
||||
{"HELM_DEBUG", "1"},
|
||||
{"HELM_HOME", settings.Home.String()},
|
||||
{"HELM_PATH_REPOSITORY", settings.Home.Repository()},
|
||||
{"HELM_PATH_REPOSITORY_FILE", settings.Home.RepositoryFile()},
|
||||
{"HELM_PATH_CACHE", settings.Home.Cache()},
|
||||
{"HELM_PATH_LOCAL_REPOSITORY", settings.Home.LocalRepository()},
|
||||
{"HELM_PATH_STARTER", settings.Home.Starters()},
|
||||
{"TILLER_HOST", settings.TillerHost},
|
||||
{"TILLER_NAMESPACE", settings.TillerNamespace},
|
||||
} {
|
||||
if got := os.Getenv(tt.name); got != tt.expect {
|
||||
t.Errorf("Expected $%s=%q, got %q", tt.name, tt.expect, got)
|
||||
}
|
||||
}
|
||||
}
|
113
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_update.go
generated
vendored
Normal file
113
src/vendor/github.com/kubernetes/helm/cmd/helm/plugin_update.go
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/plugin"
|
||||
"k8s.io/helm/pkg/plugin/installer"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type pluginUpdateCmd struct {
|
||||
names []string
|
||||
home helmpath.Home
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func newPluginUpdateCmd(out io.Writer) *cobra.Command {
|
||||
pcmd := &pluginUpdateCmd{out: out}
|
||||
cmd := &cobra.Command{
|
||||
Use: "update <plugin>...",
|
||||
Short: "update one or more Helm plugins",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return pcmd.complete(args)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return pcmd.run()
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (pcmd *pluginUpdateCmd) complete(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("please provide plugin name to update")
|
||||
}
|
||||
pcmd.names = args
|
||||
pcmd.home = settings.Home
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pcmd *pluginUpdateCmd) run() error {
|
||||
installer.Debug = settings.Debug
|
||||
debug("loading installed plugins from %s", settings.PluginDirs())
|
||||
plugins, err := findPlugins(settings.PluginDirs())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var errorPlugins []string
|
||||
|
||||
for _, name := range pcmd.names {
|
||||
if found := findPlugin(plugins, name); found != nil {
|
||||
if err := updatePlugin(found, pcmd.home); err != nil {
|
||||
errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to update plugin %s, got error (%v)", name, err))
|
||||
} else {
|
||||
fmt.Fprintf(pcmd.out, "Updated plugin: %s\n", name)
|
||||
}
|
||||
} else {
|
||||
errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name))
|
||||
}
|
||||
}
|
||||
if len(errorPlugins) > 0 {
|
||||
return fmt.Errorf(strings.Join(errorPlugins, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updatePlugin(p *plugin.Plugin, home helmpath.Home) 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, home)
|
||||
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)
|
||||
}
|
82
src/vendor/github.com/kubernetes/helm/cmd/helm/printer.go
generated
vendored
Normal file
82
src/vendor/github.com/kubernetes/helm/cmd/helm/printer.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"time"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
"k8s.io/helm/pkg/timeconv"
|
||||
)
|
||||
|
||||
var printReleaseTemplate = `REVISION: {{.Release.Version}}
|
||||
RELEASED: {{.ReleaseDate}}
|
||||
CHART: {{.Release.Chart.Metadata.Name}}-{{.Release.Chart.Metadata.Version}}
|
||||
USER-SUPPLIED VALUES:
|
||||
{{.Release.Config.Raw}}
|
||||
COMPUTED VALUES:
|
||||
{{.ComputedValues}}
|
||||
HOOKS:
|
||||
{{- range .Release.Hooks }}
|
||||
---
|
||||
# {{.Name}}
|
||||
{{.Manifest}}
|
||||
{{- end }}
|
||||
MANIFEST:
|
||||
{{.Release.Manifest}}
|
||||
`
|
||||
|
||||
func printRelease(out io.Writer, rel *release.Release) error {
|
||||
if rel == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg, err := chartutil.CoalesceValues(rel.Chart, rel.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfgStr, err := cfg.YAML()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"Release": rel,
|
||||
"ComputedValues": cfgStr,
|
||||
"ReleaseDate": timeconv.Format(rel.Info.LastDeployed, time.ANSIC),
|
||||
}
|
||||
return tpl(printReleaseTemplate, data, out)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func debug(format string, args ...interface{}) {
|
||||
if settings.Debug {
|
||||
format = fmt.Sprintf("[debug] %s\n", format)
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
}
|
110
src/vendor/github.com/kubernetes/helm/cmd/helm/release_testing.go
generated
vendored
Normal file
110
src/vendor/github.com/kubernetes/helm/cmd/helm/release_testing.go
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
const releaseTestDesc = `
|
||||
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.
|
||||
`
|
||||
|
||||
type releaseTestCmd struct {
|
||||
name string
|
||||
out io.Writer
|
||||
client helm.Interface
|
||||
timeout int64
|
||||
cleanup bool
|
||||
}
|
||||
|
||||
func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
|
||||
rlsTest := &releaseTestCmd{
|
||||
out: out,
|
||||
client: c,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "test [RELEASE]",
|
||||
Short: "test a release",
|
||||
Long: releaseTestDesc,
|
||||
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "release name"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rlsTest.name = args[0]
|
||||
rlsTest.client = ensureHelmClient(rlsTest.client)
|
||||
return rlsTest.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.Int64Var(&rlsTest.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
f.BoolVar(&rlsTest.cleanup, "cleanup", false, "delete test pods upon completion")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (t *releaseTestCmd) run() (err error) {
|
||||
c, errc := t.client.RunReleaseTest(
|
||||
t.name,
|
||||
helm.ReleaseTestTimeout(t.timeout),
|
||||
helm.ReleaseTestCleanup(t.cleanup),
|
||||
)
|
||||
testErr := &testErr{}
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-errc:
|
||||
if prettyError(err) == nil && testErr.failed > 0 {
|
||||
return testErr.Error()
|
||||
}
|
||||
return prettyError(err)
|
||||
case res, ok := <-c:
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
if res.Status == release.TestRun_FAILURE {
|
||||
testErr.failed++
|
||||
}
|
||||
|
||||
fmt.Fprintf(t.out, res.Msg+"\n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type testErr struct {
|
||||
failed int
|
||||
}
|
||||
|
||||
func (err *testErr) Error() error {
|
||||
return fmt.Errorf("%v test(s) failed", err.failed)
|
||||
}
|
106
src/vendor/github.com/kubernetes/helm/cmd/helm/release_testing_test.go
generated
vendored
Normal file
106
src/vendor/github.com/kubernetes/helm/cmd/helm/release_testing_test.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestReleaseTesting(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
flags []string
|
||||
responses map[string]release.TestRun_Status
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
name: "basic test",
|
||||
args: []string{"example-release"},
|
||||
flags: []string{},
|
||||
responses: map[string]release.TestRun_Status{"PASSED: green lights everywhere": release.TestRun_SUCCESS},
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
name: "test failure",
|
||||
args: []string{"example-fail"},
|
||||
flags: []string{},
|
||||
responses: map[string]release.TestRun_Status{"FAILURE: red lights everywhere": release.TestRun_FAILURE},
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "test unknown",
|
||||
args: []string{"example-unknown"},
|
||||
flags: []string{},
|
||||
responses: map[string]release.TestRun_Status{"UNKNOWN: yellow lights everywhere": release.TestRun_UNKNOWN},
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
name: "test error",
|
||||
args: []string{"example-error"},
|
||||
flags: []string{},
|
||||
responses: map[string]release.TestRun_Status{"ERROR: yellow lights everywhere": release.TestRun_FAILURE},
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "test running",
|
||||
args: []string{"example-running"},
|
||||
flags: []string{},
|
||||
responses: map[string]release.TestRun_Status{"RUNNING: things are happpeningggg": release.TestRun_RUNNING},
|
||||
fail: false,
|
||||
},
|
||||
{
|
||||
name: "multiple tests example",
|
||||
args: []string{"example-suite"},
|
||||
flags: []string{},
|
||||
responses: map[string]release.TestRun_Status{
|
||||
"RUNNING: things are happpeningggg": release.TestRun_RUNNING,
|
||||
"PASSED: party time": release.TestRun_SUCCESS,
|
||||
"RUNNING: things are happening again": release.TestRun_RUNNING,
|
||||
"FAILURE: good thing u checked :)": release.TestRun_FAILURE,
|
||||
"RUNNING: things are happpeningggg yet again": release.TestRun_RUNNING,
|
||||
"PASSED: feel free to party again": release.TestRun_SUCCESS},
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
c := &helm.FakeClient{Responses: tt.responses}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
cmd := newReleaseTestCmd(c, buf)
|
||||
cmd.ParseFlags(tt.flags)
|
||||
|
||||
err := cmd.RunE(cmd, tt.args)
|
||||
if err == nil && tt.fail {
|
||||
t.Errorf("%q did not fail but should have failed", tt.name)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if tt.fail {
|
||||
continue
|
||||
} else {
|
||||
t.Errorf("%q reported error: %s", tt.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
47
src/vendor/github.com/kubernetes/helm/cmd/helm/repo.go
generated
vendored
Normal file
47
src/vendor/github.com/kubernetes/helm/cmd/helm/repo.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
)
|
||||
|
||||
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.
|
||||
Example usage:
|
||||
$ helm repo add [NAME] [REPO_URL]
|
||||
`
|
||||
|
||||
func newRepoCmd(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "repo [FLAGS] add|remove|list|index|update [ARGS]",
|
||||
Short: "add, list, remove, update, and index chart repositories",
|
||||
Long: repoHelm,
|
||||
}
|
||||
|
||||
cmd.AddCommand(newRepoAddCmd(out))
|
||||
cmd.AddCommand(newRepoListCmd(out))
|
||||
cmd.AddCommand(newRepoRemoveCmd(out))
|
||||
cmd.AddCommand(newRepoIndexCmd(out))
|
||||
cmd.AddCommand(newRepoUpdateCmd(out))
|
||||
|
||||
return cmd
|
||||
}
|
117
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_add.go
generated
vendored
Normal file
117
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_add.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
type repoAddCmd struct {
|
||||
name string
|
||||
url string
|
||||
username string
|
||||
password string
|
||||
home helmpath.Home
|
||||
noupdate bool
|
||||
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func newRepoAddCmd(out io.Writer) *cobra.Command {
|
||||
add := &repoAddCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "add [flags] [NAME] [URL]",
|
||||
Short: "add a chart repository",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "name for the chart repository", "the url of the chart repository"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
add.name = args[0]
|
||||
add.url = args[1]
|
||||
add.home = settings.Home
|
||||
|
||||
return add.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.StringVar(&add.username, "username", "", "chart repository username")
|
||||
f.StringVar(&add.password, "password", "", "chart repository password")
|
||||
f.BoolVar(&add.noupdate, "no-update", false, "raise error if repo is already registered")
|
||||
f.StringVar(&add.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
|
||||
f.StringVar(&add.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
|
||||
f.StringVar(&add.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (a *repoAddCmd) run() error {
|
||||
if err := addRepository(a.name, a.url, a.username, a.password, a.home, a.certFile, a.keyFile, a.caFile, a.noupdate); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(a.out, "%q has been added to your repositories\n", a.name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func addRepository(name, url, username, password string, home helmpath.Home, certFile, keyFile, caFile string, noUpdate bool) error {
|
||||
f, err := repo.LoadRepositoriesFile(home.RepositoryFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if noUpdate && f.Has(name) {
|
||||
return fmt.Errorf("repository name (%s) already exists, please specify a different name", name)
|
||||
}
|
||||
|
||||
cif := home.CacheIndex(name)
|
||||
c := repo.Entry{
|
||||
Name: name,
|
||||
Cache: cif,
|
||||
URL: url,
|
||||
Username: username,
|
||||
Password: password,
|
||||
CertFile: certFile,
|
||||
KeyFile: keyFile,
|
||||
CAFile: caFile,
|
||||
}
|
||||
|
||||
r, err := repo.NewChartRepository(&c, getter.All(settings))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.DownloadIndexFile(home.Cache()); err != nil {
|
||||
return fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", url, err.Error())
|
||||
}
|
||||
|
||||
f.Update(&c)
|
||||
|
||||
return f.WriteFile(home.RepositoryFile(), 0644)
|
||||
}
|
103
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_add_test.go
generated
vendored
Normal file
103
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_add_test.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/repo/repotest"
|
||||
)
|
||||
|
||||
var testName = "test-name"
|
||||
|
||||
func TestRepoAddCmd(t *testing.T) {
|
||||
srv, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
srv.Stop()
|
||||
os.RemoveAll(thome.String())
|
||||
cleanup()
|
||||
}()
|
||||
if err := ensureTestHome(thome, t); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
settings.Home = thome
|
||||
|
||||
tests := []releaseCase{
|
||||
{
|
||||
name: "add a repository",
|
||||
args: []string{testName, srv.URL()},
|
||||
expected: "\"" + testName + "\" has been added to your repositories",
|
||||
},
|
||||
}
|
||||
|
||||
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {
|
||||
return newRepoAddCmd(out)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepoAdd(t *testing.T) {
|
||||
ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cleanup := resetEnv()
|
||||
hh := thome
|
||||
defer func() {
|
||||
ts.Stop()
|
||||
os.RemoveAll(thome.String())
|
||||
cleanup()
|
||||
}()
|
||||
if err := ensureTestHome(hh, t); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
settings.Home = thome
|
||||
|
||||
if err := addRepository(testName, ts.URL(), "", "", hh, "", "", "", true); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
f, err := repo.LoadRepositoriesFile(hh.RepositoryFile())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if !f.Has(testName) {
|
||||
t.Errorf("%s was not successfully inserted into %s", testName, hh.RepositoryFile())
|
||||
}
|
||||
|
||||
if err := addRepository(testName, ts.URL(), "", "", hh, "", "", "", false); err != nil {
|
||||
t.Errorf("Repository was not updated: %s", err)
|
||||
}
|
||||
|
||||
if err := addRepository(testName, ts.URL(), "", "", hh, "", "", "", false); err != nil {
|
||||
t.Errorf("Duplicate repository name was added")
|
||||
}
|
||||
}
|
105
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_index.go
generated
vendored
Normal file
105
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_index.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/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 repoIndexCmd struct {
|
||||
dir string
|
||||
url string
|
||||
out io.Writer
|
||||
merge string
|
||||
}
|
||||
|
||||
func newRepoIndexCmd(out io.Writer) *cobra.Command {
|
||||
index := &repoIndexCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "index [flags] [DIR]",
|
||||
Short: "generate an index file given a directory containing packaged charts",
|
||||
Long: repoIndexDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "path to a directory"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
index.dir = args[0]
|
||||
|
||||
return index.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.StringVar(&index.url, "url", "", "url of chart repository")
|
||||
f.StringVar(&index.merge, "merge", "", "merge the generated index into the given index")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (i *repoIndexCmd) run() 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, 0755)
|
||||
} else {
|
||||
i2, err = repo.LoadIndexFile(mergeTo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Merge failed: %s", err)
|
||||
}
|
||||
}
|
||||
i.Merge(i2)
|
||||
}
|
||||
i.SortEntries()
|
||||
return i.WriteFile(out, 0755)
|
||||
}
|
171
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_index_test.go
generated
vendored
Normal file
171
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_index_test.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
func TestRepoIndexCmd(t *testing.T) {
|
||||
|
||||
dir, err := ioutil.TempDir("", "helm-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
_, 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) != 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)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
66
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_list.go
generated
vendored
Normal file
66
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_list.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
type repoListCmd struct {
|
||||
out io.Writer
|
||||
home helmpath.Home
|
||||
}
|
||||
|
||||
func newRepoListCmd(out io.Writer) *cobra.Command {
|
||||
list := &repoListCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list [flags]",
|
||||
Short: "list chart repositories",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
list.home = settings.Home
|
||||
return list.run()
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (a *repoListCmd) run() error {
|
||||
f, err := repo.LoadRepositoriesFile(a.home.RepositoryFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(f.Repositories) == 0 {
|
||||
return errors.New("no repositories to show")
|
||||
}
|
||||
table := uitable.New()
|
||||
table.AddRow("NAME", "URL")
|
||||
for _, re := range f.Repositories {
|
||||
table.AddRow(re.Name, re.URL)
|
||||
}
|
||||
fmt.Fprintln(a.out, table)
|
||||
return nil
|
||||
}
|
92
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_remove.go
generated
vendored
Normal file
92
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_remove.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
type repoRemoveCmd struct {
|
||||
out io.Writer
|
||||
name string
|
||||
home helmpath.Home
|
||||
}
|
||||
|
||||
func newRepoRemoveCmd(out io.Writer) *cobra.Command {
|
||||
remove := &repoRemoveCmd{out: out}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "remove [flags] [NAME]",
|
||||
Aliases: []string{"rm"},
|
||||
Short: "remove a chart repository",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := checkArgsLength(len(args), "name of chart repository"); err != nil {
|
||||
return err
|
||||
}
|
||||
remove.name = args[0]
|
||||
remove.home = settings.Home
|
||||
|
||||
return remove.run()
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (r *repoRemoveCmd) run() error {
|
||||
return removeRepoLine(r.out, r.name, r.home)
|
||||
}
|
||||
|
||||
func removeRepoLine(out io.Writer, name string, home helmpath.Home) error {
|
||||
repoFile := home.RepositoryFile()
|
||||
r, err := repo.LoadRepositoriesFile(repoFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.Remove(name) {
|
||||
return fmt.Errorf("no repo named %q found", name)
|
||||
}
|
||||
if err := r.WriteFile(repoFile, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := removeRepoCache(name, home); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "%q has been removed from your repositories\n", name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeRepoCache(name string, home helmpath.Home) error {
|
||||
if _, err := os.Stat(home.CacheIndex(name)); err == nil {
|
||||
err = os.Remove(home.CacheIndex(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
81
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_remove_test.go
generated
vendored
Normal file
81
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_remove_test.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/repo/repotest"
|
||||
)
|
||||
|
||||
func TestRepoRemove(t *testing.T) {
|
||||
ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hh := helmpath.Home(thome)
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
ts.Stop()
|
||||
os.RemoveAll(thome.String())
|
||||
cleanup()
|
||||
}()
|
||||
if err := ensureTestHome(hh, t); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
settings.Home = thome
|
||||
|
||||
b := bytes.NewBuffer(nil)
|
||||
|
||||
if err := removeRepoLine(b, testName, hh); err == nil {
|
||||
t.Errorf("Expected error removing %s, but did not get one.", testName)
|
||||
}
|
||||
if err := addRepository(testName, ts.URL(), "", "", hh, "", "", "", true); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
mf, _ := os.Create(hh.CacheIndex(testName))
|
||||
mf.Close()
|
||||
|
||||
b.Reset()
|
||||
if err := removeRepoLine(b, testName, hh); err != nil {
|
||||
t.Errorf("Error removing %s from repositories", testName)
|
||||
}
|
||||
if !strings.Contains(b.String(), "has been removed") {
|
||||
t.Errorf("Unexpected output: %s", b.String())
|
||||
}
|
||||
|
||||
if _, err := os.Stat(hh.CacheIndex(testName)); err == nil {
|
||||
t.Errorf("Error cache file was not removed for repository %s", testName)
|
||||
}
|
||||
|
||||
f, err := repo.LoadRepositoriesFile(hh.RepositoryFile())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if f.Has(testName) {
|
||||
t.Errorf("%s was not successfully removed from repositories list", testName)
|
||||
}
|
||||
}
|
109
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_update.go
generated
vendored
Normal file
109
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_update.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/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'.
|
||||
|
||||
'helm update' is the deprecated form of 'helm repo update'. It will be removed in
|
||||
future releases.
|
||||
`
|
||||
|
||||
var errNoRepositories = errors.New("no repositories found. You must add one before updating")
|
||||
|
||||
type repoUpdateCmd struct {
|
||||
update func([]*repo.ChartRepository, io.Writer, helmpath.Home)
|
||||
home helmpath.Home
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func newRepoUpdateCmd(out io.Writer) *cobra.Command {
|
||||
u := &repoUpdateCmd{
|
||||
out: out,
|
||||
update: updateCharts,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "update",
|
||||
Aliases: []string{"up"},
|
||||
Short: "update information of available charts locally from chart repositories",
|
||||
Long: updateDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
u.home = settings.Home
|
||||
return u.run()
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (u *repoUpdateCmd) run() error {
|
||||
f, err := repo.LoadRepositoriesFile(u.home.RepositoryFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if 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)
|
||||
}
|
||||
|
||||
u.update(repos, u.out, u.home)
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateCharts(repos []*repo.ChartRepository, out io.Writer, home helmpath.Home) {
|
||||
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 re.Config.Name == localRepository {
|
||||
fmt.Fprintf(out, "...Skip %s chart repository\n", re.Config.Name)
|
||||
return
|
||||
}
|
||||
err := re.DownloadIndexFile(home.Cache())
|
||||
if 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!⎈ ")
|
||||
}
|
106
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_update_test.go
generated
vendored
Normal file
106
src/vendor/github.com/kubernetes/helm/cmd/helm/repo_update_test.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/repo/repotest"
|
||||
)
|
||||
|
||||
func TestUpdateCmd(t *testing.T) {
|
||||
thome, err := tempHelmHome(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
os.RemoveAll(thome.String())
|
||||
cleanup()
|
||||
}()
|
||||
|
||||
settings.Home = thome
|
||||
|
||||
out := bytes.NewBuffer(nil)
|
||||
// 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, hh helmpath.Home) {
|
||||
for _, re := range repos {
|
||||
fmt.Fprintln(out, re.Config.Name)
|
||||
}
|
||||
}
|
||||
uc := &repoUpdateCmd{
|
||||
update: updater,
|
||||
home: helmpath.Home(thome),
|
||||
out: out,
|
||||
}
|
||||
if err := uc.run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if got := out.String(); !strings.Contains(got, "charts") || !strings.Contains(got, "local") {
|
||||
t.Errorf("Expected 'charts' and 'local' (in any order) got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateCharts(t *testing.T) {
|
||||
ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hh := helmpath.Home(thome)
|
||||
cleanup := resetEnv()
|
||||
defer func() {
|
||||
ts.Stop()
|
||||
os.RemoveAll(thome.String())
|
||||
cleanup()
|
||||
}()
|
||||
if err := ensureTestHome(hh, t); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
settings.Home = thome
|
||||
|
||||
r, err := repo.NewChartRepository(&repo.Entry{
|
||||
Name: "charts",
|
||||
URL: ts.URL(),
|
||||
Cache: hh.CacheIndex("charts"),
|
||||
}, getter.All(settings))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
b := bytes.NewBuffer(nil)
|
||||
updateCharts([]*repo.ChartRepository{r}, b, hh)
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
131
src/vendor/github.com/kubernetes/helm/cmd/helm/reset.go
generated
vendored
Normal file
131
src/vendor/github.com/kubernetes/helm/cmd/helm/reset.go
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
|
||||
"k8s.io/helm/cmd/helm/installer"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
const resetDesc = `
|
||||
This command uninstalls Tiller (the Helm server-side component) from your
|
||||
Kubernetes Cluster and optionally deletes local configuration in
|
||||
$HELM_HOME (default ~/.helm/)
|
||||
`
|
||||
|
||||
type resetCmd struct {
|
||||
force bool
|
||||
removeHelmHome bool
|
||||
namespace string
|
||||
out io.Writer
|
||||
home helmpath.Home
|
||||
client helm.Interface
|
||||
kubeClient internalclientset.Interface
|
||||
}
|
||||
|
||||
func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
|
||||
d := &resetCmd{
|
||||
out: out,
|
||||
client: client,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "reset",
|
||||
Short: "uninstalls Tiller from a cluster",
|
||||
Long: resetDesc,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := setupConnection(); !d.force && err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 0 {
|
||||
return errors.New("This command does not accept arguments")
|
||||
}
|
||||
|
||||
d.namespace = settings.TillerNamespace
|
||||
d.home = settings.Home
|
||||
d.client = ensureHelmClient(d.client)
|
||||
|
||||
return d.run()
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.BoolVarP(&d.force, "force", "f", false, "forces Tiller uninstall even if there are releases installed, or if Tiller is not in ready state. Releases are not deleted.)")
|
||||
f.BoolVar(&d.removeHelmHome, "remove-helm-home", false, "if set deletes $HELM_HOME")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// runReset uninstalls tiller from Kubernetes Cluster and deletes local config
|
||||
func (d *resetCmd) run() error {
|
||||
if d.kubeClient == nil {
|
||||
c, err := getInternalKubeClient(settings.KubeContext)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get kubernetes client: %s", err)
|
||||
}
|
||||
d.kubeClient = c
|
||||
}
|
||||
|
||||
res, err := d.client.ListReleases(
|
||||
helm.ReleaseListStatuses([]release.Status_Code{release.Status_DEPLOYED}),
|
||||
)
|
||||
if !d.force && err != nil {
|
||||
return prettyError(err)
|
||||
}
|
||||
|
||||
if !d.force && res != nil && len(res.Releases) > 0 {
|
||||
return fmt.Errorf("there are still %d deployed releases (Tip: use --force to remove Tiller. Releases will not be deleted.)", len(res.Releases))
|
||||
}
|
||||
|
||||
if err := installer.Uninstall(d.kubeClient, &installer.Options{Namespace: d.namespace}); err != nil {
|
||||
return fmt.Errorf("error unstalling Tiller: %s", err)
|
||||
}
|
||||
|
||||
if d.removeHelmHome {
|
||||
if err := deleteDirectories(d.home, d.out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(d.out, "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteDirectories deletes $HELM_HOME
|
||||
func deleteDirectories(home helmpath.Home, out io.Writer) error {
|
||||
if _, err := os.Stat(home.String()); err == nil {
|
||||
fmt.Fprintf(out, "Deleting %s \n", home.String())
|
||||
if err := os.RemoveAll(home.String()); err != nil {
|
||||
return fmt.Errorf("Could not remove %s: %s", home.String(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
170
src/vendor/github.com/kubernetes/helm/cmd/helm/reset_test.go
generated
vendored
Normal file
170
src/vendor/github.com/kubernetes/helm/cmd/helm/reset_test.go
generated
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES 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/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/proto/hapi/release"
|
||||
)
|
||||
|
||||
func TestResetCmd(t *testing.T) {
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(home)
|
||||
|
||||
var buf bytes.Buffer
|
||||
c := &helm.FakeClient{}
|
||||
fc := fake.NewSimpleClientset()
|
||||
cmd := &resetCmd{
|
||||
out: &buf,
|
||||
home: helmpath.Home(home),
|
||||
client: c,
|
||||
kubeClient: fc,
|
||||
namespace: core.NamespaceDefault,
|
||||
}
|
||||
if err := cmd.run(); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
actions := fc.Actions()
|
||||
if len(actions) != 3 {
|
||||
t.Errorf("Expected 3 actions, got %d", len(actions))
|
||||
}
|
||||
expected := "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster."
|
||||
if !strings.Contains(buf.String(), expected) {
|
||||
t.Errorf("expected %q, got %q", expected, buf.String())
|
||||
}
|
||||
if _, err := os.Stat(home); err != nil {
|
||||
t.Errorf("Helm home directory %s does not exists", home)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetCmd_removeHelmHome(t *testing.T) {
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(home)
|
||||
|
||||
var buf bytes.Buffer
|
||||
c := &helm.FakeClient{}
|
||||
fc := fake.NewSimpleClientset()
|
||||
cmd := &resetCmd{
|
||||
removeHelmHome: true,
|
||||
out: &buf,
|
||||
home: helmpath.Home(home),
|
||||
client: c,
|
||||
kubeClient: fc,
|
||||
namespace: core.NamespaceDefault,
|
||||
}
|
||||
if err := cmd.run(); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
actions := fc.Actions()
|
||||
if len(actions) != 3 {
|
||||
t.Errorf("Expected 3 actions, got %d", len(actions))
|
||||
}
|
||||
expected := "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster."
|
||||
if !strings.Contains(buf.String(), expected) {
|
||||
t.Errorf("expected %q, got %q", expected, buf.String())
|
||||
}
|
||||
if _, err := os.Stat(home); err == nil {
|
||||
t.Errorf("Helm home directory %s already exists", home)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReset_deployedReleases(t *testing.T) {
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(home)
|
||||
|
||||
var buf bytes.Buffer
|
||||
resp := []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}),
|
||||
}
|
||||
c := &helm.FakeClient{
|
||||
Rels: resp,
|
||||
}
|
||||
fc := fake.NewSimpleClientset()
|
||||
cmd := &resetCmd{
|
||||
out: &buf,
|
||||
home: helmpath.Home(home),
|
||||
client: c,
|
||||
kubeClient: fc,
|
||||
namespace: core.NamespaceDefault,
|
||||
}
|
||||
err = cmd.run()
|
||||
expected := "there are still 1 deployed releases (Tip: use --force to remove Tiller. Releases will not be deleted.)"
|
||||
if !strings.Contains(err.Error(), expected) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(home); err != nil {
|
||||
t.Errorf("Helm home directory %s does not exists", home)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReset_forceFlag(t *testing.T) {
|
||||
home, err := ioutil.TempDir("", "helm_home")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(home)
|
||||
|
||||
var buf bytes.Buffer
|
||||
resp := []*release.Release{
|
||||
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide", StatusCode: release.Status_DEPLOYED}),
|
||||
}
|
||||
c := &helm.FakeClient{
|
||||
Rels: resp,
|
||||
}
|
||||
fc := fake.NewSimpleClientset()
|
||||
cmd := &resetCmd{
|
||||
force: true,
|
||||
out: &buf,
|
||||
home: helmpath.Home(home),
|
||||
client: c,
|
||||
kubeClient: fc,
|
||||
namespace: core.NamespaceDefault,
|
||||
}
|
||||
if err := cmd.run(); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
actions := fc.Actions()
|
||||
if len(actions) != 3 {
|
||||
t.Errorf("Expected 3 actions, got %d", len(actions))
|
||||
}
|
||||
expected := "Tiller (the Helm server-side component) has been uninstalled from your Kubernetes Cluster."
|
||||
if !strings.Contains(buf.String(), expected) {
|
||||
t.Errorf("expected %q, got %q", expected, buf.String())
|
||||
}
|
||||
if _, err := os.Stat(home); err != nil {
|
||||
t.Errorf("Helm home directory %s does not exists", home)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user