Merge pull request #1 from kubernetes/master

Update
pull/3064/head
Neil Moore 7 years ago committed by GitHub
commit 86f34c67c1

@ -23,3 +23,11 @@ jobs:
- deploy:
name: deploy
command: .circleci/deploy.sh
workflows:
version: 2
build:
jobs:
- build:
filters:
tags:
only: /.*/

@ -7,7 +7,7 @@
Helm is a tool for managing Kubernetes charts. Charts are packages of
pre-configured Kubernetes resources.
Use Helm to...
Use Helm to:
- Find and use [popular software packaged as Kubernetes charts](https://github.com/kubernetes/charts)
- Share your own applications as Kubernetes charts
@ -34,10 +34,10 @@ Think of it like apt/yum/homebrew for Kubernetes.
Binary downloads of the Helm client can be found at the following links:
- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.1-darwin-amd64.tar.gz)
- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.1-linux-amd64.tar.gz)
- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.1-linux-386.tar.gz)
- [Windows](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.1-windows-amd64.tar.gz)
- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-darwin-amd64.tar.gz)
- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz)
- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-386.tar.gz)
- [Windows](https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-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`.
@ -53,7 +53,7 @@ Get started with the [Quick Start guide](https://docs.helm.sh/using_helm/#quicks
## Roadmap
The [Helm roadmap is currently located on the wiki](https://github.com/kubernetes/helm/wiki/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

@ -96,7 +96,7 @@ func (d *dependencyUpdateCmd) run() error {
Getters: getter.All(settings),
}
if d.verify {
man.Verify = downloader.VerifyIfPossible
man.Verify = downloader.VerifyAlways
}
if settings.Debug {
man.Debug = true

@ -79,8 +79,8 @@ func newFetchCmd(out io.Writer) *cobra.Command {
}
if fch.version == "" && fch.devel {
debug("setting version to >0.0.0-a")
fch.version = ">0.0.0-a"
debug("setting version to >0.0.0-0")
fch.version = ">0.0.0-0"
}
for i := 0; i < len(args); i++ {
@ -105,7 +105,7 @@ func newFetchCmd(out io.Writer) *cobra.Command {
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-a'. If --version is set, this is ignored.")
f.BoolVar(&fch.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
return cmd
}

@ -239,16 +239,16 @@ func getKubeClient(context string) (*rest.Config, kubernetes.Interface, error) {
// 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) (*rest.Config, internalclientset.Interface, error) {
func getInternalKubeClient(context string) (internalclientset.Interface, error) {
config, err := configForContext(context)
if err != nil {
return nil, nil, err
return nil, err
}
client, err := internalclientset.NewForConfig(config)
if err != nil {
return nil, nil, fmt.Errorf("could not get Kubernetes client: %s", err)
return nil, fmt.Errorf("could not get Kubernetes client: %s", err)
}
return config, client, nil
return client, nil
}
// ensureHelmClient returns a new helm client impl. if h is not nil.

@ -17,6 +17,8 @@ limitations under the License.
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
@ -26,6 +28,7 @@ import (
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/helmpath"
@ -120,6 +123,10 @@ func newInitCmd(out io.Writer) *cobra.Command {
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.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
}
@ -160,31 +167,66 @@ func (i *initCmd) run() error {
i.opts.ServiceAccount = i.serviceAccount
i.opts.MaxHistory = i.maxHistory
if settings.Debug {
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
}
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 {
}
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
}
if _, err := fmt.Fprintln(w, "kind:", kind); err != nil {
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 := fmt.Fprint(w, body); err != nil {
if _, err = i.out.Write(out.Bytes()); err != nil {
return err
}
if !last {
return nil
return nil
case "yaml":
if err := writeYAMLManifest("extensions/v1beta1", "Deployment", body, true, false); err != nil {
return err
}
// YAML ending document boundary marker
_, err := fmt.Fprintln(w, "...")
return err
return nil
default:
return fmt.Errorf("unknown output format: %q", i.opts.Output)
}
}
if settings.Debug {
var body string
var err error

@ -27,14 +27,16 @@ import (
"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"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
testcore "k8s.io/client-go/testing"
"encoding/json"
"k8s.io/helm/cmd/helm/installer"
"k8s.io/helm/pkg/helm/helmpath"
)
@ -303,3 +305,53 @@ func TestInitCmd_tlsOptions(t *testing.T) {
}
}
}
// 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())
}
}
}

@ -41,13 +41,14 @@ import (
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/repo"
"k8s.io/helm/pkg/strvals"
"net/url"
)
const installDesc = `
This command installs a chart archive.
The install argument must be either a relative path to a chart directory or the
name of a chart in the current working directory.
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.
@ -159,8 +160,8 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
debug("Original chart version: %q", inst.version)
if inst.version == "" && inst.devel {
debug("setting version to >0.0.0-a")
inst.version = ">0.0.0-a"
debug("setting version to >0.0.0-0")
inst.version = ">0.0.0-0"
}
cp, err := locateChartPath(inst.repoURL, args[0], inst.version, inst.verify, inst.keyring,
@ -175,7 +176,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.VarP(&inst.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
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")
@ -192,7 +193,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
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-a'. If --version is set, this is ignored.")
f.BoolVar(&inst.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
return cmd
}
@ -316,8 +317,9 @@ func vals(valueFiles valueFiles, values []string) ([]byte, error) {
if strings.TrimSpace(filePath) == "-" {
bytes, err = ioutil.ReadAll(os.Stdin)
} else {
bytes, err = ioutil.ReadFile(filePath)
bytes, err = readFile(filePath)
}
if err != nil {
return []byte{}, err
}
@ -469,3 +471,23 @@ func checkDependencies(ch *chart.Chart, reqs *chartutil.Requirements) error {
}
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)
} else {
getter, err := getterConstructor(filePath, "", "", "")
if err != nil {
return []byte{}, err
}
data, err := getter.Get(filePath)
return data.Bytes(), err
}
}

@ -19,16 +19,18 @@ package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"fmt"
"io/ioutil"
"strings"
"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/client-go/pkg/api/v1"
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
"k8s.io/helm/pkg/chartutil"
)
// Install uses Kubernetes client to install Tiller.
@ -74,13 +76,17 @@ func Upgrade(client kubernetes.Interface, opts *Options) error {
// createDeployment creates the Tiller Deployment resource.
func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error {
obj := deployment(opts)
_, err := client.Deployments(obj.Namespace).Create(obj)
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 {
func deployment(opts *Options) (*v1beta1.Deployment, error) {
return generateDeployment(opts)
}
@ -99,7 +105,10 @@ func service(namespace string) *v1.Service {
// DeploymentManifest gets the manifest (as a string) that describes the Tiller Deployment
// resource.
func DeploymentManifest(opts *Options) (string, error) {
obj := deployment(opts)
obj, err := deployment(opts)
if err != nil {
return "", err
}
buf, err := yaml.Marshal(obj)
return string(buf), err
}
@ -117,8 +126,28 @@ func generateLabels(labels map[string]string) map[string]string {
return labels
}
func generateDeployment(opts *Options) *v1beta1.Deployment {
// 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,
@ -166,10 +195,8 @@ func generateDeployment(opts *Options) *v1beta1.Deployment {
},
},
},
HostNetwork: opts.EnableHostNetwork,
NodeSelector: map[string]string{
"beta.kubernetes.io/os": "linux",
},
HostNetwork: opts.EnableHostNetwork,
NodeSelector: nodeSelectors,
},
},
},
@ -205,7 +232,40 @@ func generateDeployment(opts *Options) *v1beta1.Deployment {
},
})
}
return d
// 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 {

