Merge branch 'master' of github.com:kubernetes/helm into upstream

pull/2862/head
Ryan Payton 8 years ago
commit 2bd6940061

@ -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.6.2-darwin-amd64.tar.gz)
- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.2-linux-amd64.tar.gz)
- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.2-linux-386.tar.gz)
- [Windows](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.2-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

@ -46,8 +46,8 @@ import (
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.

@ -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"},
},

@ -26,6 +26,7 @@ import (
"strings"
"time"
"github.com/Masterminds/semver"
"github.com/spf13/cobra"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/engine"
@ -35,6 +36,7 @@ import (
util "k8s.io/helm/pkg/releaseutil"
"k8s.io/helm/pkg/tiller"
"k8s.io/helm/pkg/timeconv"
tversion "k8s.io/helm/pkg/version"
)
const templateDesc = `
@ -61,6 +63,7 @@ type templateCmd struct {
showNotes bool
releaseName string
renderFiles []string
kubeVersion string
}
func newTemplateCmd(out io.Writer) *cobra.Command {
@ -84,6 +87,7 @@ func newTemplateCmd(out io.Writer) *cobra.Command {
f.StringVar(&t.namespace, "namespace", "", "namespace to install the release into")
f.StringArrayVar(&t.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringVar(&t.nameTemplate, "name-template", "", "specify template used to name the release")
f.StringVar(&t.kubeVersion, "kube-version", "", "override the Kubernetes version used as Capabilities.KubeVersion.Major/Minor (e.g. 1.7)")
return cmd
}
@ -171,7 +175,22 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
// Set up engine.
renderer := engine.New()
vals, err := chartutil.ToRenderValues(c, config, options)
caps := &chartutil.Capabilities{
APIVersions: chartutil.DefaultVersionSet,
KubeVersion: chartutil.DefaultKubeVersion,
TillerVersion: tversion.GetVersionProto(),
}
// If --kube-versionis set, try to parse it as SemVer, and override the
// kubernetes version
if t.kubeVersion != "" {
kv, err := semver.NewVersion(t.kubeVersion)
if err != nil {
return fmt.Errorf("could not parse a kubernetes version: %v", err)
}
caps.KubeVersion.Major = fmt.Sprint(kv.Major())
caps.KubeVersion.Minor = fmt.Sprint(kv.Minor())
}
vals, err := chartutil.ToRenderValuesCaps(c, config, options, caps)
if err != nil {
return err
}

@ -104,6 +104,13 @@ func TestTemplateCmd(t *testing.T) {
expectKey: "subchart1/templates/service.yaml",
expectValue: "release-name: \"foobar-YWJj-baz\"",
},
{
name: "check_kube_version",
desc: "verify --kube-version overrides the kubernetes version",
args: []string{chartPath, "--kube-version", "1.6"},
expectKey: "subchart1/templates/service.yaml",
expectValue: "kube-version/major: \"1\"\n kube-version/minor: \"6\"",
},
}
var buf bytes.Buffer

@ -87,7 +87,7 @@ in the future.) It is considered good practice to add a hook weight, and set it
to `0` if weight is not important.
### Hook resources are not managed with correponding releases
### Hook resources are not managed with corresponding releases
The resources that a hook creates are not tracked or managed as part of the
release. Once Tiller verifies that the hook has reached its ready state, it
@ -180,5 +180,4 @@ It is also possible to define policies that determine when to delete correspondi
"helm.sh/hook-delete-policy": hook-succeeded
```
When using `"helm.sh/hook-delete-policy"` annoation, you can choose its value from `"hook-succeeded"` and `"hook-failed"`. The value `"hook-succeeded"` specifies Tiller should delete the hook after the hook is successfully excuted, while the value `"hook-failed"`specifies Tiller should delete the hook if the hook is failed during execuation.
When using `"helm.sh/hook-delete-policy"` annotation, you can choose its value from `"hook-succeeded"` and `"hook-failed"`. The value `"hook-succeeded"` specifies Tiller should delete the hook after the hook is successfully executed, while the value `"hook-failed"`specifies Tiller should delete the hook if the hook failed during execution.

@ -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.

@ -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.

@ -26,6 +26,7 @@ helm template [flags] CHART
```
-x, --execute stringArray only execute the given templates
--kube-version string override the Kubernetes version used as Capabilities.KubeVersion.Major/Minor (e.g. 1.7)
-n, --name string release name (default "RELEASE-NAME")
--name-template string specify template used to name the release
--namespace string namespace to install the release into
@ -47,4 +48,4 @@ helm template [flags] CHART
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 24-Aug-2017
###### Auto generated by spf13/cobra on 11-Sep-2017

@ -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

@ -57,6 +57,7 @@ Tools layered on top of Helm or Tiller.
- [Monocular](https://github.com/helm/monocular) - Web UI for Helm Chart repositories
- [Helm Chart Publisher](https://github.com/luizbafilho/helm-chart-publisher) - HTTP API for publishing Helm Charts in an easy way
- [Armada](https://github.com/att-comdev/armada) - Manage prefixed releases throughout various Kubernetes namespaces, and removes completed jobs for complex deployments. Used by the [Openstack-Helm](https://github.com/openstack/openstack-helm) team.
- [ChartMuseum](https://github.com/chartmuseum/chartmuseum) - Helm Chart Repository with support for Amazon S3 and Google Cloud Storage
## Helm Included

46
glide.lock generated

@ -1,5 +1,5 @@
hash: 54e64255ab9112d0183766264214969a8add57903fbe9e96034f9640b9c9cd82
updated: 2017-07-10T10:52:19.616678852-04:00
hash: 0759b118eb4017d612af767460cdec467d6f78013ad1efff1c82676f1df84a75
updated: 2017-09-26T15:21:30.833774-07:00
imports:
- name: cloud.google.com/go
version: 3b1ae45394a234c385be014e9a488f2bb6eef821
@ -8,7 +8,7 @@ imports:
- name: github.com/asaskevich/govalidator
version: 7664702784775e51966f0885f5cd27435916517b
- name: github.com/Azure/go-autorest
version: d7c034a8af24eda120dd6460bfcd6d9ed14e43ca
version: 58f6f26e200fa5dfb40c9cd1c83f3e2c860d779d
- name: github.com/beorn7/perks
version: 3ac7bf7a47d159a033b107610db8a1b6575507a4
subpackages:
@ -68,10 +68,6 @@ imports:
version: ba18e35c5c1b36ef6334cad706eb681153d2d379
- 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
@ -137,20 +133,20 @@ imports:
subpackages:
- lru
- name: github.com/golang/protobuf
version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
version: 4bd1920723d7b7c925de087aa32e2187708897f7
subpackages:
- proto
- ptypes/any
- ptypes/timestamp
- name: github.com/google/gofuzz
version: bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5
version: 44d81051d367757e1c7c6a5a86423ece9afcf63c
- name: github.com/gosuri/uitable
version: 36ee7e946282a3fb1cfecd476ddc9b35d8847e42
subpackages:
- util/strutil
- util/wordwrap
- name: github.com/grpc-ecosystem/go-grpc-prometheus
version: 0c1b191dbfe51efdabe3c14b9f6f3b96429e0722
version: 2500245aa6110c562d17020fb31a2c133d737799
- name: github.com/hashicorp/golang-lru
version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
- name: github.com/howeyc/gopass
@ -172,7 +168,7 @@ imports:
- name: github.com/Masterminds/semver
version: 517734cc7d6470c0d07130e40fd40bdeb9bcd3fd
- name: github.com/Masterminds/sprig
version: 9526be0327b26ad31aa70296a7b10704883976d5
version: 4c164950cd0a8d3724ddb78982e2c56dc7f47112
- name: github.com/Masterminds/vcs
version: 3084677c2c188840777bff30054f2b553729d329
- name: github.com/mattn/go-runewidth
@ -223,7 +219,7 @@ imports:
- name: github.com/spf13/pflag
version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7
- name: github.com/technosophos/moniker
version: ab470f5e105a44d0c87ea21bacd6a335c4816d83
version: 9f956786b91d9786ca11aa5be6104542fa911546
- name: github.com/ugorji/go
version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74
subpackages:
@ -254,7 +250,7 @@ imports:
- lex/httplex
- trace
- name: golang.org/x/oauth2
version: 3c3a985cb79f52a3190fbc056984415ca6763d01
version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4
- name: golang.org/x/sys
version: 8f0908ab3b2457e2e15403d3697c9ef5cb4b57a9
subpackages:
@ -302,34 +298,14 @@ imports:
- name: k8s.io/api
version: 4fe9229aaa9d704f8a2a21cdcd50de2bbb6e1b57
subpackages:
- admissionregistration/v1alpha1
- apps/v1beta1
- authentication/v1
- authentication/v1beta1
- authorization/v1
- authorization/v1beta1
- autoscaling/v1
- autoscaling/v2alpha1
- batch/v1
- batch/v2alpha1
- certificates/v1beta1
- core/v1
- extensions/v1beta1
- networking/v1
- policy/v1beta1
- rbac/v1alpha1
- rbac/v1beta1
- settings/v1alpha1
- storage/v1
- storage/v1beta1
- name: k8s.io/apiserver
version: 087d1a2efeb6296f04bb0f54e7af9890052aa6d7
version: 2308857ad3b8b18abf74ff734853973eda9da94d
subpackages:
- pkg/admission
- pkg/apis/apiserver
- pkg/apis/apiserver/install
- pkg/apis/apiserver/v1alpha1
- pkg/apis/audit
- pkg/authentication/authenticator
- pkg/authentication/serviceaccount
- pkg/authentication/user
@ -338,7 +314,7 @@ imports:
- pkg/util/feature
- pkg/util/flag
- name: k8s.io/kubernetes
version: d3ada0119e776222f11ec7945e6d860061339aad
version: d3faa3f8f2e85c8089e80a661955626ae24abf80
subpackages:
- cmd/kubeadm/app/apis/kubeadm
- federation/apis/federation

@ -14,13 +14,13 @@ import:
- package: github.com/imdario/mergo
version: 6633656539c1639d9d78127b7d47c622b5d7b6dc
- package: github.com/Masterminds/sprig
version: ^2.12
version: ^2.13
- 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
@ -38,8 +38,6 @@ import:
- 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
@ -56,7 +54,7 @@ import:
# hacks for kubernetes v1.7
- package: cloud.google.com/go
- package: github.com/Azure/go-autorest
version: d7c034a8af24eda120dd6460bfcd6d9ed14e43ca
version: v8.0.0
- package: github.com/dgrijalva/jwt-go
- package: github.com/docker/spdystream
- package: github.com/go-openapi/analysis
@ -81,7 +79,6 @@ import:
ignore:
- k8s.io/client-go
- k8s.io/apimachinery
testImports:
- package: github.com/stretchr/testify
version: ^1.1.4

@ -16,12 +16,26 @@ limitations under the License.
package chartutil
import (
"fmt"
"runtime"
"k8s.io/apimachinery/pkg/version"
tversion "k8s.io/helm/pkg/proto/hapi/version"
)
// DefaultVersionSet is the default version set, which includes only Core V1 ("v1").
var DefaultVersionSet = NewVersionSet("v1")
var (
// DefaultVersionSet is the default version set, which includes only Core V1 ("v1").
DefaultVersionSet = NewVersionSet("v1")
// DefaultKubeVersion is the default kubernetes version
DefaultKubeVersion = &version.Info{
Major: "1",
Minor: "7",
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
}
)
// Capabilities describes the capabilities of the Kubernetes cluster that Tiller is attached to.
type Capabilities struct {

@ -183,7 +183,7 @@ func ToYaml(v interface{}) string {
// This is not a general-purpose YAML parser, and will not parse all valid
// YAML documents. Additionally, because its intended use is within templates
// it tolerates errors. It will insert the returned error message string into
// m["error"] in the returned map.
// m["Error"] in the returned map.
func FromYaml(str string) map[string]interface{} {
m := map[string]interface{}{}
@ -225,7 +225,7 @@ func ToJson(v interface{}) string {
// This is not a general-purpose JSON parser, and will not parse all valid
// YAML documents. Additionally, because its intended use is within templates
// it tolerates errors. It will insert the returned error message string into
// m["error"] in the returned map.
// m["Error"] in the returned map.
func FromJson(str string) map[string]interface{} {
m := map[string]interface{}{}

@ -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) {

@ -6,6 +6,8 @@ metadata:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
namespace: "{{ .Release.Namespace }}"
release-name: "{{ .Release.Name }}"
kube-version/major: "{{ .Capabilities.KubeVersion.Major }}"
kube-version/minor: "{{ .Capabilities.KubeVersion.Minor }}"
spec:
type: {{ .Values.service.type }}
ports:

@ -157,6 +157,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 +174,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 +645,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
}

@ -122,22 +122,22 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
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,28 +146,32 @@ 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
}
}

@ -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)

@ -21,10 +21,8 @@ import (
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/ghodss/yaml"
"k8s.io/apimachinery/pkg/version"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/engine"
"k8s.io/helm/pkg/lint/support"
@ -55,14 +53,8 @@ func Templates(linter *support.Linter) {
options := chartutil.ReleaseOptions{Name: "testRelease", Time: timeconv.Now(), Namespace: "testNamespace"}
caps := &chartutil.Capabilities{
APIVersions: chartutil.DefaultVersionSet,
KubeVersion: &version.Info{
Major: "1",
Minor: "7",
GoVersion: runtime.Version(),
Compiler: runtime.Compiler,
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
},
APIVersions: chartutil.DefaultVersionSet,
KubeVersion: chartutil.DefaultKubeVersion,
TillerVersion: tversion.GetVersionProto(),
}
valuesToRender, err := chartutil.ToRenderValuesCaps(chart, chart.Values, options, caps)

@ -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)

@ -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)}
}

@ -32,6 +32,7 @@ var InstallOrder SortOrder = []string{
"LimitRange",
"Secret",
"ConfigMap",
"StorageClass",
"PersistentVolume",
"PersistentVolumeClaim",
"ServiceAccount",
@ -74,6 +75,7 @@ var UninstallOrder SortOrder = []string{
"ServiceAccount",
"PersistentVolumeClaim",
"PersistentVolume",
"StorageClass",
"ConfigMap",
"Secret",
"LimitRange",
@ -116,8 +118,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

@ -49,6 +49,10 @@ func TestKindSorter(t *testing.T) {
Name: "r",
Head: &util.SimpleHead{Kind: "Deployment"},
},
{
Name: "1",
Head: &util.SimpleHead{Kind: "StorageClass"},
},
{
Name: "!",
Head: &util.SimpleHead{Kind: "HonkyTonkSet"},
@ -128,8 +132,8 @@ func TestKindSorter(t *testing.T) {
order SortOrder
expected string
}{
{"install", InstallOrder, "abcdefghijklmnopqrstuvw!"},
{"uninstall", UninstallOrder, "wvmutsrqponlkjihgfedcba!"},
{"install", InstallOrder, "abcde1fghijklmnopqrstuvw!"},
{"uninstall", UninstallOrder, "wvmutsrqponlkjihgf1edcba!"},
} {
var buf bytes.Buffer
t.Run(test.description, func(t *testing.T) {
@ -175,7 +179,7 @@ func TestKindSorterSubSort(t *testing.T) {
Head: &util.SimpleHead{Kind: "ClusterRoleBinding"},
},
{
Name: "u3",
Name: "u2",
Head: &util.SimpleHead{Kind: "Unknown"},
},
{
@ -183,8 +187,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 +197,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
}

Loading…
Cancel
Save