@ -23,13 +23,14 @@ import (
"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"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
testcore "k8s.io/client-go/testing"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/version"
)
@ -330,7 +331,7 @@ func TestInstall_canary(t *testing.T) {
func TestUpgrade(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
serviceAccount := "newServiceAccount"
existingDeployment := deployment(&Options{
existingDeployment, _ := deployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace",
ServiceAccount: "serviceAccountToReplace",
@ -371,7 +372,7 @@ func TestUpgrade(t *testing.T) {
func TestUpgrade_serviceNotFound(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
existingDeployment := deployment(&Options{
existingDeployment, _ := deployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace",
UseCanary: false,
@ -419,3 +420,107 @@ func tlsTestFile(t *testing.T, path string) string {
}
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)
}
}
}
}

@ -19,7 +19,8 @@ package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"fmt"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/api/core/v1"
"k8s.io/helm/pkg/strvals"
"k8s.io/helm/pkg/version"
)
@ -76,6 +77,15 @@ type Options struct {
//
// Less than or equal to zero means no limit.
MaxHistory 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 {
@ -97,3 +107,42 @@ func (opts *Options) pullPolicy() v1.PullPolicy {
}
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)
}

@ -19,7 +19,7 @@ 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/client-go/pkg/apis/extensions"
"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"

@ -86,7 +86,7 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
// runReset uninstalls tiller from Kubernetes Cluster and deletes local config
func (d *resetCmd) run() error {
if d.kubeClient == nil {
_, c, err := getInternalKubeClient(settings.KubeContext)
c, err := getInternalKubeClient(settings.KubeContext)
if err != nil {
return fmt.Errorf("could not get kubernetes client: %s", err)
}

@ -30,7 +30,6 @@ import (
"strings"
"github.com/Masterminds/semver"
"k8s.io/helm/pkg/repo"
)
@ -147,6 +146,8 @@ func (i *Index) SearchLiteral(term string, threshold int) []*Result {
term = strings.ToLower(term)
buf := []*Result{}
for k, v := range i.lines {
k = strings.ToLower(k)
v = strings.ToLower(v)
res := strings.Index(v, term)
if score := i.calcScore(res, v); res != -1 && score < threshold {
parts := strings.Split(k, verSep) // Remove version, if it is there.
@ -231,5 +232,5 @@ func (s scoreSorter) Less(a, b int) bool {
func indstr(name string, ref *repo.ChartVersion) string {
i := ref.Name + sep + name + "/" + ref.Name + sep +
ref.Description + sep + strings.Join(ref.Keywords, " ")
return strings.ToLower(i)
return i
}

@ -135,7 +135,7 @@ func TestAll(t *testing.T) {
func TestAddRepo_Sort(t *testing.T) {
i := loadTestIndex(t, true)
sr, err := i.Search("testing/santa-maria", 100, false)
sr, err := i.Search("TESTING/SANTA-MARIA", 100, false)
if err != nil {
t.Fatal(err)
}
@ -202,6 +202,14 @@ func TestSearchByName(t *testing.T) {
{Name: "ztesting/pinta"},
},
},
{
name: "description upper search, two results",
query: "TWO",
expect: []*Result{
{Name: "testing/pinta"},
{Name: "ztesting/pinta"},
},
},
{
name: "nothing found",
query: "mayflower",
@ -209,7 +217,7 @@ func TestSearchByName(t *testing.T) {
},
{
name: "regexp, one result",
query: "th[ref]*",
query: "Th[ref]*",
expect: []*Result{
{Name: "testing/santa-maria"},
},

@ -98,8 +98,8 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
}
if upgrade.version == "" && upgrade.devel {
debug("setting version to >0.0.0-a")
upgrade.version = ">0.0.0-a"
debug("setting version to >0.0.0-0")
upgrade.version = ">0.0.0-0"
}
upgrade.release = args[0]
@ -111,7 +111,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade")
f.BoolVar(&upgrade.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
f.BoolVar(&upgrade.force, "force", false, "force resource update through delete/recreate if needed")
@ -131,7 +131,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.StringVar(&upgrade.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&upgrade.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&upgrade.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&upgrade.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-a'. If --version is set, this is ignored.")
f.BoolVar(&upgrade.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.MarkDeprecated("disable-hooks", "use --no-hooks instead")

@ -57,6 +57,7 @@ const (
storageMemory = "memory"
storageConfigMap = "configmap"
storageSecret = "secret"
probeAddr = ":44135"
traceAddr = ":44136"
@ -68,7 +69,7 @@ const (
var (
grpcAddr = flag.String("listen", ":44134", "address:port to listen on")
enableTracing = flag.Bool("trace", false, "enable rpc tracing")
store = flag.String("storage", storageConfigMap, "storage driver to use. One of 'configmap' or 'memory'")
store = flag.String("storage", storageConfigMap, "storage driver to use. One of 'configmap', 'memory', or 'secret'")
remoteReleaseModules = flag.Bool("experimental-release", false, "enable experimental release modules")
tlsEnable = flag.Bool("tls", tlsEnableEnvVarDefault(), "enable TLS")
tlsVerify = flag.Bool("tls-verify", tlsVerifyEnvVarDefault(), "enable TLS and verify remote certificate")
@ -117,6 +118,12 @@ func start() {
env.Releases = storage.Init(cfgmaps)
env.Releases.Log = newLogger("storage").Printf
case storageSecret:
secrets := driver.NewSecrets(clientset.Core().Secrets(namespace()))
secrets.Log = newLogger("storage/driver").Printf
env.Releases = storage.Init(secrets)
env.Releases.Log = newLogger("storage").Printf
}
if *maxHistory > 0 {

@ -36,6 +36,7 @@ Incorrect:
{{/* ... */}}
{{ end -}}
```
It is highly recommended that new charts are created via `helm create` command as the template names are automatically defined as per this best practice.
## Formatting Templates

@ -96,6 +96,34 @@ data:
food: "pizza"
```
Variables are not "global". They are scoped to the block in which they are declared. Earlier, we assigned `$relname` in the top level of the template. That variable will be in scope for the entire template. But in our last example, `$key` and `$val` will only be in scope inside of the `{{range...}}{{end}}` block.
Variables are normally not "global". They are scoped to the block in which they are declared. Earlier, we assigned `$relname` in the top level of the template. That variable will be in scope for the entire template. But in our last example, `$key` and `$val` will only be in scope inside of the `{{range...}}{{end}}` block.
However, there is one variable that is always global - `$` - this
variable will always point to the root context. This can be very
useful when you are looping in a range need to know the chart's release
name.
An example illustrating this:
```yaml
{{- range .Values.tlsSecrets }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .name }}
labels:
# Many helm templates would use `.` below, but that will not work,
# however `$` will work here
app: {{ template "fullname" $ }}
# I cannot reference .Chart.Name, but I can do $.Chart.Name
chart: "{{ $.Chart.Name }}-{{ $.Chart.Version }}"
release: "{{ $.Release.Name }}"
heritage: "{{ $.Release.Service }}"
type: kubernetes.io/tls
data:
tls.crt: {{ .certificate }}
tls.key: {{ .key }}
---
{{- end }}
```
So far we have looked at just one template declared in just one file. But one of the powerful features of the Helm template language is its ability to declare multiple templates and use them together. We'll turn to that in the next section.

@ -319,7 +319,7 @@ refer to that value by reference. YAML refers to this as "anchoring":
```yaml
coffee: "yes, please"
favorite: &favoriteCoffee "Cappucino"
coffess:
coffees:
- Latte
- *favoriteCoffee
- Espresso
@ -339,7 +339,7 @@ YAML would be:
```YAML
coffee: yes, please
favorite: Cappucino
coffess:
coffees:
- Latte
- Cappucino
- Espresso

@ -96,6 +96,35 @@ For example:
The above will render the template when .Values.foo is defined, but will fail
to render and exit when .Values.foo is undefined.
## Creating Image Pull Secrets
Image pull secrets are essentially a combination of _registry_, _username_, and _password_. You may need them in an application you are deploying, but to create them requires running _base64_ a couple of times. We can write a helper template to compose the Docker configuration file for use as the Secret's payload. Here is an example:
First, assume that the credentials are defined in the `values.yaml` file like so:
```
imageCredentials:
registry: quay.io
username: someone
password: sillyness
```
We then define our helper template as follows:
```
{{- define "imagePullSecret" }}
{{- printf "{\"auths\": {\"%s\": {\"auth\": \"%s\"}}}" .Values.imageCredentials.registry (printf "%s:%s" .Values.imageCredentials.username .Values.imageCredentials.password | b64enc) | b64enc }}
{{- end }}
```
Finally, we use the helper template in a larger template to create the Secret manifest:
```
apiVersion: v1
kind: Secret
metadata:
name: myregistrykey
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ template "imagePullSecret" . }}
```
## Automatically Roll Deployments When ConfigMaps or Secrets change
Often times configmaps or secrets are injected as configuration

@ -21,7 +21,9 @@ We use Make to build our programs. The simplest way to get started is:
$ make bootstrap build
```
NOTE: This will fail if not run from the path: `$GOPATH/src/k8s.io/helm`.
NOTE: This will fail if not running from the path `$GOPATH/src/k8s.io/helm`. The
directory `k8s.io` should not be a symlink or `build` will not find the relevant
packages.
This will build both Helm and Tiller. `make bootstrap` will attempt to
install certain tools if they are missing.

@ -2,7 +2,7 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- define "alpine.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@ -10,7 +10,7 @@ Expand the name of the chart.
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- define "alpine.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

@ -1,7 +1,7 @@
apiVersion: v1
kind: Pod
metadata:
name: {{ template "fullname" . }}
name: {{ template "alpine.fullname" . }}
labels:
# The "heritage" label is used to track which tool deployed a given chart.
# It is useful for admins who want to see what releases a particular tool
@ -12,7 +12,7 @@ metadata:
release: {{ .Release.Name }}
# This makes it easy to audit chart usage.
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
app: {{ template "alpine.name" . }}
spec:
# This shows how to use a simple value. This will look for a passed-in value called restartPolicy.
restartPolicy: {{ .Values.restartPolicy }}

@ -2,7 +2,7 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- define "nginx.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@ -10,7 +10,7 @@ Expand the name of the chart.
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- define "nginx.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

@ -2,12 +2,12 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "fullname" . }}
name: {{ template "nginx.fullname" . }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
app: {{ template "nginx.name" . }}
data:
# When the config map is mounted as a volume, these will be created as files.
index.html: {{ .Values.index | quote }}

@ -4,7 +4,7 @@ metadata:
# This uses a "fullname" template (see _helpers)
# Basing names on .Release.Name means that the same chart can be installed
# multiple times into the same namespace.
name: {{ template "fullname" . }}
name: {{ template "nginx.fullname" . }}
labels:
# The "heritage" label is used to track which tool deployed a given chart.
# It is useful for admins who want to see what releases a particular tool
@ -15,7 +15,7 @@ metadata:
release: {{ .Release.Name }}
# This makes it easy to audit chart usage.
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
app: {{ template "nginx.name" . }}
spec:
replicas: {{ .Values.replicaCount }}
template:
@ -26,11 +26,11 @@ spec:
{{ toYaml .Values.podAnnotations | indent 8 }}
{{- end }}
labels:
app: {{ template "name" . }}
app: {{ template "nginx.name" . }}
release: {{ .Release.Name }}
spec:
containers:
- name: {{ template "name" . }}
- name: {{ template "nginx.name" . }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
@ -54,4 +54,4 @@ spec:
volumes:
- name: wwwdata-volume
configMap:
name: {{ template "fullname" . }}
name: {{ template "nginx.fullname" . }}

@ -1,7 +1,7 @@
apiVersion: batch/v1
kind: Job
metadata:
name: {{ template "fullname" . }}
name: {{ template "nginx.fullname" . }}
labels:
# The "heritage" label is used to track which tool deployed a given chart.
# It is useful for admins who want to see what releases a particular tool
@ -12,7 +12,7 @@ metadata:
release: {{ .Release.Name }}
# This makes it easy to audit chart usage.
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
app: {{ template "nginx.name" . }}
annotations:
# This is what defines this resource as a hook. Without this line, the
# job is considered part of the release.
@ -20,10 +20,10 @@ metadata:
spec:
template:
metadata:
name: {{ template "fullname" . }}
name: {{ template "nginx.fullname" . }}
labels:
release: {{ .Release.Name }}
app: {{ template "name" . }}
app: {{ template "nginx.name" . }}
spec:
# This shows how to use a simple value. This will look for a passed-in value
# called restartPolicy. If it is not found, it will use the default value.

@ -3,12 +3,12 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ template "fullname" . }}
name: {{ template "nginx.fullname" . }}
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
app: {{ template "nginx.name" . }}
# This declares the resource to be a hook. By convention, we also name the
# file "pre-install-XXX.yaml", but Helm itself doesn't care about file names.
annotations:

@ -1,12 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ template "fullname" . }}-service-test"
name: "{{ template "nginx.fullname" . }}-service-test"
labels:
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app: {{ template "name" . }}
app: {{ template "nginx.name" . }}
annotations:
"helm.sh/hook": test-success
spec:
@ -14,5 +14,5 @@ spec:
- name: curl
image: radial/busyboxplus:curl
command: ['curl']
args: ['{{ template "fullname" . }}:{{ .Values.service.port }}']
args: ['{{ template "nginx.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

@ -6,11 +6,11 @@ metadata:
{{ toYaml .Values.service.annotations | indent 4 }}
{{- end }}
labels:
app: {{ template "name" . }}
app: {{ template "nginx.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
name: {{ template "fullname" . }}
name: {{ template "nginx.fullname" . }}
spec:
# Provides options for the service so chart users have the full choice
type: "{{ .Values.service.type }}"
@ -35,5 +35,5 @@ spec:
nodePort: {{ .Values.service.nodePort }}
{{- end }}
selector:
app: {{ template "name" . }}
app: {{ template "nginx.name" . }}
release: {{ .Release.Name }}

@ -30,7 +30,7 @@ helm fetch [flags] [chart URL | repo/chartname] [...]
--ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file
-d, --destination string location to write the chart. If this and tardir are specified, tardir is appended to this (default ".")
--devel use development versions, too. Equivalent to version '>0.0.0-a'. If --version is set, this is ignored.
--devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.
--key-file string identify HTTPS client using this SSL key file
--keyring string keyring containing public keys (default "~/.gnupg/pubring.gpg")
--prov fetch the provenance file, but don't perform verification

@ -39,6 +39,9 @@ helm init
--history-max int limit the maximum number of revisions saved per release. Use 0 for no limit.
--local-repo-url string URL for local repository (default "http://127.0.0.1:8879/charts")
--net-host install Tiller with net=host
--node-selectors string labels to specify the node on which Tiller is installed (app=tiller,helm=rocks)
-o, --output OutputFormat skip installation and output Tiller's manifest in specified format (json or yaml)
--override stringArray override values for the Tiller Deployment manifest (can specify multiple or separate values with commas: key1=val1,key2=val2)
--service-account string name of service account
--skip-refresh do not refresh (download) the local repository cache
--stable-repo-url string URL for stable repository (default "https://kubernetes-charts.storage.googleapis.com")
@ -64,4 +67,4 @@ helm init
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 10-Aug-2017
###### Auto generated by spf13/cobra on 10-Oct-2017

@ -8,8 +8,8 @@ install a chart archive
This command installs a chart archive.
The install argument must be either a relative path to a chart directory or the
name of a chart in the current working directory.
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.
@ -70,7 +70,7 @@ helm install [CHART]
```
--ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file
--devel use development versions, too. Equivalent to version '>0.0.0-a'. If --version is set, this is ignored.
--devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.
--dry-run simulate an install
--key-file string identify HTTPS client using this SSL key file
--keyring string location of public keys used for verification (default "~/.gnupg/pubring.gpg")
@ -87,7 +87,7 @@ helm install [CHART]
--tls-cert string path to TLS certificate file (default "$HELM_HOME/cert.pem")
--tls-key string path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify enable TLS for request and verify remote
-f, --values valueFiles specify values in a YAML file (can specify multiple) (default [])
-f, --values valueFiles specify values in a YAML file or a URL(can specify multiple) (default [])
--verify verify the package before installing it
--version string specify the exact chart version to install. If this is not specified, the latest version is installed
--wait 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

@ -38,7 +38,7 @@ helm upgrade [RELEASE] [CHART]
```
--ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file
--devel use development versions, too. Equivalent to version '>0.0.0-a'. If --version is set, this is ignored.
--devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.
--dry-run simulate an upgrade
--force force resource update through delete/recreate if needed
-i, --install if a release by this name doesn't already exist, run an install
@ -57,7 +57,7 @@ helm upgrade [RELEASE] [CHART]
--tls-cert string path to TLS certificate file (default "$HELM_HOME/cert.pem")
--tls-key string path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify enable TLS for request and verify remote
-f, --values valueFiles specify values in a YAML file (can specify multiple) (default [])
-f, --values valueFiles specify values in a YAML file or a URL(can specify multiple) (default [])
--verify verify the provenance of the chart before upgrading
--version string specify the exact chart version to use. If this is not specified, the latest version is used
--wait 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

@ -197,6 +197,144 @@ Tiller can then be re-installed from the client with:
$ helm init
```
## Advanced Usage
`helm init` provides additional flags for modifying Tiller's deployment
manifest before it is installed.
### Using `--node-selectors`
The `--node-selectors` flag allows us to specify the node labels required
for scheduling the Tiller pod.
The example below will create the specified label under the nodeSelector
property.
```
helm init --node-selectors "beta.kubernetes.io/os"="linux"
```
The installed deployment manifest will contain our node selector label.
```
...
spec:
template:
spec:
nodeSelector:
beta.kubernetes.io/os: linux
...
```
### Using `--override`
`--override` allows you to specify properties of Tiller's
deployment manifest. Unlike the `--set` command used elsewhere in Helm,
`helm init --override` manipulates the specified properties of the final
manifest (there is no "values" file). Therefore you may specify any valid
value for any valid property in the deployment manifest.
#### Override annotation
In the example below we use `--override` to add the revision property and set
its value to 1.
```
helm init --set metadata.annotations."deployment\.kubernetes\.io/revision"="1"
```
Output:
```
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
...
```
#### Override affinity
In the example below we set properties for node affinity. Multiple
`--override` commands may be combined to modify different properties of the
same list item.
```
helm init --override "spec.template.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].weight"="1" --override "spec.template.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[0].key"="e2e-az-name"
```
The specified properties are combined into the
"preferredDuringSchedulingIgnoredDuringExecution" property's first
list item.
```
...
spec:
strategy: {}
template:
...
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: e2e-az-name
operator: ""
weight: 1
...
```
### Using `--output`
The `--output` flag allows us skip the installation of Tiller's deployment
manifest and simply output the deployment manifest to stdout in either
JSON or YAML format. The output may then be modified with tools like `jq`
and installed manually with `kubectl`.
In the example below we execute `helm init` with the `--output json` flag.
```
helm init --output json
```
The Tiller installation is skipped and the manifest is output to stdout
in JSON format.
```
"apiVersion": "extensions/v1beta1",
"kind": "Deployment",
"metadata": {
"creationTimestamp": null,
"labels": {
"app": "helm",
"name": "tiller"
},
"name": "tiller-deploy",
"namespace": "kube-system"
},
...
```
### Storage backends
By default, `tiller` stores release information in `ConfigMaps` in the namespace
where it is running. As of Helm 2.7.0, there is now a beta storage backend that
uses `Secrets` for storing release information. This was added for additional
security in protecting charts in conjunction with the release of `Secret`
encryption in Kubernetes.
To enable the secrets backend, you'll need to init Tiller with the following
options:
```shell
helm init --override 'spec.template.spec.containers[0].command'='{/tiller,--storage=secret}'
```
Currently, if you want to switch from the default backend to the secrets
backend, you'll have to do the migration for this on your own. When this backend
graduates from beta, there will be a more official path of migration
## Conclusion
In most cases, installation is as simple as getting a pre-built `helm` binary

@ -27,7 +27,7 @@ Kubernetes bootstrapped with `kubeadm` is known to work on the following Linux
distributions:
- Ubuntu 16.04
- CAN SOMEONE CONFIRM ON FEDORA?
- Fedora release 25
Some versions of Helm (v2.0.0-beta2) require you to `export KUBECONFIG=/etc/kubernetes/admin.conf`
or create a `~/.kube/config`.
@ -35,3 +35,7 @@ or create a `~/.kube/config`.
## Container Linux by CoreOS
Helm requires that kubelet have access to a copy of the `socat` program to proxy connections to the Tiller API. On Container Linux the Kubelet runs inside of a [hyperkube](https://github.com/kubernetes/kubernetes/tree/master/cluster/images/hyperkube) container image that has socat. So, even though Container Linux doesn't ship `socat` the container filesystem running kubelet does have socat. To learn more read the [Kubelet Wrapper](https://coreos.com/kubernetes/docs/latest/kubelet-wrapper.html) docs.
## Openshift
Helm works straightforward on OpenShift Online, OpenShift Dedicated, OpenShift Container Platform (version >= 3.6) or OpenShift Origin (version >= 3.6). To learn more read [this blog](https://blog.openshift.com/getting-started-helm-openshift/) post.

@ -18,8 +18,8 @@ helm\-install \- install a chart archive
This command installs a chart archive.
.PP
The install argument must be either a relative path to a chart directory or the
name of a chart in the current working directory.
The install argument must be a chart reference, a path to a packaged chart,
a path to an unpacked chart directory or a URL.
.PP
To override values in a chart, use either the '\-\-values' flag and pass in a file
@ -192,7 +192,7 @@ charts in a repository, use 'helm search'.
.PP
\fB\-f\fP, \fB\-\-values\fP=[]
specify values in a YAML file (can specify multiple)
specify values in a YAML file or a URL(can specify multiple)
.PP
\fB\-\-verify\fP[=false]

@ -143,7 +143,7 @@ $ helm upgrade \-\-set foo=bar \-\-set foo=newbar redis ./redis
.PP
\fB\-f\fP, \fB\-\-values\fP=[]
specify values in a YAML file (can specify multiple)
specify values in a YAML file or a URL(can specify multiple)
.PP
\fB\-\-verify\fP[=false]

437
glide.lock generated

@ -1,14 +1,22 @@
hash: 91eba16992e639203a273b247807bb990901cc0ef7e744991722106fc9db0956
updated: 2017-09-18T15:49:23.687863166-04:00
hash: 650f1d4cd9e9dc5ba76480a5465923ce1bbd11b8fa956b644aaf975e8f7e1f33
updated: 2017-10-12T13:08:50.435765-07:00
imports:
- name: cloud.google.com/go
version: 3b1ae45394a234c385be014e9a488f2bb6eef821
subpackages:
- compute/metadata
- internal
- name: github.com/aokoli/goutils
version: 9c37978a95bd5c709a15883b6242714ea6709e64
- name: github.com/asaskevich/govalidator
version: 7664702784775e51966f0885f5cd27435916517b
- name: github.com/Azure/go-autorest
version: d7c034a8af24eda120dd6460bfcd6d9ed14e43ca
version: 58f6f26e200fa5dfb40c9cd1c83f3e2c860d779d
subpackages:
- autorest
- autorest/adal
- autorest/azure
- autorest/date
- name: github.com/beorn7/perks
version: 3ac7bf7a47d159a033b107610db8a1b6575507a4
subpackages:
@ -22,42 +30,59 @@ imports:
subpackages:
- md2man
- name: github.com/davecgh/go-spew
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
version: 782f4967f2dc4564575ca782fe2d04090b5faca8
subpackages:
- spew
- name: github.com/dgrijalva/jwt-go
version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20
- name: github.com/docker/distribution
version: 03efb43768979f4d2ea5187bef995656441829e5
version: edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c
subpackages:
- digest
- digestset
- reference
- name: github.com/docker/engine-api
version: dea108d3aa0c67d7162a3fd8aa65f38a430019fd
subpackages:
- name: github.com/docker/docker
version: 4f3616fb1c112e206b88cb7a9922bf49067a7756
subpackages:
- api
- api/types
- api/types/blkiodev
- api/types/container
- api/types/events
- api/types/filters
- api/types/image
- api/types/mount
- api/types/network
- api/types/registry
- api/types/strslice
- api/types/swarm
- api/types/swarm/runtime
- api/types/time
- api/types/versions
- api/types/volume
- client
- client/transport
- client/transport/cancellable
- types
- types/blkiodev
- types/container
- types/filters
- types/network
- types/reference
- types/registry
- types/strslice
- types/time
- types/versions
- pkg/ioutils
- pkg/jsonlog
- pkg/jsonmessage
- pkg/longpath
- pkg/mount
- pkg/stdcopy
- pkg/symlink
- pkg/system
- pkg/term
- pkg/term/windows
- pkg/tlsconfig
- name: github.com/docker/go-connections
version: f549a9393d05688dff0992ef3efd8bbe6c628aeb
version: 3ede32e2033de7505e6500d6c868c2b9ed9f169d
subpackages:
- nat
- sockets
- tlsconfig
- name: github.com/docker/go-units
version: e30f1e79f3cd72542f2026ceec18d3bd67ab859c
version: 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
- name: github.com/docker/spdystream
version: 449fdfce4d962303d702fec724ef0ad181c92528
subpackages:
- spdy
- name: github.com/emicklei/go-restful
version: ff4f55a206334ef123e4f79bbf348980da81ca46
subpackages:
@ -65,31 +90,19 @@ imports:
- name: github.com/emicklei/go-restful-swagger12
version: dcef7f55730566d41eae5db10e7d6981829720f6
- name: github.com/evanphx/json-patch
version: ba18e35c5c1b36ef6334cad706eb681153d2d379
version: 944e07253867aacae43c04b2e6a239005443f33a
- name: github.com/exponent-io/jsonpath
version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5
- name: github.com/facebookgo/atomicfile
version: 2de1f203e7d5e386a6833233882782932729f27e
- name: github.com/facebookgo/symwalk
version: 42004b9f322246749dd73ad71008b1f3160c0052
- name: github.com/fatih/camelcase
version: f6a740d52f961c60348ebb109adde9f4635d7540
- name: github.com/ghodss/yaml
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
- name: github.com/go-openapi/analysis
version: b44dc874b601d9e4e2f6e19140e794ba24bead3b
- name: github.com/go-openapi/errors
version: d24ebc2075bad502fac3a8ae27aa6dd58e1952dc
- name: github.com/go-openapi/jsonpointer
version: 46af16f9f7b149af66e5d1bd010e3574dc06de98
- name: github.com/go-openapi/jsonreference
version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272
- name: github.com/go-openapi/loads
version: 18441dfa706d924a39a030ee2c3b1d8d81917b38
- name: github.com/go-openapi/spec
version: 6aced65f8501fe1217321abf0749d354824ba2ff
- name: github.com/go-openapi/strfmt
version: d65c7fdb29eca313476e529628176fe17e58c488
- name: github.com/go-openapi/swag
version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72
- name: github.com/gobwas/glob
@ -105,31 +118,8 @@ imports:
- name: github.com/gogo/protobuf
version: c0656edd0d9eab7c66d1eb0c568f9039345796f7
subpackages:
- gogoproto
- plugin/compare
- plugin/defaultcheck
- plugin/description
- plugin/embedcheck
- plugin/enumstringer
- plugin/equal
- plugin/face
- plugin/gostring
- plugin/marshalto
- plugin/oneofcheck
- plugin/populate
- plugin/size
- plugin/stringer
- plugin/testgen
- plugin/union
- plugin/unmarshal
- proto
- protoc-gen-gogo/descriptor
- protoc-gen-gogo/generator
- protoc-gen-gogo/grpc
- protoc-gen-gogo/plugin
- sortkeys
- vanity
- vanity/command
- name: github.com/golang/glog
version: 44145f04b68cf362d9c4df2182967c2275eaefed
- name: github.com/golang/groupcache
@ -137,30 +127,57 @@ imports:
subpackages:
- lru
- name: github.com/golang/protobuf
version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
version: 4bd1920723d7b7c925de087aa32e2187708897f7
subpackages:
- proto
- ptypes
- ptypes/any
- ptypes/duration
- ptypes/timestamp
- name: github.com/google/btree
version: 7d79101e329e5a3adf994758c578dab82b90c017
- name: github.com/google/gofuzz
version: bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5
version: 44d81051d367757e1c7c6a5a86423ece9afcf63c
- name: github.com/googleapis/gnostic
version: 0c5108395e2debce0d731cf0287ddf7242066aba
subpackages:
- OpenAPIv2
- compiler
- extensions
- name: github.com/gophercloud/gophercloud
version: 2bf16b94fdd9b01557c4d076e567fe5cbbe5a961
subpackages:
- openstack
- openstack/identity/v2/tenants
- openstack/identity/v2/tokens
- openstack/identity/v3/tokens
- openstack/utils
- pagination
- name: github.com/gosuri/uitable
version: 36ee7e946282a3fb1cfecd476ddc9b35d8847e42
subpackages:
- util/strutil
- util/wordwrap
- name: github.com/gregjones/httpcache
version: 787624de3eb7bd915c329cba748687a3b22666a6
subpackages:
- diskcache
- name: github.com/grpc-ecosystem/go-grpc-prometheus
version: 0c1b191dbfe51efdabe3c14b9f6f3b96429e0722
- name: github.com/hashicorp/golang-lru
version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
subpackages:
- simplelru
- name: github.com/howeyc/gopass
version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d
version: bf9dde6d0d2c004a008c27aaee91170c786f6db8
- name: github.com/huandu/xstrings
version: 3959339b333561bf62a38b424fd41517c2c90f40
- name: github.com/imdario/mergo
version: 6633656539c1639d9d78127b7d47c622b5d7b6dc
- name: github.com/inconshreveable/mousetrap
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- name: github.com/json-iterator/go
version: 36b14963da70d11297d313183d7e6388c8510e1e
- name: github.com/juju/ratelimit
version: 5b9ff866471762aa2ab2dced63c9fb6f53921342
- name: github.com/mailru/easyjson
@ -172,7 +189,7 @@ imports:
- name: github.com/Masterminds/semver
version: 517734cc7d6470c0d07130e40fd40bdeb9bcd3fd
- name: github.com/Masterminds/sprig
version: 4c164950cd0a8d3724ddb78982e2c56dc7f47112
version: efda631a76d70875162cdc25ffa0d0164bf69758
- name: github.com/Masterminds/vcs
version: 3084677c2c188840777bff30054f2b553729d329
- name: github.com/mattn/go-runewidth
@ -181,12 +198,19 @@ imports:
version: fc2b8d3a73c4867e51861bbdd5ae3c1f0869dd6a
subpackages:
- pbutil
- name: github.com/mitchellh/mapstructure
version: 740c764bc6149d3f1806231418adb9f52c11bcbf
- name: github.com/naoina/go-stringutil
version: 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b
- name: github.com/opencontainers/go-digest
version: a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
- name: github.com/opencontainers/image-spec
version: 372ad780f63454fbbbbcc7cf80e5b90245c13e13
subpackages:
- specs-go
- specs-go/v1
- name: github.com/pborman/uuid
version: ca53cad383cad2479bbba7f7a1a05797ec1386e4
- name: github.com/peterbourgon/diskv
version: 5f041e8faa004a95c88a202771f4cc3e991971e6
- name: github.com/prometheus/client_golang
version: c5b7fccd204277076155f10851dad72b76a49317
subpackages:
@ -228,9 +252,8 @@ imports:
version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74
subpackages:
- codec
- codec/codecgen
- name: golang.org/x/crypto
version: d172538b2cfce0c13cee31e647d0367aa8cd2486
version: 81e90905daefcd6fd217b62423c0908922eadb30
subpackages:
- cast5
- openpgp
@ -244,9 +267,10 @@ imports:
- scrypt
- ssh/terminal
- name: golang.org/x/net
version: f2499483f923065a842d38eb4c7f1927e6fc6e6d
version: 1c05540f6879653db88113bc4a2b70aec4bd491f
subpackages:
- context
- context/ctxhttp
- http2
- http2/hpack
- idna
@ -254,19 +278,26 @@ imports:
- lex/httplex
- trace
- name: golang.org/x/oauth2
version: 3c3a985cb79f52a3190fbc056984415ca6763d01
version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4
subpackages:
- google
- internal
- jws
- jwt
- name: golang.org/x/sys
version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9
version: 43eea11bc92608addb41b8a406b0407495c106f6
subpackages:
- unix
- windows
- name: golang.org/x/text
version: 2910a502d2bf9e43193af9d68ca516529614eed3
version: b19bf474d317b857955b12035d2c5acb57ce8b01
subpackages:
- cases
- encoding
- encoding/internal
- encoding/internal/identifier
- encoding/unicode
- internal
- internal/tag
- internal/utf8internal
- language
@ -277,6 +308,18 @@ imports:
- unicode/bidi
- unicode/norm
- width
- name: google.golang.org/appengine
version: 12d5545dc1cfa6047a286d5e853841b6471f4c19
subpackages:
- internal
- internal/app_identity
- internal/base
- internal/datastore
- internal/log
- internal/modules
- internal/remote_api
- internal/urlfetch
- urlfetch
- name: google.golang.org/grpc
version: 8050b9cbc271307e5a716a9d782803d09b0d6f2d
subpackages:
@ -293,37 +336,99 @@ imports:
- transport
- name: gopkg.in/inf.v0
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
- name: gopkg.in/mgo.v2
version: 3f83fa5005286a7fe593b055f0d7771a7dce4655
subpackages:
- bson
- name: gopkg.in/yaml.v2
version: 53feefa2559fb8dfa8d81baad31be332c97d6c77
- name: k8s.io/api
version: 4fe9229aaa9d704f8a2a21cdcd50de2bbb6e1b57
version: cadaf100c0a3dd6b254f320d6d651df079ec8e0a
subpackages:
- admission/v1alpha1
- admissionregistration/v1alpha1
- apps/v1beta1
- apps/v1beta2
- authentication/v1
- authentication/v1beta1
- authorization/v1
- authorization/v1beta1
- autoscaling/v1
- autoscaling/v2alpha1
- autoscaling/v2beta1
- batch/v1
- batch/v1beta1
- batch/v2alpha1
- certificates/v1beta1
- core/v1
- extensions/v1beta1
- imagepolicy/v1alpha1
- networking/v1
- policy/v1beta1
- rbac/v1
- rbac/v1alpha1
- rbac/v1beta1
- scheduling/v1alpha1
- settings/v1alpha1
- storage/v1
- storage/v1beta1
- name: k8s.io/apiextensions-apiserver
version: a5bbfd114a9b122acd741c61d88c84812375d9e1
subpackages:
- pkg/features
- name: k8s.io/apimachinery
version: 3b05bbfa0a45413bfa184edbf9af617e277962fb
subpackages:
- pkg/api/equality
- pkg/api/errors
- pkg/api/meta
- pkg/api/resource
- pkg/api/validation
- pkg/apimachinery
- pkg/apimachinery/announced
- pkg/apimachinery/registered
- pkg/apis/meta/internalversion
- pkg/apis/meta/v1
- pkg/apis/meta/v1/unstructured
- pkg/apis/meta/v1/validation
- pkg/apis/meta/v1alpha1
- pkg/conversion
- pkg/conversion/queryparams
- pkg/conversion/unstructured
- pkg/fields
- pkg/labels
- pkg/runtime
- pkg/runtime/schema
- pkg/runtime/serializer
- pkg/runtime/serializer/json
- pkg/runtime/serializer/protobuf
- pkg/runtime/serializer/recognizer
- pkg/runtime/serializer/streaming
- pkg/runtime/serializer/versioning
- pkg/selection
- pkg/types
- pkg/util/cache
- pkg/util/clock
- pkg/util/diff
- pkg/util/errors
- pkg/util/framer
- pkg/util/httpstream
- pkg/util/httpstream/spdy
- pkg/util/intstr
- pkg/util/json
- pkg/util/mergepatch
- pkg/util/net
- pkg/util/rand
- pkg/util/runtime
- pkg/util/sets
- pkg/util/strategicpatch
- pkg/util/uuid
- pkg/util/validation
- pkg/util/validation/field
- pkg/util/wait
- pkg/util/yaml
- pkg/version
- pkg/watch
- third_party/forked/golang/json
- third_party/forked/golang/netutil
- third_party/forked/golang/reflect
- name: k8s.io/apiserver
version: 087d1a2efeb6296f04bb0f54e7af9890052aa6d7
version: c1e53d745d0fe45bf7d5d44697e6eface25fceca
subpackages:
- pkg/admission
- pkg/apis/apiserver
@ -337,20 +442,118 @@ imports:
- pkg/features
- pkg/util/feature
- pkg/util/flag
- name: k8s.io/client-go
version: 82aa063804cf055e16e8911250f888bc216e8b61
subpackages:
- discovery
- discovery/fake
- dynamic
- informers/apps/v1beta1
- informers/core/v1
- informers/extensions/v1beta1
- informers/internalinterfaces
- kubernetes
- kubernetes/fake
- kubernetes/scheme
- kubernetes/typed/admissionregistration/v1alpha1
- kubernetes/typed/admissionregistration/v1alpha1/fake
- kubernetes/typed/apps/v1beta1
- kubernetes/typed/apps/v1beta1/fake
- kubernetes/typed/apps/v1beta2
- kubernetes/typed/apps/v1beta2/fake
- kubernetes/typed/authentication/v1
- kubernetes/typed/authentication/v1/fake
- kubernetes/typed/authentication/v1beta1
- kubernetes/typed/authentication/v1beta1/fake
- kubernetes/typed/authorization/v1
- kubernetes/typed/authorization/v1/fake
- kubernetes/typed/authorization/v1beta1
- kubernetes/typed/authorization/v1beta1/fake
- kubernetes/typed/autoscaling/v1
- kubernetes/typed/autoscaling/v1/fake
- kubernetes/typed/autoscaling/v2beta1
- kubernetes/typed/autoscaling/v2beta1/fake
- kubernetes/typed/batch/v1
- kubernetes/typed/batch/v1/fake
- kubernetes/typed/batch/v1beta1
- kubernetes/typed/batch/v1beta1/fake
- kubernetes/typed/batch/v2alpha1
- kubernetes/typed/batch/v2alpha1/fake
- kubernetes/typed/certificates/v1beta1
- kubernetes/typed/certificates/v1beta1/fake
- kubernetes/typed/core/v1
- kubernetes/typed/core/v1/fake
- kubernetes/typed/extensions/v1beta1
- kubernetes/typed/extensions/v1beta1/fake
- kubernetes/typed/networking/v1
- kubernetes/typed/networking/v1/fake
- kubernetes/typed/policy/v1beta1
- kubernetes/typed/policy/v1beta1/fake
- kubernetes/typed/rbac/v1
- kubernetes/typed/rbac/v1/fake
- kubernetes/typed/rbac/v1alpha1
- kubernetes/typed/rbac/v1alpha1/fake
- kubernetes/typed/rbac/v1beta1
- kubernetes/typed/rbac/v1beta1/fake
- kubernetes/typed/scheduling/v1alpha1
- kubernetes/typed/scheduling/v1alpha1/fake
- kubernetes/typed/settings/v1alpha1
- kubernetes/typed/settings/v1alpha1/fake
- kubernetes/typed/storage/v1
- kubernetes/typed/storage/v1/fake
- kubernetes/typed/storage/v1beta1
- kubernetes/typed/storage/v1beta1/fake
- listers/apps/v1beta1
- listers/core/v1
- listers/extensions/v1beta1
- pkg/version
- plugin/pkg/client/auth
- plugin/pkg/client/auth/azure
- plugin/pkg/client/auth/gcp
- plugin/pkg/client/auth/oidc
- plugin/pkg/client/auth/openstack
- rest
- rest/fake
- rest/watch
- testing
- third_party/forked/golang/template
- tools/auth
- tools/cache
- tools/clientcmd
- tools/clientcmd/api
- tools/clientcmd/api/latest
- tools/clientcmd/api/v1
- tools/metrics
- tools/pager
- tools/portforward
- tools/record
- tools/reference
- transport
- transport/spdy
- util/cert
- util/flowcontrol
- util/homedir
- util/integer
- util/jsonpath
- util/retry
- util/workqueue
- name: k8s.io/kube-openapi
version: 868f2f29720b192240e18284659231b440f9cda5
subpackages:
- pkg/common
- name: k8s.io/kubernetes
version: d3ada0119e776222f11ec7945e6d860061339aad
version: 0b9efaeb34a2fc51ff8e4d34ad9bc6375459c4a4
subpackages:
- cmd/kubeadm/app/apis/kubeadm
- federation/apis/federation
- federation/apis/federation/install
- federation/apis/federation/v1beta1
- federation/client/clientset_generated/federation_internalclientset
- federation/client/clientset_generated/federation_internalclientset/scheme
- federation/client/clientset_generated/federation_internalclientset/typed/autoscaling/internalversion
- federation/client/clientset_generated/federation_internalclientset/typed/batch/internalversion
- federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion
- federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion
- federation/client/clientset_generated/federation_internalclientset/typed/federation/internalversion
- federation/client/clientset_generated/federation_clientset
- federation/client/clientset_generated/federation_clientset/scheme
- federation/client/clientset_generated/federation_clientset/typed/autoscaling/v1
- federation/client/clientset_generated/federation_clientset/typed/batch/v1
- federation/client/clientset_generated/federation_clientset/typed/core/v1
- federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1
- federation/client/clientset_generated/federation_clientset/typed/federation/v1beta1
- pkg/api
- pkg/api/events
- pkg/api/helper
@ -366,7 +569,6 @@ imports:
- pkg/api/v1/helper
- pkg/api/v1/helper/qos
- pkg/api/v1/pod
- pkg/api/v1/ref
- pkg/api/validation
- pkg/apis/admission
- pkg/apis/admission/install
@ -377,6 +579,7 @@ imports:
- pkg/apis/apps
- pkg/apis/apps/install
- pkg/apis/apps/v1beta1
- pkg/apis/apps/v1beta2
- pkg/apis/authentication
- pkg/apis/authentication/install
- pkg/apis/authentication/v1
@ -388,10 +591,11 @@ imports:
- pkg/apis/autoscaling
- pkg/apis/autoscaling/install
- pkg/apis/autoscaling/v1
- pkg/apis/autoscaling/v2alpha1
- pkg/apis/autoscaling/v2beta1
- pkg/apis/batch
- pkg/apis/batch/install
- pkg/apis/batch/v1
- pkg/apis/batch/v1beta1
- pkg/apis/batch/v2alpha1
- pkg/apis/certificates
- pkg/apis/certificates/install
@ -413,8 +617,12 @@ imports:
- pkg/apis/policy/v1beta1
- pkg/apis/rbac
- pkg/apis/rbac/install
- pkg/apis/rbac/v1
- pkg/apis/rbac/v1alpha1
- pkg/apis/rbac/v1beta1
- pkg/apis/scheduling
- pkg/apis/scheduling/install
- pkg/apis/scheduling/v1alpha1
- pkg/apis/settings
- pkg/apis/settings/install
- pkg/apis/settings/v1alpha1
@ -424,28 +632,6 @@ imports:
- pkg/apis/storage/v1
- pkg/apis/storage/v1beta1
- pkg/capabilities
- pkg/client/clientset_generated/clientset
- pkg/client/clientset_generated/clientset/scheme
- pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1
- pkg/client/clientset_generated/clientset/typed/apps/v1beta1
- pkg/client/clientset_generated/clientset/typed/authentication/v1
- pkg/client/clientset_generated/clientset/typed/authentication/v1beta1
- pkg/client/clientset_generated/clientset/typed/authorization/v1
- pkg/client/clientset_generated/clientset/typed/authorization/v1beta1
- pkg/client/clientset_generated/clientset/typed/autoscaling/v1
- pkg/client/clientset_generated/clientset/typed/autoscaling/v2alpha1
- pkg/client/clientset_generated/clientset/typed/batch/v1
- pkg/client/clientset_generated/clientset/typed/batch/v2alpha1
- pkg/client/clientset_generated/clientset/typed/certificates/v1beta1
- pkg/client/clientset_generated/clientset/typed/core/v1
- pkg/client/clientset_generated/clientset/typed/extensions/v1beta1
- pkg/client/clientset_generated/clientset/typed/networking/v1
- pkg/client/clientset_generated/clientset/typed/policy/v1beta1
- pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1
- pkg/client/clientset_generated/clientset/typed/rbac/v1beta1
- pkg/client/clientset_generated/clientset/typed/settings/v1alpha1
- pkg/client/clientset_generated/clientset/typed/storage/v1
- pkg/client/clientset_generated/clientset/typed/storage/v1beta1
- pkg/client/clientset_generated/internalclientset
- pkg/client/clientset_generated/internalclientset/fake
- pkg/client/clientset_generated/internalclientset/scheme
@ -473,24 +659,19 @@ imports:
- pkg/client/clientset_generated/internalclientset/typed/policy/internalversion/fake
- pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion
- pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/fake
- pkg/client/clientset_generated/internalclientset/typed/scheduling/internalversion
- pkg/client/clientset_generated/internalclientset/typed/scheduling/internalversion/fake
- pkg/client/clientset_generated/internalclientset/typed/settings/internalversion
- pkg/client/clientset_generated/internalclientset/typed/settings/internalversion/fake
- pkg/client/clientset_generated/internalclientset/typed/storage/internalversion
- pkg/client/clientset_generated/internalclientset/typed/storage/internalversion/fake
- pkg/client/informers/informers_generated/externalversions/apps/v1beta1
- pkg/client/informers/informers_generated/externalversions/core/v1
- pkg/client/informers/informers_generated/externalversions/extensions/v1beta1
- pkg/client/informers/informers_generated/externalversions/internalinterfaces
- pkg/client/leaderelection/resourcelock
- pkg/client/listers/apps/v1beta1
- pkg/client/listers/core/v1
- pkg/client/listers/extensions/v1beta1
- pkg/client/retry
- pkg/client/unversioned
- pkg/controller
- pkg/controller/daemon
- pkg/controller/daemon/util
- pkg/controller/deployment/util
- pkg/controller/history
- pkg/controller/statefulset
- pkg/credentialprovider
- pkg/features
- pkg/fieldpath
@ -498,9 +679,13 @@ imports:
- pkg/kubectl/cmd/testing
- pkg/kubectl/cmd/util
- pkg/kubectl/cmd/util/openapi
- pkg/kubectl/cmd/util/openapi/validation
- pkg/kubectl/plugins
- pkg/kubectl/resource
- pkg/kubectl/util
- pkg/kubectl/util/hash
- pkg/kubectl/util/slice
- pkg/kubectl/validation
- pkg/kubelet/apis
- pkg/kubelet/qos
- pkg/kubelet/types
@ -510,16 +695,18 @@ imports:
- pkg/registry/rbac/validation
- pkg/security/apparmor
- pkg/serviceaccount
- pkg/util
- pkg/util/exec
- pkg/util/file
- pkg/util/hash
- pkg/util/io
- pkg/util/labels
- pkg/util/metrics
- pkg/util/mount
- pkg/util/net/sets
- pkg/util/node
- pkg/util/parsers
- pkg/util/pointer
- pkg/util/slice
- pkg/util/taints
- pkg/version
- pkg/volume/util
- pkg/watch/json
@ -529,6 +716,7 @@ imports:
- plugin/pkg/scheduler/api
- plugin/pkg/scheduler/schedulercache
- plugin/pkg/scheduler/util
- staging/src/k8s.io/apimachinery/pkg/util/rand
- name: k8s.io/metrics
version: 8efbc8e22d00b9c600afec5f1c14073fd2412fce
subpackages:
@ -537,6 +725,11 @@ imports:
- pkg/client/clientset_generated/clientset
- pkg/client/clientset_generated/clientset/scheme
- pkg/client/clientset_generated/clientset/typed/metrics/v1alpha1
- name: k8s.io/utils
version: 9fdc871a36f37980dd85f96d576b20d564cc0784
subpackages:
- exec
- exec/testing
- name: vbom.ml/util
version: db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394
repo: https://github.com/fvbommel/util.git

@ -14,32 +14,38 @@ import:
- package: github.com/imdario/mergo
version: 6633656539c1639d9d78127b7d47c622b5d7b6dc
- package: github.com/Masterminds/sprig
version: ^2.13
version: ^2.14
- package: github.com/ghodss/yaml
- package: github.com/Masterminds/semver
version: ~1.3.1
- package: github.com/technosophos/moniker
- package: github.com/golang/protobuf
version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
version: 4bd1920723d7b7c925de087aa32e2187708897f7
subpackages:
- proto
- ptypes/any
- ptypes/timestamp
- package: google.golang.org/grpc
version: 1.2.1
# 1.8.1 libs are hosed and need some manual intervention, so pinning to 1.8.0 for now
# so others aren't getting errors when running `glide up`
- package: k8s.io/kubernetes
version: ~1.7.0
version: 1.8.0
- package: github.com/gosuri/uitable
- package: github.com/asaskevich/govalidator
version: ^4.0.0
- package: golang.org/x/crypto
subpackages:
- openpgp
# pin version of golang.org/x/sys that is compatible with golang.org/x/crypto
- package: golang.org/x/sys
version: 43eea11
subpackages:
- unix
- windows
- package: github.com/gobwas/glob
version: ^0.2.1
- package: github.com/evanphx/json-patch
- package: github.com/facebookgo/atomicfile
- package: github.com/facebookgo/symwalk
- package: github.com/BurntSushi/toml
version: ~0.3.0
- package: github.com/naoina/go-stringutil
@ -50,37 +56,6 @@ import:
- package: vbom.ml/util
repo: https://github.com/fvbommel/util.git
vcs: git
- package: github.com/docker/distribution
version: ~2.4.0
# hacks for kubernetes v1.7
- package: cloud.google.com/go
- package: github.com/Azure/go-autorest
version: d7c034a8af24eda120dd6460bfcd6d9ed14e43ca
- package: github.com/dgrijalva/jwt-go
- package: github.com/docker/spdystream
- package: github.com/go-openapi/analysis
version: b44dc874b601d9e4e2f6e19140e794ba24bead3b
- package: github.com/go-openapi/errors
version: d24ebc2075bad502fac3a8ae27aa6dd58e1952dc
- package: github.com/go-openapi/loads
version: 18441dfa706d924a39a030ee2c3b1d8d81917b38
- package: github.com/go-openapi/spec
version: 6aced65f8501fe1217321abf0749d354824ba2ff
- package: github.com/google/gofuzz
- package: github.com/hashicorp/golang-lru
- package: github.com/howeyc/gopass
- package: github.com/juju/ratelimit
version: 5b9ff866471762aa2ab2dced63c9fb6f53921342
- package: github.com/pborman/uuid
- package: golang.org/x/oauth2
- package: gopkg.in/inf.v0
- package: github.com/go-openapi/strfmt
- package: github.com/mitchellh/mapstructure
- package: gopkg.in/mgo.v2/bson
ignore:
- k8s.io/client-go
- k8s.io/apimachinery
testImports:
- package: github.com/stretchr/testify

@ -21,6 +21,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"k8s.io/helm/pkg/proto/hapi/chart"
)
@ -111,14 +112,14 @@ const defaultIgnore = `# Patterns to ignore when building packages.
`
const defaultIngress = `{{- if .Values.ingress.enabled -}}
{{- $serviceName := include "fullname" . -}}
{{- $serviceName := include "<CHARTNAME>.fullname" . -}}
{{- $servicePort := .Values.service.externalPort -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "fullname" . }}
name: {{ template "<CHARTNAME>.fullname" . }}
labels:
app: {{ template "name" . }}
app: {{ template "<CHARTNAME>.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
@ -147,9 +148,9 @@ spec:
const defaultDeployment = `apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ template "fullname" . }}
name: {{ template "<CHARTNAME>.fullname" . }}
labels:
app: {{ template "name" . }}
app: {{ template "<CHARTNAME>.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
@ -158,7 +159,7 @@ spec:
template:
metadata:
labels:
app: {{ template "name" . }}
app: {{ template "<CHARTNAME>.name" . }}
release: {{ .Release.Name }}
spec:
containers:
@ -186,9 +187,9 @@ spec:
const defaultService = `apiVersion: v1
kind: Service
metadata:
name: {{ template "fullname" . }}
name: {{ template "<CHARTNAME>.fullname" . }}
labels:
app: {{ template "name" . }}
app: {{ template "<CHARTNAME>.name" . }}
chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
@ -200,7 +201,7 @@ spec:
protocol: TCP
name: {{ .Values.service.name }}
selector:
app: {{ template "name" . }}
app: {{ template "<CHARTNAME>.name" . }}
release: {{ .Release.Name }}
`
@ -210,16 +211,16 @@ const defaultNotes = `1. Get the application URL by running these commands:
http://{{ . }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }})
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "<CHARTNAME>.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
You can watch the status of by running 'kubectl get svc -w {{ template "<CHARTNAME>.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "<CHARTNAME>.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.externalPort }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "<CHARTNAME>.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:{{ .Values.service.internalPort }}
{{- end }}
@ -229,7 +230,7 @@ const defaultHelpers = `{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- define "<CHARTNAME>.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@ -237,7 +238,7 @@ Expand the name of the chart.
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- define "<CHARTNAME>.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@ -318,27 +319,27 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) {
{
// ingress.yaml
path: filepath.Join(cdir, TemplatesDir, IngressFileName),
content: []byte(defaultIngress),
content: []byte(strings.Replace(defaultIngress, "<CHARTNAME>", chartfile.Name, -1)),
},
{
// deployment.yaml
path: filepath.Join(cdir, TemplatesDir, DeploymentName),
content: []byte(defaultDeployment),
content: []byte(strings.Replace(defaultDeployment, "<CHARTNAME>", chartfile.Name, -1)),
},
{
// service.yaml
path: filepath.Join(cdir, TemplatesDir, ServiceName),
content: []byte(defaultService),
content: []byte(strings.Replace(defaultService, "<CHARTNAME>", chartfile.Name, -1)),
},
{
// NOTES.txt
path: filepath.Join(cdir, TemplatesDir, NotesName),
content: []byte(defaultNotes),
content: []byte(strings.Replace(defaultNotes, "<CHARTNAME>", chartfile.Name, -1)),
},
{
// _helpers.tpl
path: filepath.Join(cdir, TemplatesDir, HelpersName),
content: []byte(defaultHelpers),
content: []byte(strings.Replace(defaultHelpers, "<CHARTNAME>", chartfile.Name, -1)),
},
}

@ -28,7 +28,6 @@ import (
"path/filepath"
"strings"
"github.com/facebookgo/symwalk"
"github.com/golang/protobuf/ptypes/any"
"k8s.io/helm/pkg/ignore"
@ -244,7 +243,7 @@ func LoadDir(dir string) (*chart.Chart, error) {
files := []*BufferedFile{}
topdir += string(filepath.Separator)
err = symwalk.Walk(topdir, func(name string, fi os.FileInfo, err error) error {
err = filepath.Walk(topdir, func(name string, fi os.FileInfo, err error) error {
n := strings.TrimPrefix(name, topdir)
// Normalize to / since it will also work on Windows

@ -23,6 +23,7 @@ import (
"github.com/ghodss/yaml"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/version"
)
const (
@ -230,7 +231,7 @@ func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Ch
if existingChart.Metadata.Name != aliasChart.Name {
continue
}
if existingChart.Metadata.Version != aliasChart.Version {
if !version.IsCompatibleRange(aliasChart.Version, existingChart.Metadata.Version) {
continue
}
chartFound = *existingChart
@ -266,7 +267,7 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
for _, existingDependency := range c.Dependencies {
var dependencyFound bool
for _, req := range reqs.Dependencies {
if existingDependency.Metadata.Name == req.Name && existingDependency.Metadata.Version == req.Version {
if existingDependency.Metadata.Name == req.Name && version.IsCompatibleRange(req.Version, existingDependency.Metadata.Version) {
dependencyFound = true
break
}

@ -21,6 +21,7 @@ import (
"strconv"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/version"
)
func TestLoadRequirements(t *testing.T) {
@ -347,11 +348,24 @@ func TestGetAliasDependency(t *testing.T) {
t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Name, aliasChart.Metadata.Name)
}
if req.Dependencies[0].Version != "" {
if !version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
t.Fatalf("Dependency chart version is not in the compatible range")
}
}
// Failure case
req.Dependencies[0].Name = "something-else"
if aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]); aliasChart != nil {
t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name)
}
req.Dependencies[0].Version = "something else which is not in the compatible range"
if version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
t.Fatalf("Dependency chart version which is not in the compatible range should cause a failure other than a success ")
}
}
func TestDependentChartAliases(t *testing.T) {

@ -217,7 +217,12 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge
// If the URL is relative (no scheme), prepend the chart repo's base URL
if !u.IsAbs() {
u, err = url.Parse(rc.URL + "/" + u.Path)
path := u.Path
u, err = url.Parse(rc.URL)
if err != nil {
return u, r.Client, err
}
u.Path = u.Path + path
return u, r.Client, err
}

@ -43,6 +43,7 @@ func TestResolveChartRef(t *testing.T) {
{name: "reference, testing repo", ref: "testing/alpine", expect: "http://example.com/alpine-1.2.3.tgz"},
{name: "reference, version, testing repo", ref: "testing/alpine", version: "0.2.0", expect: "http://example.com/alpine-0.2.0.tgz"},
{name: "reference, version, malformed repo", ref: "malformed/alpine", version: "1.2.3", expect: "http://dl.example.com/alpine-1.2.3.tgz"},
{name: "reference, querystring repo", ref: "testing-querystring/alpine", expect: "http://example.com/alpine-1.2.3.tgz?key=value"},
{name: "full URL, HTTPS, irrelevant version", ref: "https://example.com/foo-1.2.3.tgz", version: "0.1.0", expect: "https://example.com/foo-1.2.3.tgz", fail: true},
{name: "full URL, file", ref: "file:///foo-1.2.3.tgz", fail: true},
{name: "invalid", ref: "invalid-1.2.3", fail: true},

@ -0,0 +1,16 @@
apiVersion: v1
entries:
alpine:
- name: alpine
urls:
- alpine-1.2.3.tgz
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
home: https://k8s.io/helm
sources:
- https://github.com/kubernetes/helm
version: 1.2.3
description: Deploy a basic Alpine Linux pod
keywords: []
maintainers: []
engine: ""
icon: ""

@ -10,3 +10,5 @@ repositories:
url: "http://example.com/charts"
- name: malformed
url: "http://dl.example.com"
- name: testing-querystring
url: "http://example.com?key=value"

@ -17,7 +17,7 @@ limitations under the License.
package portforwarder
import (
"k8s.io/client-go/pkg/api/v1"
"k8s.io/api/core/v1"
)
// These functions are adapted from the "kubernetes" repository's file

@ -19,11 +19,11 @@ package portforwarder
import (
"fmt"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/rest"
"k8s.io/helm/pkg/kube"

@ -19,9 +19,9 @@ package portforwarder
import (
"testing"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/pkg/api/v1"
)
func mockTillerPod() v1.Pod {

@ -27,6 +27,10 @@ import (
"time"
jsonpatch "github.com/evanphx/json-patch"
apps "k8s.io/api/apps/v1beta2"
batch "k8s.io/api/batch/v1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -40,15 +44,12 @@ import (
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/helper"
"k8s.io/kubernetes/pkg/api/v1"
apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
batchinternal "k8s.io/kubernetes/pkg/apis/batch"
batch "k8s.io/kubernetes/pkg/apis/batch/v1"
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
conditions "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/validation"
"k8s.io/kubernetes/pkg/printers"
)
@ -103,13 +104,9 @@ func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shoul
}
func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result {
schema, err := c.Validator(true, c.SchemaCacheDir)
if err != nil {
c.Log("warning: failed to load schema: %s", err)
}
return c.NewBuilder(true).
ContinueOnError().
Schema(schema).
Schema(c.validator()).
NamespaceParam(namespace).
DefaultNamespace().
Stream(reader, "").
@ -117,20 +114,25 @@ func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result
Do()
}
// BuildUnstructured validates for Kubernetes objects and returns unstructured infos.
func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result, error) {
schema, err := c.Validator(true, c.SchemaCacheDir)
func (c *Client) validator() validation.Schema {
const openapi = false // only works on v1.8 clusters
schema, err := c.Validator(true, openapi, c.SchemaCacheDir)
if err != nil {
c.Log("warning: failed to load schema: %s", err)
}
return schema
}
// BuildUnstructured validates for Kubernetes objects and returns unstructured infos.
func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result, error) {
var result Result
b, err := c.NewUnstructuredBuilder(true)
if err != nil {
return result, err
}
result, err = b.ContinueOnError().
Schema(schema).
Schema(c.validator()).
NamespaceParam(namespace).
DefaultNamespace().
Stream(reader, "").
@ -157,6 +159,9 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
if err != nil {
return "", err
}
var objPods = make(map[string][]api.Pod)
missing := []string{}
err = perform(infos, func(info *resource.Info) error {
c.Log("Doing get for %s: %q", info.Mapping.GroupVersionKind.Kind, info.Name)
@ -171,12 +176,26 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
gvk := info.ResourceMapping().GroupVersionKind
vk := gvk.Version + "/" + gvk.Kind
objs[vk] = append(objs[vk], info.Object)
//Get the relation pods
objPods, err = c.getSelectRelationPod(info, objPods)
if err != nil {
c.Log("Warning: get the relation pod is failed, err:%s", err.Error())
}
return nil
})
if err != nil {
return "", err
}
//here, we will add the objPods to the objs
for key, podItems := range objPods {
for i := range podItems {
objs[key+"(related)"] = append(objs[key+"(related)"], &podItems[i])
}
}
// Ok, now we have all the objects grouped by types (say, by v1/Pod, v1/Service, etc.), so
// spin through them and print them. Printer is cool since it prints the header only when
// an object type changes, so we can just rely on that. Problem is it doesn't seem to keep
@ -628,3 +647,67 @@ func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Inf
return err
}
//get an kubernetes resources's relation pods
// kubernetes resource used select labels to relate pods
func (c *Client) getSelectRelationPod(info *resource.Info, objPods map[string][]api.Pod) (map[string][]api.Pod, error) {
if info == nil {
return objPods, nil
}
c.Log("get relation pod of object: %s/%s/%s", info.Namespace, info.Mapping.GroupVersionKind.Kind, info.Name)
versioned, err := c.AsVersionedObject(info.Object)
if runtime.IsNotRegisteredError(err) {
return objPods, nil
}
if err != nil {
return objPods, err
}
// We can ignore this error because it will only error if it isn't a type that doesn't
// have pods. In that case, we don't care
selector, _ := getSelectorFromObject(versioned)
selectorString := labels.Set(selector).AsSelector().String()
// If we have an empty selector, this likely is a service or config map, so bail out now
if selectorString == "" {
return objPods, nil
}
client, _ := c.ClientSet()
pods, err := client.Core().Pods(info.Namespace).List(metav1.ListOptions{
FieldSelector: fields.Everything().String(),
LabelSelector: labels.Set(selector).AsSelector().String(),
})
if err != nil {
return objPods, err
}
for _, pod := range pods.Items {
if pod.APIVersion == "" {
pod.APIVersion = "v1"
}
if pod.Kind == "" {
pod.Kind = "Pod"
}
vk := pod.GroupVersionKind().Version + "/" + pod.GroupVersionKind().Kind
if !isFoundPod(objPods[vk], pod) {
objPods[vk] = append(objPods[vk], pod)
}
}
return objPods, nil
}
func isFoundPod(podItem []api.Pod, pod api.Pod) bool {
for _, value := range podItem {
if (value.Namespace == pod.Namespace) && (value.Name == pod.Name) {
return true
}
}
return false
}

@ -35,11 +35,11 @@ import (
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/kubectl"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/validation"
"k8s.io/kubernetes/pkg/printers"
watchjson "k8s.io/kubernetes/pkg/watch/json"
)

@ -83,5 +83,5 @@ func (r Result) Intersect(rs Result) Result {
// isMatchingInfo returns true if infos match on Name and GroupVersionKind.
func isMatchingInfo(a, b *resource.Info) bool {
return a.Name == b.Name && a.Mapping.GroupVersionKind == b.Mapping.GroupVersionKind
return a.Name == b.Name && a.Mapping.GroupVersionKind.Kind == b.Mapping.GroupVersionKind.Kind
}

@ -21,11 +21,12 @@ import (
"io"
"io/ioutil"
"net"
"net/http"
"strconv"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/portforward"
"k8s.io/client-go/tools/remotecommand"
"k8s.io/client-go/transport/spdy"
)
// Tunnel describes a ssh-like tunnel to a kubernetes pod
@ -71,10 +72,11 @@ func (t *Tunnel) ForwardPort() error {
Name(t.PodName).
SubResource("portforward").URL()
dialer, err := remotecommand.NewExecutor(t.config, "POST", u)
transport, upgrader, err := spdy.RoundTripperFor(t.config)
if err != nil {
return err
}
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", u)
local, err := getAvailablePort()
if err != nil {

@ -19,20 +19,18 @@ package kube // import "k8s.io/helm/pkg/kube"
import (
"time"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
"k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/api/v1/helper"
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
core "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1"
internalclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
)
@ -47,11 +45,10 @@ type deployment struct {
func (c *Client) waitForResources(timeout time.Duration, created Result) error {
c.Log("beginning wait for %d resources with timeout of %v", len(created), timeout)
cs, err := c.ClientSet()
kcs, err := c.KubernetesClientSet()
if err != nil {
return err
}
client := versionedClientsetForDeployment(cs)
return wait.Poll(2*time.Second, timeout, func() (bool, error) {
pods := []v1.Pod{}
services := []v1.Service{}
@ -64,24 +61,24 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
}
switch value := obj.(type) {
case (*v1.ReplicationController):
list, err := getPods(client, value.Namespace, value.Spec.Selector)
list, err := getPods(kcs, value.Namespace, value.Spec.Selector)
if err != nil {
return false, err
}
pods = append(pods, list...)
case (*v1.Pod):
pod, err := client.Core().Pods(value.Namespace).Get(value.Name, metav1.GetOptions{})
pod, err := kcs.Core().Pods(value.Namespace).Get(value.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
pods = append(pods, *pod)
case (*extensions.Deployment):
currentDeployment, err := client.Extensions().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{})
currentDeployment, err := kcs.Extensions().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
// Find RS associated with deployment
newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, client)
newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.ExtensionsV1beta1())
if err != nil || newReplicaSet == nil {
return false, err
}
@ -91,53 +88,59 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
}
deployments = append(deployments, newDeployment)
case (*extensions.DaemonSet):
list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
if err != nil {
return false, err
}
pods = append(pods, list...)
case (*apps.StatefulSet):
list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
case (*appsv1beta1.StatefulSet):
list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
if err != nil {
return false, err
}
pods = append(pods, list...)
case (*appsv1beta2.StatefulSet):
list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
if err != nil {
return false, err
}
pods = append(pods, list...)
case (*extensions.ReplicaSet):
list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
list, err := getPods(kcs, value.Namespace, value.Spec.Selector.MatchLabels)
if err != nil {
return false, err
}
pods = append(pods, list...)
case (*v1.PersistentVolumeClaim):
claim, err := client.Core().PersistentVolumeClaims(value.Namespace).Get(value.Name, metav1.GetOptions{})
claim, err := kcs.Core().PersistentVolumeClaims(value.Namespace).Get(value.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
pvc = append(pvc, *claim)
case (*v1.Service):
svc, err := client.Core().Services(value.Namespace).Get(value.Name, metav1.GetOptions{})
svc, err := kcs.Core().Services(value.Namespace).Get(value.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
services = append(services, *svc)
}
}
isReady := podsReady(pods) && servicesReady(services) && volumesReady(pvc) && deploymentsReady(deployments)
c.Log("resources ready: %v", isReady)
isReady := c.podsReady(pods) && c.servicesReady(services) && c.volumesReady(pvc) && c.deploymentsReady(deployments)
return isReady, nil
})
}
func podsReady(pods []v1.Pod) bool {
func (c *Client) podsReady(pods []v1.Pod) bool {
for _, pod := range pods {
if !podutil.IsPodReady(&pod) {
c.Log("Pod is not ready: %s/%s", pod.GetNamespace(), pod.GetName())
return false
}
}
return true
}
func servicesReady(svc []v1.Service) bool {
func (c *Client) servicesReady(svc []v1.Service) bool {
for _, s := range svc {
// ExternalName Services are external to cluster so helm shouldn't be checking to see if they're 'ready' (i.e. have an IP Set)
if s.Spec.Type == v1.ServiceTypeExternalName {
@ -146,48 +149,42 @@ func servicesReady(svc []v1.Service) bool {
// Make sure the service is not explicitly set to "None" before checking the IP
if s.Spec.ClusterIP != v1.ClusterIPNone && !helper.IsServiceIPSet(&s) {
c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName())
return false
}
// This checks if the service has a LoadBalancer and that balancer has an Ingress defined
if s.Spec.Type == v1.ServiceTypeLoadBalancer && s.Status.LoadBalancer.Ingress == nil {
c.Log("Service is not ready: %s/%s", s.GetNamespace(), s.GetName())
return false
}
}
return true
}
func volumesReady(vols []v1.PersistentVolumeClaim) bool {
func (c *Client) volumesReady(vols []v1.PersistentVolumeClaim) bool {
for _, v := range vols {
if v.Status.Phase != v1.ClaimBound {
c.Log("PersistentVolumeClaim is not ready: %s/%s", v.GetNamespace(), v.GetName())
return false
}
}
return true
}
func deploymentsReady(deployments []deployment) bool {
func (c *Client) deploymentsReady(deployments []deployment) bool {
for _, v := range deployments {
if !(v.replicaSets.Status.ReadyReplicas >= *v.deployment.Spec.Replicas-deploymentutil.MaxUnavailable(*v.deployment)) {
c.Log("Deployment is not ready: %s/%s", v.deployment.GetNamespace(), v.deployment.GetName())
return false
}
}
return true
}
func getPods(client clientset.Interface, namespace string, selector map[string]string) ([]v1.Pod, error) {
func getPods(client kubernetes.Interface, namespace string, selector map[string]string) ([]v1.Pod, error) {
list, err := client.Core().Pods(namespace).List(metav1.ListOptions{
FieldSelector: fields.Everything().String(),
LabelSelector: labels.Set(selector).AsSelector().String(),
})
return list.Items, err
}
func versionedClientsetForDeployment(internalClient internalclientset.Interface) clientset.Interface {
if internalClient == nil {
return &clientset.Clientset{}
}
return &clientset.Clientset{
CoreV1Client: core.New(internalClient.Core().RESTClient()),
ExtensionsV1beta1Client: extensionsclient.New(internalClient.Extensions().RESTClient()),
}
}

@ -68,7 +68,7 @@ func TestBadChart(t *testing.T) {
func TestInvalidYaml(t *testing.T) {
m := All(badYamlFileDir).Messages
if len(m) != 1 {
t.Errorf("All didn't fail with expected errors, got %#v", m)
t.Fatalf("All didn't fail with expected errors, got %#v", m)
}
if !strings.Contains(m[0].Err.Error(), "deliberateSyntaxError") {
t.Errorf("All didn't have the error for deliberateSyntaxError")
@ -78,7 +78,7 @@ func TestInvalidYaml(t *testing.T) {
func TestBadValues(t *testing.T) {
m := All(badValuesFileDir).Messages
if len(m) != 1 {
t.Errorf("All didn't fail with expected errors, got %#v", m)
t.Fatalf("All didn't fail with expected errors, got %#v", m)
}
if !strings.Contains(m[0].Err.Error(), "cannot unmarshal") {
t.Errorf("All didn't have the error for invalid key format: %s", m[0].Err)

@ -62,7 +62,7 @@ func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository,
}
client, err := getterConstructor(cfg.URL, cfg.CertFile, cfg.KeyFile, cfg.CAFile)
if err != nil {
return nil, fmt.Errorf("Could not construct protocol handler for: %s", u.Scheme)
return nil, fmt.Errorf("Could not construct protocol handler for: %s error: %v", u.Scheme, err)
}
return &ChartRepository{
@ -110,8 +110,13 @@ func (r *ChartRepository) Load() error {
// is for pre-2.2.0 repo files.
func (r *ChartRepository) DownloadIndexFile(cachePath string) error {
var indexURL string
parsedURL, err := url.Parse(r.Config.URL)
if err != nil {
return err
}
parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml"
indexURL = strings.TrimSuffix(r.Config.URL, "/") + "/index.yaml"
indexURL = parsedURL.String()
resp, err := r.Client.Get(indexURL)
if err != nil {
return err

@ -23,7 +23,6 @@ import (
"os"
"time"
"github.com/facebookgo/atomicfile"
"github.com/ghodss/yaml"
)
@ -135,20 +134,9 @@ func (r *RepoFile) Remove(name string) bool {
// WriteFile writes a repositories file to the given path.
func (r *RepoFile) WriteFile(path string, perm os.FileMode) error {
f, err := atomicfile.New(path, perm)
if err != nil {
return err
}
data, err := yaml.Marshal(r)
if err != nil {
return err
}
_, err = f.File.Write(data)
if err != nil {
return err
}
return f.Close()
return ioutil.WriteFile(path, data, perm)
}

@ -201,18 +201,10 @@ func TestWriteFile(t *testing.T) {
t.Errorf("failed to create test-file (%v)", err)
}
defer os.Remove(repoFile.Name())
fileMode := os.FileMode(0744)
if err := sampleRepository.WriteFile(repoFile.Name(), fileMode); err != nil {
if err := sampleRepository.WriteFile(repoFile.Name(), 744); err != nil {
t.Errorf("failed to write file (%v)", err)
}
info, _ := os.Stat(repoFile.Name())
mode := info.Mode()
if mode != fileMode {
t.Errorf("incorrect file mode: %s (expected %s)", mode, fileMode)
}
repos, err := LoadRepositoriesFile(repoFile.Name())
if err != nil {
t.Errorf("failed to load file (%v)", err)

@ -17,16 +17,11 @@ limitations under the License.
package driver // import "k8s.io/helm/pkg/storage/driver"
import (
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"
"io/ioutil"
"strconv"
"strings"
"time"
"github.com/golang/protobuf/proto"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kblabels "k8s.io/apimachinery/pkg/labels"
@ -42,10 +37,6 @@ var _ Driver = (*ConfigMaps)(nil)
// ConfigMapsDriverName is the string name of the driver.
const ConfigMapsDriverName = "ConfigMap"
var b64 = base64.StdEncoding
var magicGzip = []byte{0x1f, 0x8b, 0x08}
// ConfigMaps is a wrapper around an implementation of a kubernetes
// ConfigMapsInterface.
type ConfigMaps struct {
@ -265,57 +256,3 @@ func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*api.Config
Data: map[string]string{"release": s},
}, nil
}
// encodeRelease encodes a release returning a base64 encoded
// gzipped binary protobuf encoding representation, or error.
func encodeRelease(rls *rspb.Release) (string, error) {
b, err := proto.Marshal(rls)
if err != nil {
return "", err
}
var buf bytes.Buffer
w, err := gzip.NewWriterLevel(&buf, gzip.BestCompression)
if err != nil {
return "", err
}
if _, err = w.Write(b); err != nil {
return "", err
}
w.Close()
return b64.EncodeToString(buf.Bytes()), nil
}
// decodeRelease decodes the bytes in data into a release
// type. Data must contain a base64 encoded string of a
// valid protobuf encoding of a release, otherwise
// an error is returned.
func decodeRelease(data string) (*rspb.Release, error) {
// base64 decode string
b, err := b64.DecodeString(data)
if err != nil {
return nil, err
}
// For backwards compatibility with releases that were stored before
// compression was introduced we skip decompression if the
// gzip magic header is not found
if bytes.Equal(b[0:3], magicGzip) {
r, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {
return nil, err
}
b2, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
b = b2
}
var rls rspb.Release
// unmarshal protobuf bytes
if err := proto.Unmarshal(b, &rls); err != nil {
return nil, err
}
return &rls, nil
}

@ -142,3 +142,81 @@ func (mock *MockConfigMapsInterface) Delete(name string, opts *metav1.DeleteOpti
delete(mock.objects, name)
return nil
}
// newTestFixture initializes a MockSecretsInterface.
// Secrets are created for each release provided.
func newTestFixtureSecrets(t *testing.T, releases ...*rspb.Release) *Secrets {
var mock MockSecretsInterface
mock.Init(t, releases...)
return NewSecrets(&mock)
}
// MockSecretsInterface mocks a kubernetes SecretsInterface
type MockSecretsInterface struct {
internalversion.SecretInterface
objects map[string]*api.Secret
}
// Init initializes the MockSecretsInterface with the set of releases.
func (mock *MockSecretsInterface) Init(t *testing.T, releases ...*rspb.Release) {
mock.objects = map[string]*api.Secret{}
for _, rls := range releases {
objkey := testKey(rls.Name, rls.Version)
secret, err := newSecretsObject(objkey, rls, nil)
if err != nil {
t.Fatalf("Failed to create secret: %s", err)
}
mock.objects[objkey] = secret
}
}
// Get returns the Secret by name.
func (mock *MockSecretsInterface) Get(name string, options metav1.GetOptions) (*api.Secret, error) {
object, ok := mock.objects[name]
if !ok {
return nil, apierrors.NewNotFound(api.Resource("tests"), name)
}
return object, nil
}
// List returns the a of Secret.
func (mock *MockSecretsInterface) List(opts metav1.ListOptions) (*api.SecretList, error) {
var list api.SecretList
for _, secret := range mock.objects {
list.Items = append(list.Items, *secret)
}
return &list, nil
}
// Create creates a new Secret.
func (mock *MockSecretsInterface) Create(secret *api.Secret) (*api.Secret, error) {
name := secret.ObjectMeta.Name
if object, ok := mock.objects[name]; ok {
return object, apierrors.NewAlreadyExists(api.Resource("tests"), name)
}
mock.objects[name] = secret
return secret, nil
}
// Update updates a Secret.
func (mock *MockSecretsInterface) Update(secret *api.Secret) (*api.Secret, error) {
name := secret.ObjectMeta.Name
if _, ok := mock.objects[name]; !ok {
return nil, apierrors.NewNotFound(api.Resource("tests"), name)
}
mock.objects[name] = secret
return secret, nil
}
// Delete deletes a Secret by name.
func (mock *MockSecretsInterface) Delete(name string, opts *metav1.DeleteOptions) error {
if _, ok := mock.objects[name]; !ok {
return apierrors.NewNotFound(api.Resource("tests"), name)
}
delete(mock.objects, name)
return nil
}

@ -20,6 +20,8 @@ import (
"sort"
"strconv"
"github.com/golang/protobuf/proto"
rspb "k8s.io/helm/pkg/proto/hapi/release"
)
@ -129,5 +131,5 @@ func newRecord(key string, rls *rspb.Release) *record {
lbs.set("STATUS", rspb.Status_Code_name[int32(rls.Info.Status.Code)])
lbs.set("VERSION", strconv.Itoa(int(rls.Version)))
return &record{key: key, lbs: lbs, rls: rls}
return &record{key: key, lbs: lbs, rls: proto.Clone(rls).(*rspb.Release)}
}

@ -0,0 +1,258 @@
/*
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 driver // import "k8s.io/helm/pkg/storage/driver"
import (
"fmt"
"strconv"
"strings"
"time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kblabels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
rspb "k8s.io/helm/pkg/proto/hapi/release"
)
var _ Driver = (*Secrets)(nil)
// SecretsDriverName is the string name of the driver.
const SecretsDriverName = "Secret"
// Secrets is a wrapper around an implementation of a kubernetes
// SecretsInterface.
type Secrets struct {
impl internalversion.SecretInterface
Log func(string, ...interface{})
}
// NewSecrets initializes a new Secrets wrapping an implmenetation of
// the kubernetes SecretsInterface.
func NewSecrets(impl internalversion.SecretInterface) *Secrets {
return &Secrets{
impl: impl,
Log: func(_ string, _ ...interface{}) {},
}
}
// Name returns the name of the driver.
func (secrets *Secrets) Name() string {
return SecretsDriverName
}
// Get fetches the release named by key. The corresponding release is returned
// or error if not found.
func (secrets *Secrets) Get(key string) (*rspb.Release, error) {
// fetch the secret holding the release named by key
obj, err := secrets.impl.Get(key, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return nil, ErrReleaseNotFound(key)
}
secrets.Log("get: failed to get %q: %s", key, err)
return nil, err
}
// found the secret, decode the base64 data string
r, err := decodeRelease(string(obj.Data["release"]))
if err != nil {
secrets.Log("get: failed to decode data %q: %s", key, err)
return nil, err
}
// return the release object
return r, nil
}
// List fetches all releases and returns the list releases such
// that filter(release) == true. An error is returned if the
// secret fails to retrieve the releases.
func (secrets *Secrets) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) {
lsel := kblabels.Set{"OWNER": "TILLER"}.AsSelector()
opts := metav1.ListOptions{LabelSelector: lsel.String()}
list, err := secrets.impl.List(opts)
if err != nil {
secrets.Log("list: failed to list: %s", err)
return nil, err
}
var results []*rspb.Release
// iterate over the secrets object list
// and decode each release
for _, item := range list.Items {
rls, err := decodeRelease(string(item.Data["release"]))
if err != nil {
secrets.Log("list: failed to decode release: %v: %s", item, err)
continue
}
if filter(rls) {
results = append(results, rls)
}
}
return results, nil
}
// Query fetches all releases that match the provided map of labels.
// An error is returned if the secret fails to retrieve the releases.
func (secrets *Secrets) Query(labels map[string]string) ([]*rspb.Release, error) {
ls := kblabels.Set{}
for k, v := range labels {
if errs := validation.IsValidLabelValue(v); len(errs) != 0 {
return nil, fmt.Errorf("invalid label value: %q: %s", v, strings.Join(errs, "; "))
}
ls[k] = v
}
opts := metav1.ListOptions{LabelSelector: ls.AsSelector().String()}
list, err := secrets.impl.List(opts)
if err != nil {
secrets.Log("query: failed to query with labels: %s", err)
return nil, err
}
if len(list.Items) == 0 {
return nil, ErrReleaseNotFound(labels["NAME"])
}
var results []*rspb.Release
for _, item := range list.Items {
rls, err := decodeRelease(string(item.Data["release"]))
if err != nil {
secrets.Log("query: failed to decode release: %s", err)
continue
}
results = append(results, rls)
}
return results, nil
}
// Create creates a new Secret holding the release. If the
// Secret already exists, ErrReleaseExists is returned.
func (secrets *Secrets) Create(key string, rls *rspb.Release) error {
// set labels for secrets object meta data
var lbs labels
lbs.init()
lbs.set("CREATED_AT", strconv.Itoa(int(time.Now().Unix())))
// create a new secret to hold the release
obj, err := newSecretsObject(key, rls, lbs)
if err != nil {
secrets.Log("create: failed to encode release %q: %s", rls.Name, err)
return err
}
// push the secret object out into the kubiverse
if _, err := secrets.impl.Create(obj); err != nil {
if apierrors.IsAlreadyExists(err) {
return ErrReleaseExists(rls.Name)
}
secrets.Log("create: failed to create: %s", err)
return err
}
return nil
}
// Update updates the Secret holding the release. If not found
// the Secret is created to hold the release.
func (secrets *Secrets) Update(key string, rls *rspb.Release) error {
// set labels for secrets object meta data
var lbs labels
lbs.init()
lbs.set("MODIFIED_AT", strconv.Itoa(int(time.Now().Unix())))
// create a new secret object to hold the release
obj, err := newSecretsObject(key, rls, lbs)
if err != nil {
secrets.Log("update: failed to encode release %q: %s", rls.Name, err)
return err
}
// push the secret object out into the kubiverse
_, err = secrets.impl.Update(obj)
if err != nil {
secrets.Log("update: failed to update: %s", err)
return err
}
return nil
}
// Delete deletes the Secret holding the release named by key.
func (secrets *Secrets) Delete(key string) (rls *rspb.Release, err error) {
// fetch the release to check existence
if rls, err = secrets.Get(key); err != nil {
if apierrors.IsNotFound(err) {
return nil, ErrReleaseExists(rls.Name)
}
secrets.Log("delete: failed to get release %q: %s", key, err)
return nil, err
}
// delete the release
if err = secrets.impl.Delete(key, &metav1.DeleteOptions{}); err != nil {
return rls, err
}
return rls, nil
}
// newSecretsObject constructs a kubernetes Secret object
// to store a release. Each secret data entry is the base64
// encoded string of a release's binary protobuf encoding.
//
// The following labels are used within each secret:
//
// "MODIFIED_AT" - timestamp indicating when this secret was last modified. (set in Update)
// "CREATED_AT" - timestamp indicating when this secret was created. (set in Create)
// "VERSION" - version of the release.
// "STATUS" - status of the release (see proto/hapi/release.status.pb.go for variants)
// "OWNER" - owner of the secret, currently "TILLER".
// "NAME" - name of the release.
//
func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*api.Secret, error) {
const owner = "TILLER"
// encode the release
s, err := encodeRelease(rls)
if err != nil {
return nil, err
}
if lbs == nil {
lbs.init()
}
// apply labels
lbs.set("NAME", rls.Name)
lbs.set("OWNER", owner)
lbs.set("STATUS", rspb.Status_Code_name[int32(rls.Info.Status.Code)])
lbs.set("VERSION", strconv.Itoa(int(rls.Version)))
// create and return secret object
return &api.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: key,
Labels: lbs.toMap(),
},
Data: map[string][]byte{"release": []byte(s)},
}, nil
}

@ -0,0 +1,186 @@
/*
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 driver
import (
"encoding/base64"
"reflect"
"testing"
"github.com/gogo/protobuf/proto"
"k8s.io/kubernetes/pkg/api"
rspb "k8s.io/helm/pkg/proto/hapi/release"
)
func TestSecretName(t *testing.T) {
c := newTestFixtureSecrets(t)
if c.Name() != SecretsDriverName {
t.Errorf("Expected name to be %q, got %q", SecretsDriverName, c.Name())
}
}
func TestSecretGet(t *testing.T) {
vers := int32(1)
name := "smug-pigeon"
namespace := "default"
key := testKey(name, vers)
rel := releaseStub(name, vers, namespace, rspb.Status_DEPLOYED)
secrets := newTestFixtureSecrets(t, []*rspb.Release{rel}...)
// get release with key
got, err := secrets.Get(key)
if err != nil {
t.Fatalf("Failed to get release: %s", err)
}
// compare fetched release with original
if !reflect.DeepEqual(rel, got) {
t.Errorf("Expected {%q}, got {%q}", rel, got)
}
}
func TestUNcompressedSecretGet(t *testing.T) {
vers := int32(1)
name := "smug-pigeon"
namespace := "default"
key := testKey(name, vers)
rel := releaseStub(name, vers, namespace, rspb.Status_DEPLOYED)
// Create a test fixture which contains an uncompressed release
secret, err := newSecretsObject(key, rel, nil)
if err != nil {
t.Fatalf("Failed to create secret: %s", err)
}
b, err := proto.Marshal(rel)
if err != nil {
t.Fatalf("Failed to marshal release: %s", err)
}
secret.Data["release"] = []byte(base64.StdEncoding.EncodeToString(b))
var mock MockSecretsInterface
mock.objects = map[string]*api.Secret{key: secret}
secrets := NewSecrets(&mock)
// get release with key
got, err := secrets.Get(key)
if err != nil {
t.Fatalf("Failed to get release: %s", err)
}
// compare fetched release with original
if !reflect.DeepEqual(rel, got) {
t.Errorf("Expected {%q}, got {%q}", rel, got)
}
}
func TestSecretList(t *testing.T) {
secrets := newTestFixtureSecrets(t, []*rspb.Release{
releaseStub("key-1", 1, "default", rspb.Status_DELETED),
releaseStub("key-2", 1, "default", rspb.Status_DELETED),
releaseStub("key-3", 1, "default", rspb.Status_DEPLOYED),
releaseStub("key-4", 1, "default", rspb.Status_DEPLOYED),
releaseStub("key-5", 1, "default", rspb.Status_SUPERSEDED),
releaseStub("key-6", 1, "default", rspb.Status_SUPERSEDED),
}...)
// list all deleted releases
del, err := secrets.List(func(rel *rspb.Release) bool {
return rel.Info.Status.Code == rspb.Status_DELETED
})
// check
if err != nil {
t.Errorf("Failed to list deleted: %s", err)
}
if len(del) != 2 {
t.Errorf("Expected 2 deleted, got %d:\n%v\n", len(del), del)
}
// list all deployed releases
dpl, err := secrets.List(func(rel *rspb.Release) bool {
return rel.Info.Status.Code == rspb.Status_DEPLOYED
})
// check
if err != nil {
t.Errorf("Failed to list deployed: %s", err)
}
if len(dpl) != 2 {
t.Errorf("Expected 2 deployed, got %d", len(dpl))
}
// list all superseded releases
ssd, err := secrets.List(func(rel *rspb.Release) bool {
return rel.Info.Status.Code == rspb.Status_SUPERSEDED
})
// check
if err != nil {
t.Errorf("Failed to list superseded: %s", err)
}
if len(ssd) != 2 {
t.Errorf("Expected 2 superseded, got %d", len(ssd))
}
}
func TestSecretCreate(t *testing.T) {
secrets := newTestFixtureSecrets(t)
vers := int32(1)
name := "smug-pigeon"
namespace := "default"
key := testKey(name, vers)
rel := releaseStub(name, vers, namespace, rspb.Status_DEPLOYED)
// store the release in a secret
if err := secrets.Create(key, rel); err != nil {
t.Fatalf("Failed to create release with key %q: %s", key, err)
}
// get the release back
got, err := secrets.Get(key)
if err != nil {
t.Fatalf("Failed to get release with key %q: %s", key, err)
}
// compare created release with original
if !reflect.DeepEqual(rel, got) {
t.Errorf("Expected {%q}, got {%q}", rel, got)
}
}
func TestSecretUpdate(t *testing.T) {
vers := int32(1)
name := "smug-pigeon"
namespace := "default"
key := testKey(name, vers)
rel := releaseStub(name, vers, namespace, rspb.Status_DEPLOYED)
secrets := newTestFixtureSecrets(t, []*rspb.Release{rel}...)
// modify release status code
rel.Info.Status.Code = rspb.Status_SUPERSEDED
// perform the update
if err := secrets.Update(key, rel); err != nil {
t.Fatalf("Failed to update release: %s", err)
}
// fetch the updated release
got, err := secrets.Get(key)
if err != nil {
t.Fatalf("Failed to get release with key %q: %s", key, err)
}
// check release has actually been updated by comparing modified fields
if rel.Info.Status.Code != got.Info.Status.Code {
t.Errorf("Expected status %s, got status %s", rel.Info.Status.Code, got.Info.Status.Code)
}
}

@ -0,0 +1,85 @@
/*
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 driver // import "k8s.io/helm/pkg/storage/driver"
import (
"bytes"
"compress/gzip"
"encoding/base64"
"io/ioutil"
"github.com/golang/protobuf/proto"
rspb "k8s.io/helm/pkg/proto/hapi/release"
)
var b64 = base64.StdEncoding
var magicGzip = []byte{0x1f, 0x8b, 0x08}
// encodeRelease encodes a release returning a base64 encoded
// gzipped binary protobuf encoding representation, or error.
func encodeRelease(rls *rspb.Release) (string, error) {
b, err := proto.Marshal(rls)
if err != nil {
return "", err
}
var buf bytes.Buffer
w, err := gzip.NewWriterLevel(&buf, gzip.BestCompression)
if err != nil {
return "", err
}
if _, err = w.Write(b); err != nil {
return "", err
}
w.Close()
return b64.EncodeToString(buf.Bytes()), nil
}
// decodeRelease decodes the bytes in data into a release
// type. Data must contain a base64 encoded string of a
// valid protobuf encoding of a release, otherwise
// an error is returned.
func decodeRelease(data string) (*rspb.Release, error) {
// base64 decode string
b, err := b64.DecodeString(data)
if err != nil {
return nil, err
}
// For backwards compatibility with releases that were stored before
// compression was introduced we skip decompression if the
// gzip magic header is not found
if bytes.Equal(b[0:3], magicGzip) {
r, err := gzip.NewReader(bytes.NewReader(b))
if err != nil {
return nil, err
}
b2, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
b = b2
}
var rls rspb.Release
// unmarshal protobuf bytes
if err := proto.Unmarshal(b, &rls); err != nil {
return nil, err
}
return &rls, nil
}

@ -32,9 +32,11 @@ var InstallOrder SortOrder = []string{
"LimitRange",
"Secret",
"ConfigMap",
"StorageClass",
"PersistentVolume",
"PersistentVolumeClaim",
"ServiceAccount",
"CustomResourceDefinition",
"ClusterRole",
"ClusterRoleBinding",
"Role",
@ -71,9 +73,11 @@ var UninstallOrder SortOrder = []string{
"Role",
"ClusterRoleBinding",
"ClusterRole",
"CustomResourceDefinition",
"ServiceAccount",
"PersistentVolumeClaim",
"PersistentVolume",
"StorageClass",
"ConfigMap",
"Secret",
"LimitRange",
@ -116,8 +120,12 @@ func (k *kindSorter) Less(i, j int) bool {
b := k.manifests[j]
first, aok := k.ordering[a.Head.Kind]
second, bok := k.ordering[b.Head.Kind]
// if same kind (including unknown) sub sort alphanumeric
if first == second {
// same kind (including unknown) so sub sort alphanumeric
// if both are unknown and of different kind sort by kind alphabetically
if !aok && !bok && a.Head.Kind != b.Head.Kind {
return a.Head.Kind < b.Head.Kind
}
return a.Name < b.Name
}
// unknown kind is last

@ -41,6 +41,10 @@ func TestKindSorter(t *testing.T) {
Name: "u",
Head: &util.SimpleHead{Kind: "CronJob"},
},
{
Name: "2",
Head: &util.SimpleHead{Kind: "CustomResourceDefinition"},
},
{
Name: "n",
Head: &util.SimpleHead{Kind: "DaemonSet"},
@ -117,6 +121,10 @@ func TestKindSorter(t *testing.T) {
Name: "s",
Head: &util.SimpleHead{Kind: "StatefulSet"},
},
{
Name: "1",
Head: &util.SimpleHead{Kind: "StorageClass"},
},
{
Name: "w",
Head: &util.SimpleHead{Kind: "APIService"},
@ -128,8 +136,8 @@ func TestKindSorter(t *testing.T) {
order SortOrder
expected string
}{
{"install", InstallOrder, "abcdefghijklmnopqrstuvw!"},
{"uninstall", UninstallOrder, "wvmutsrqponlkjihgfedcba!"},
{"install", InstallOrder, "abcde1fgh2ijklmnopqrstuvw!"},
{"uninstall", UninstallOrder, "wvmutsrqponlkji2hgf1edcba!"},
} {
var buf bytes.Buffer
t.Run(test.description, func(t *testing.T) {
@ -175,7 +183,7 @@ func TestKindSorterSubSort(t *testing.T) {
Head: &util.SimpleHead{Kind: "ClusterRoleBinding"},
},
{
Name: "u3",
Name: "u2",
Head: &util.SimpleHead{Kind: "Unknown"},
},
{
@ -183,8 +191,8 @@ func TestKindSorterSubSort(t *testing.T) {
Head: &util.SimpleHead{Kind: "Unknown"},
},
{
Name: "u2",
Head: &util.SimpleHead{Kind: "Unknown"},
Name: "t3",
Head: &util.SimpleHead{Kind: "Unknown2"},
},
}
for _, test := range []struct {
@ -193,7 +201,7 @@ func TestKindSorterSubSort(t *testing.T) {
expected string
}{
// expectation is sorted by kind (unknown is last) and then sub sorted alphabetically within each group
{"cm,clusterRole,clusterRoleBinding,Unknown", InstallOrder, "01Aa!zu1u2u3"},
{"cm,clusterRole,clusterRoleBinding,Unknown,Unknown2", InstallOrder, "01Aa!zu1u2t3"},
} {
var buf bytes.Buffer
t.Run(test.description, func(t *testing.T) {

@ -155,7 +155,7 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R
updatedRelease.Info.Status.Code = release.Status_FAILED
updatedRelease.Info.Description = msg
s.recordRelease(originalRelease, true)
s.recordRelease(updatedRelease, false)
s.recordRelease(updatedRelease, true)
return res, err
}

@ -20,6 +20,8 @@ import (
"strings"
"testing"
"github.com/golang/protobuf/proto"
"k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release"
@ -59,10 +61,7 @@ func TestUpdateRelease(t *testing.T) {
t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace)
}
updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version)
if err != nil {
t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases)
}
updated := compareStoredAndReturnedRelease(t, *rs, *res)
if len(updated.Hooks) != 1 {
t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks))
@ -79,8 +78,8 @@ func TestUpdateRelease(t *testing.T) {
t.Errorf("Expected event 0 to be pre upgrade")
}
if len(res.Release.Manifest) == 0 {
t.Errorf("No manifest returned: %v", res.Release)
if len(updated.Manifest) == 0 {
t.Errorf("Expected manifest in %v", res)
}
if res.Release.Config == nil {
@ -89,12 +88,8 @@ func TestUpdateRelease(t *testing.T) {
t.Errorf("Expected release values %q, got %q", rel.Config.Raw, res.Release.Config.Raw)
}
if len(updated.Manifest) == 0 {
t.Errorf("Expected manifest in %v", res)
}
if !strings.Contains(updated.Manifest, "---\n# Source: hello/templates/hello\nhello: world") {
t.Errorf("unexpected output: %s", rel.Manifest)
t.Errorf("unexpected output: %s", updated.Manifest)
}
if res.Release.Version != 2 {
@ -167,6 +162,7 @@ func TestUpdateRelease_ReuseValues(t *testing.T) {
if res.Release.Config != nil && res.Release.Config.Raw != expect {
t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw)
}
compareStoredAndReturnedRelease(t, *rs, *res)
}
func TestUpdateRelease_ResetReuseValues(t *testing.T) {
@ -196,6 +192,7 @@ func TestUpdateRelease_ResetReuseValues(t *testing.T) {
if res.Release.Config != nil && res.Release.Config.Raw != "" {
t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw)
}
compareStoredAndReturnedRelease(t, *rs, *res)
}
func TestUpdateReleaseFailure(t *testing.T) {
@ -204,6 +201,7 @@ func TestUpdateReleaseFailure(t *testing.T) {
rel := releaseStub()
rs.env.Releases.Create(rel)
rs.env.KubeClient = newUpdateFailingKubeClient()
rs.Log = t.Logf
req := &services.UpdateReleaseRequest{
Name: rel.Name,
@ -225,6 +223,8 @@ func TestUpdateReleaseFailure(t *testing.T) {
t.Errorf("Expected FAILED release. Got %d", updatedStatus)
}
compareStoredAndReturnedRelease(t, *rs, *res)
edesc := "Upgrade \"angry-panda\" failed: Failed update in kube client"
if got := res.Release.Info.Description; got != edesc {
t.Errorf("Expected description %q, got %q", edesc, got)
@ -285,3 +285,16 @@ func TestUpdateReleaseNoChanges(t *testing.T) {
t.Fatalf("Failed updated: %s", err)
}
}
func compareStoredAndReturnedRelease(t *testing.T, rs ReleaseServer, res services.UpdateReleaseResponse) *release.Release {
storedRelease, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version)
if err != nil {
t.Fatalf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases)
}
if !proto.Equal(storedRelease, res.Release) {
t.Errorf("Stored release doesn't match returned Release")
}
return storedRelease
}

@ -26,7 +26,7 @@ var (
// Increment major number for new feature additions and behavioral changes.
// Increment minor number for bug fixes and performance enhancements.
// Increment patch number for critical fixes to existing releases.
Version = "v2.6"
Version = "v2.7"
// BuildMetadata is extra build time data
BuildMetadata = "unreleased"

Loading…
Cancel
Save