diff --git a/bin/kubeyaml b/bin/kubeyaml index 2728de489..c758bee40 100755 --- a/bin/kubeyaml +++ b/bin/kubeyaml @@ -1,2 +1,2 @@ #!/bin/sh -docker run --rm -i quay.io/squaremo/kubeyaml:0.6.1 "$@" +docker run --rm -i quay.io/squaremo/kubeyaml:0.7.0 "$@" diff --git a/cluster/kubernetes/errors.go b/cluster/kubernetes/errors.go index 95349e30e..799996af2 100644 --- a/cluster/kubernetes/errors.go +++ b/cluster/kubernetes/errors.go @@ -17,15 +17,16 @@ perhaps verify its presence using kubectl. `, obj)} } -func UpdateNotSupportedError(kind string) error { +func UpdateNotSupportedError(kind string) *fluxerr.Error { return &fluxerr.Error{ Type: fluxerr.User, Err: fmt.Errorf("updating resource kind %q not supported", kind), Help: `Flux does not support updating ` + kind + ` resources. -This may be because those resources do not use images, or because it -is a new kind of resource in Kubernetes, and Flux does not support it -yet. +This may be because those resources do not use images, you are trying +to use a YAML dot notation path annotation for a non HelmRelease +resource, or because it is a new kind of resource in Kubernetes, and +Flux does not support it yet. If you can use a Deployment instead, Flux can work with those. Otherwise, you may have to update the resource manually (e.g., diff --git a/cluster/kubernetes/kubeyaml.go b/cluster/kubernetes/kubeyaml.go index 68f4b05b2..ca5c7c7e6 100644 --- a/cluster/kubernetes/kubeyaml.go +++ b/cluster/kubernetes/kubeyaml.go @@ -26,6 +26,13 @@ func (k KubeYAML) Annotate(in []byte, ns, kind, name string, policies ...string) return execKubeyaml(in, args) } +// Set calls the kubeyaml subcommand `set` with the arguments given. +func (k KubeYAML) Set(in []byte, ns, kind, name string, values ...string) ([]byte, error) { + args := []string{"set", "--namespace", ns, "--kind", kind, "--name", name} + args = append(args, values...) + return execKubeyaml(in, args) +} + func execKubeyaml(in []byte, args []string) ([]byte, error) { cmd := exec.Command("kubeyaml", args...) out := &bytes.Buffer{} diff --git a/cluster/kubernetes/manifests.go b/cluster/kubernetes/manifests.go index 15f569e98..de82bba26 100644 --- a/cluster/kubernetes/manifests.go +++ b/cluster/kubernetes/manifests.go @@ -133,7 +133,32 @@ func (m *manifests) ParseManifest(def []byte, source string) (map[string]resourc } func (m *manifests) SetWorkloadContainerImage(def []byte, id resource.ID, container string, image image.Ref) ([]byte, error) { - return updateWorkload(def, id, container, image) + resources, err := m.ParseManifest(def, "stdin") + if err != nil { + return nil, err + } + res, ok := resources[id.String()] + if !ok { + return nil, fmt.Errorf("resource %s not found", id.String()) + } + // Check if the workload is a HelmRelease, and try to resolve an image + // map for the given container to perform an update based on mapped YAML + // dot notation paths. If resolving the map fails (either because there + // is no map for the given container, or the mapping does not resolve + // in to a valid image ref), it returns the error. + // + // NB: we do this here and not in e.g. the `resource` package, to ensure + // everything _outside_ this package only knows about Kubernetes native + // containers, and not the dot notation YAML paths we invented for custom + // Helm value structures. + if hr, ok := res.(*kresource.HelmRelease); ok { + paths, err := hr.GetContainerImageMap(container) + if err != nil { + return nil, err + } + return updateWorkloadImagePaths(def, id, paths, image) + } + return updateWorkloadContainer(def, id, container, image) } func (m *manifests) CreateManifestPatch(originalManifests, modifiedManifests []byte, originalSource, modifiedSource string) ([]byte, error) { diff --git a/cluster/kubernetes/policies_test.go b/cluster/kubernetes/policies_test.go index d71db1903..cb7a66bda 100644 --- a/cluster/kubernetes/policies_test.go +++ b/cluster/kubernetes/policies_test.go @@ -168,6 +168,14 @@ func TestUpdatePolicies(t *testing.T) { }, wantErr: true, }, + { + name: "add tag policy with alternative prefix does not change existing prefix", + in: []string{"filter.fluxcd.io/nginx", "glob:*"}, + out: []string{"filter.fluxcd.io/nginx", "glob:*"}, + update: resource.PolicyUpdate{ + Add: policy.Set{policy.TagPrefix("nginx"): "glob:*"}, + }, + }, { name: "set tag to all containers", in: nil, diff --git a/cluster/kubernetes/resource/helmrelease.go b/cluster/kubernetes/resource/helmrelease.go index beaae3888..c19815659 100644 --- a/cluster/kubernetes/resource/helmrelease.go +++ b/cluster/kubernetes/resource/helmrelease.go @@ -2,23 +2,121 @@ package resource import ( "fmt" + "github.com/Jeffail/gabs" "sort" + "strings" "github.com/weaveworks/flux/image" "github.com/weaveworks/flux/resource" ) -// ReleaseContainerName is the name used when Flux interprets a -// HelmRelease as having a container with an image, by virtue of -// having a `values` stanza with an image field: -// -// spec: -// ... -// values: -// image: some/image:version -// -// The name refers to the source of the image value. -const ReleaseContainerName = "chart-image" +const ( + // ReleaseContainerName is the name used when Flux interprets a + // HelmRelease as having a container with an image, by virtue of + // having a `values` stanza with an image field: + // + // spec: + // ... + // values: + // image: some/image:version + // + // The name refers to the source of the image value. + ReleaseContainerName = "chart-image" + + // ImageBasePath is the default base path for image path mappings + // in a HelmRelease resource. + ImageBasePath = "spec.values." + // ImageRegistryPrefix is the annotation key prefix for image + // registry path mappings. + ImageRegistryPrefix = "registry.fluxcd.io/" + // ImageRepositoryPrefix is the annotation key prefix for image + // repository path mappings. + ImageRepositoryPrefix = "repository.fluxcd.io/" + // ImageRepositoryPrefix is the annotation key prefix for image + // tag path mappings. + ImageTagPrefix = "tag.fluxcd.io/" +) + +// ContainerImageMap holds the YAML dot notation paths to a +// container image. +type ContainerImageMap struct { + BasePath string + Registry string + Repository string + Tag string +} + +// RepositoryOnly returns if only the repository is defined. +func (c ContainerImageMap) RepositoryOnly() bool { + return c.Repository != "" && c.Registry == "" && c.Tag == "" +} + +// RegistryRepository returns if the repository and tag are +// defined, but the registry is not. +func (c ContainerImageMap) RepositoryTag() bool { + return c.Repository != "" && c.Tag != "" && c.Registry == "" +} + +// RegistryRepository returns if the registry and repository are +// defined, but the tag is not. +func (c ContainerImageMap) RegistryRepository() bool { + return c.Registry != "" && c.Repository != "" && c.Tag == "" +} + +// AllDefined returns if all image elements are defined. +func (c ContainerImageMap) AllDefined() bool { + return c.Registry != "" && c.Repository != "" && c.Tag != "" +} + +// GetRegistry returns the full registry path (with base path). +func (c ContainerImageMap) GetRegistry() string { + if c.Registry == "" { + return c.Registry + } + return fmt.Sprintf("%s%s", c.BasePath, c.Registry) +} + +// GetRepository returns the full repository path (with base path). +func (c ContainerImageMap) GetRepository() string { + if c.Repository == "" { + return c.Repository + } + return fmt.Sprintf("%s%s", c.BasePath, c.Repository) +} + +// GetTag returns the full tag path (with base path). +func (c ContainerImageMap) GetTag() string { + if c.Tag == "" { + return c.Tag + } + return fmt.Sprintf("%s%s", c.BasePath, c.Tag) +} + +// MapImageRef maps the given imageRef to the dot notation paths +// ContainerImageMap holds. It needs at least an Repository to be able +// to compose the map, and takes the absence of the registry and/or tag +// paths into account to ensure all image elements (registry, +// repository, tag) are present in the returned map. +func (c ContainerImageMap) MapImageRef(image image.Ref) (map[string]string, bool) { + m := make(map[string]string) + switch { + case c.AllDefined(): + m[c.GetRegistry()] = image.Domain + m[c.GetRepository()] = image.Image + m[c.GetTag()] = image.Tag + case c.RegistryRepository(): + m[c.GetRegistry()] = image.Domain + m[c.GetRepository()] = image.Image + ":" + image.Tag + case c.RepositoryTag(): + m[c.GetRepository()] = image.Name.String() + m[c.GetTag()] = image.Tag + case c.RepositoryOnly(): + m[c.GetRepository()] = image.String() + default: + return m, false + } + return m, true +} // HelmRelease echoes the generated type for the custom resource // definition. It's here so we can 1. get `baseObject` in there, and @@ -33,40 +131,67 @@ type HelmRelease struct { type ImageSetter func(image.Ref) -// The type we have to interpret as containers is a -// `map[string]interface{}`; and, we want a stable order to the -// containers we output, since things will jump around in API calls, -// or fail to verify, otherwise. Since we can't get them in the order -// they appear in the document, sort them. -func sorted_keys(values map[string]interface{}) []string { +type imageAndSetter struct { + image image.Ref + setter ImageSetter +} + +// sorted_containers returns an array of container names in ascending +// order, except for `ReleaseContainerName`, which always goes first. +// We want a stable order to the containers we output, since things +// will jump around in API calls, or fail to verify, otherwise. +func sorted_containers(containers map[string]imageAndSetter) []string { var keys []string - for k := range values { + for k := range containers { keys = append(keys, k) } - sort.Strings(keys) + sort.Slice(keys, func(i, j int) bool { + if keys[i] == ReleaseContainerName { + return true + } + if keys[j] == ReleaseContainerName { + return false + } + return keys[i] < keys[j] + }) return keys } // FindHelmReleaseContainers examines the Values from a // HelmRelease (manifest, or cluster resource, or otherwise) and // calls visit with each container name and image it finds, as well as -// procedure for changing the image value. It will return an error if -// it cannot interpret the values as specifying images, or if the -// `visit` function itself returns an error. -func FindHelmReleaseContainers(values map[string]interface{}, visit func(string, image.Ref, ImageSetter) error) error { +// procedure for changing the image value. +func FindHelmReleaseContainers(annotations map[string]string, values map[string]interface{}, + visit func(string, image.Ref, ImageSetter) error) { + + containers := make(map[string]imageAndSetter) // an image defined at the top-level is given a standard container name: if image, setter, ok := interpretAsContainer(stringMap(values)); ok { - visit(ReleaseContainerName, image, setter) + containers[ReleaseContainerName] = imageAndSetter{image, setter} } // an image as part of a field is treated as a "container" spec // named for the field: - for _, k := range sorted_keys(values) { - if image, setter, ok := interpret(values[k]); ok { - visit(k, image, setter) + for k, v := range values { + if image, setter, ok := interpret(v); ok { + containers[k] = imageAndSetter{image, setter} } } - return nil + + // user mapped images, it will overwrite automagically interpreted + // images with user defined ones: + for k, v := range containerImageMappingsFromAnnotations(annotations) { + if image, setter, ok := interpretMappedContainerImage(values, v); ok { + containers[k] = imageAndSetter{image, setter} + } + } + + // sort the found containers by name, using the custom logic + // defined in sorted_containers, so the calls to visit are + // predictable: + for _, k := range sorted_containers(containers) { + visit(k, containers[k].image, containers[k].setter) + } } // The following is some machinery for interpreting a @@ -146,7 +271,7 @@ func interpretAsContainer(m mapper) (image.Ref, ImageSetter, bool) { return case reggy: m.set("registry", ref.Domain) - m.set("image", ref.Name.Image + ":" + ref.Tag) + m.set("image", ref.Name.Image+":"+ref.Tag) case taggy: m.set("image", ref.Name.String()) m.set("tag", ref.Tag) @@ -220,18 +345,110 @@ func interpretAsImage(m mapper) (image.Ref, ImageSetter, bool) { return image.Ref{}, nil, false } +// containerImageMappingsFromAnnotations collects yaml dot notation +// mappings of container images from the given annotations. +func containerImageMappingsFromAnnotations(annotations map[string]string) map[string]ContainerImageMap { + cim := make(map[string]ContainerImageMap) + for k, v := range annotations { + switch { + case strings.HasPrefix(k, ImageRegistryPrefix): + container := strings.TrimPrefix(k, ImageRegistryPrefix) + i, _ := cim[container] + i.Registry = v + cim[container] = i + case strings.HasPrefix(k, ImageRepositoryPrefix): + container := strings.TrimPrefix(k, ImageRepositoryPrefix) + i, _ := cim[container] + i.Repository = v + cim[container] = i + case strings.HasPrefix(k, ImageTagPrefix): + container := strings.TrimPrefix(k, ImageTagPrefix) + i, _ := cim[container] + i.Tag = v + cim[container] = i + } + } + for k, _ := range cim { + i, _ := cim[k] + i.BasePath = ImageBasePath + cim[k] = i + } + return cim +} + +// interpretMappedContainerImage attempt to resolve the paths in the +// `ContainerImageMap` from the given values and tries to parse the +// resolved values into a valid `image.Ref`. It returns the +// `image.Ref`, an `ImageSetter` that is able to modify the image in +// the supplied values map, and a boolean that reflects if the +// interpretation was successful. +func interpretMappedContainerImage(values map[string]interface{}, cim ContainerImageMap) (image.Ref, ImageSetter, bool) { + v, err := gabs.Consume(values) + if err != nil { + return image.Ref{}, nil, false + } + + imageValue := v.Path(cim.Repository).Data() + if img, ok := imageValue.(string); ok { + switch { + case cim.RepositoryOnly(): + if imgRef, err := image.ParseRef(img); err == nil { + return imgRef, func(ref image.Ref) { + v.SetP(ref.String(), cim.Repository) + }, true + } + case cim.AllDefined(): + registryValue := v.Path(cim.Registry).Data() + if reg, ok := registryValue.(string); ok { + tagValue := v.Path(cim.Tag).Data() + if tag, ok := tagValue.(string); ok { + if imgRef, err := image.ParseRef(reg + "/" + img + ":" + tag); err == nil { + return imgRef, func(ref image.Ref) { + v.SetP(ref.Domain, cim.Registry) + v.SetP(ref.Image, cim.Repository) + v.SetP(ref.Tag, cim.Tag) + }, true + } + } + } + case cim.RegistryRepository(): + registryValue := v.Path(cim.Registry).Data() + if reg, ok := registryValue.(string); ok { + if imgRef, err := image.ParseRef(reg + "/" + img); err == nil { + return imgRef, func(ref image.Ref) { + v.SetP(ref.Domain, cim.Registry) + v.SetP(ref.Name.Image+":"+ref.Tag, cim.Repository) + }, true + } + } + case cim.RepositoryTag(): + tagValue := v.Path(cim.Tag).Data() + if tag, ok := tagValue.(string); ok { + if imgRef, err := image.ParseRef(img + ":" + tag); err == nil { + return imgRef, func(ref image.Ref) { + v.SetP(ref.Name.String(), cim.Repository) + v.SetP(ref.Tag, cim.Tag) + }, true + } + } + } + } + + return image.Ref{}, nil, false +} + // Containers returns the containers that are defined in the // HelmRelease. func (hr HelmRelease) Containers() []resource.Container { var containers []resource.Container - // If there's an error in interpreting, return what we have. - _ = FindHelmReleaseContainers(hr.Spec.Values, func(container string, image image.Ref, _ ImageSetter) error { + addContainer := func(container string, image image.Ref, _ ImageSetter) error { containers = append(containers, resource.Container{ Name: container, Image: image, }) return nil - }) + } + FindHelmReleaseContainers(hr.Meta.Annotations, hr.Spec.Values, addContainer) return containers } @@ -241,17 +458,29 @@ func (hr HelmRelease) Containers() []resource.Container { // get away with a value-typed receiver because we set a map entry. func (hr HelmRelease) SetContainerImage(container string, ref image.Ref) error { found := false - if err := FindHelmReleaseContainers(hr.Spec.Values, func(name string, image image.Ref, setter ImageSetter) error { + imageSetter := func(name string, image image.Ref, setter ImageSetter) error { if container == name { setter(ref) found = true } return nil - }); err != nil { - return err } + FindHelmReleaseContainers(hr.Meta.Annotations, hr.Spec.Values, imageSetter) if !found { return fmt.Errorf("did not find container %s in HelmRelease", container) } return nil } + +// GetContainerImageMap returns the ContainerImageMap for a container, +// or an error if we were unable to interpret the mapping, or no mapping +// was found. +func (hr HelmRelease) GetContainerImageMap(container string) (ContainerImageMap, error) { + cim := containerImageMappingsFromAnnotations(hr.Meta.Annotations) + if c, ok := cim[container]; ok { + if _, _, ok = interpretMappedContainerImage(hr.Spec.Values, c); ok { + return c, nil + } + } + return ContainerImageMap{}, fmt.Errorf("did not find image map for container %s in HelmRelease", container) +} diff --git a/cluster/kubernetes/resource/helmrelease_test.go b/cluster/kubernetes/resource/helmrelease_test.go index ee424c6d7..36442b4bf 100644 --- a/cluster/kubernetes/resource/helmrelease_test.go +++ b/cluster/kubernetes/resource/helmrelease_test.go @@ -2,11 +2,25 @@ package resource import ( "fmt" + "github.com/stretchr/testify/assert" "testing" "github.com/weaveworks/flux/resource" ) +func TestSortedContainers(t *testing.T) { + unordered, expected := map[string]imageAndSetter{ + "ZZZ": {}, + "AAA": {}, + "FFF": {}, + ReleaseContainerName: {}, + }, []string{ReleaseContainerName, "AAA", "FFF", "ZZZ"} + + actual := sorted_containers(unordered) + + assert.Equal(t, expected, actual) +} + func TestParseImageOnlyFormat(t *testing.T) { expectedImage := "bitnami/ghost:1.21.5-r0" doc := `--- @@ -777,6 +791,415 @@ spec: } } +func TestParseMappedImageOnly(t *testing.T) { + expectedContainer := "mariadb" + expectedImage := "bitnami/mariadb:10.1.30-r1" + doc := `--- +apiVersion: helm.integrations.flux.weave.works/v1alpha2 +kind: FluxHelmRelease +metadata: + name: mariadb + namespace: maria + annotations: + ` + ImageRepositoryPrefix + expectedContainer + `: customRepository + labels: + chart: mariadb +spec: + chartGitPath: mariadb + values: + first: post + customRepository: ` + expectedImage + ` + persistence: + enabled: false +` + + resources, err := ParseMultidoc([]byte(doc), "test") + if err != nil { + t.Fatal(err) + } + res, ok := resources["maria:fluxhelmrelease/mariadb"] + if !ok { + t.Fatalf("expected resource not found; instead got %#v", resources) + } + fhr, ok := res.(resource.Workload) + if !ok { + t.Fatalf("expected resource to be a Workload, instead got %#v", res) + } + + containers := fhr.Containers() + if len(containers) != 1 { + t.Fatalf("expected 1 container; got %#v", containers) + } + container := containers[0].Name + if container != expectedContainer { + t.Errorf("expected container container %q, got %q", expectedContainer, container) + } + image := containers[0].Image.String() + if image != expectedImage { + t.Errorf("expected container image %q, got %q", expectedImage, image) + } + + newImage := containers[0].Image.WithNewTag("some-other-tag") + if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + t.Error(err) + } + + containers = fhr.Containers() + if len(containers) != 1 { + t.Fatalf("expected 1 container; got %#v", containers) + } + image = containers[0].Image.String() + if image != newImage.String() { + t.Errorf("expected container image %q, got %q", newImage.String(), image) + } + if containers[0].Name != expectedContainer { + t.Errorf("expected container name %q, got %q", expectedContainer, containers[0].Name) + } +} + +func TestParseMappedImageTag(t *testing.T) { + expectedContainer := "mariadb" + expectedImageName := "bitnami/mariadb" + expectedImageTag := "10.1.30-r1" + expectedImage := expectedImageName + ":" + expectedImageTag + doc := `--- +apiVersion: helm.integrations.flux.weave.works/v1alpha2 +kind: FluxHelmRelease +metadata: + name: mariadb + namespace: maria + annotations: + ` + ImageRepositoryPrefix + expectedContainer + `: customRepository + ` + ImageTagPrefix + expectedContainer + `: customTag + labels: + chart: mariadb +spec: + chartGitPath: mariadb + values: + first: post + customRepository: ` + expectedImageName + ` + customTag: ` + expectedImageTag + ` + persistence: + enabled: false +` + + resources, err := ParseMultidoc([]byte(doc), "test") + if err != nil { + t.Fatal(err) + } + res, ok := resources["maria:fluxhelmrelease/mariadb"] + if !ok { + t.Fatalf("expected resource not found; instead got %#v", resources) + } + fhr, ok := res.(resource.Workload) + if !ok { + t.Fatalf("expected resource to be a Workload, instead got %#v", res) + } + + containers := fhr.Containers() + if len(containers) != 1 { + t.Fatalf("expected 1 container; got %#v", containers) + } + container := containers[0].Name + if container != expectedContainer { + t.Errorf("expected container container %q, got %q", expectedContainer, container) + } + image := containers[0].Image.String() + if image != expectedImage { + t.Errorf("expected container image %q, got %q", expectedImage, image) + } + + newImage := containers[0].Image.WithNewTag("some-other-tag") + if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + t.Error(err) + } + + containers = fhr.Containers() + if len(containers) != 1 { + t.Fatalf("expected 1 container; got %#v", containers) + } + image = containers[0].Image.String() + if image != newImage.String() { + t.Errorf("expected container image %q, got %q", newImage.String(), image) + } + if containers[0].Name != expectedContainer { + t.Errorf("expected container name %q, got %q", expectedContainer, containers[0].Name) + } +} + +func TestParseMappedRegistryImage(t *testing.T) { + expectedContainer := "mariadb" + expectedRegistry := "docker.io" + expectedImageName := "bitnami/mariadb:10.1.30-r1" + expectedImage := expectedRegistry + "/" + expectedImageName + doc := `--- +apiVersion: helm.integrations.flux.weave.works/v1alpha2 +kind: FluxHelmRelease +metadata: + name: mariadb + namespace: maria + annotations: + ` + ImageRegistryPrefix + expectedContainer + `: customRegistry + ` + ImageRepositoryPrefix + expectedContainer + `: customImage + labels: + chart: mariadb +spec: + chartGitPath: mariadb + values: + first: post + customRegistry: ` + expectedRegistry + ` + customImage: ` + expectedImageName + ` + persistence: + enabled: false +` + + resources, err := ParseMultidoc([]byte(doc), "test") + if err != nil { + t.Fatal(err) + } + res, ok := resources["maria:fluxhelmrelease/mariadb"] + if !ok { + t.Fatalf("expected resource not found; instead got %#v", resources) + } + fhr, ok := res.(resource.Workload) + if !ok { + t.Fatalf("expected resource to be a Workload, instead got %#v", res) + } + + containers := fhr.Containers() + if len(containers) != 1 { + t.Fatalf("expected 1 container; got %#v", containers) + } + container := containers[0].Name + if container != expectedContainer { + t.Errorf("expected container container %q, got %q", expectedContainer, container) + } + image := containers[0].Image.String() + if image != expectedImage { + t.Errorf("expected container image %q, got %q", expectedImage, image) + } + + newImage := containers[0].Image.WithNewTag("some-other-tag") + if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + t.Error(err) + } + + containers = fhr.Containers() + if len(containers) != 1 { + t.Fatalf("expected 1 container; got %#v", containers) + } + image = containers[0].Image.String() + if image != newImage.String() { + t.Errorf("expected container image %q, got %q", newImage.String(), image) + } + if containers[0].Name != expectedContainer { + t.Errorf("expected container name %q, got %q", expectedContainer, containers[0].Name) + } +} + +func TestParseMappedRegistryImageTag(t *testing.T) { + expectedContainer := "mariadb" + expectedRegistry := "index.docker.io" + expectedImageName := "bitnami/mariadb" + expectedImageTag := "10.1.30-r1" + expectedImage := expectedRegistry + "/" + expectedImageName + ":" + expectedImageTag + doc := `--- +apiVersion: helm.integrations.flux.weave.works/v1alpha2 +kind: FluxHelmRelease +metadata: + name: mariadb + namespace: maria + annotations: + ` + ImageRegistryPrefix + expectedContainer + `: customRegistry + ` + ImageRepositoryPrefix + expectedContainer + `: customRepository + ` + ImageTagPrefix + expectedContainer + `: customTag + labels: + chart: mariadb +spec: + chartGitPath: mariadb + values: + first: post + customRegistry: ` + expectedRegistry + ` + customRepository: ` + expectedImageName + ` + customTag: ` + expectedImageTag + ` + persistence: + enabled: false +` + + resources, err := ParseMultidoc([]byte(doc), "test") + if err != nil { + t.Fatal(err) + } + res, ok := resources["maria:fluxhelmrelease/mariadb"] + if !ok { + t.Fatalf("expected resource not found; instead got %#v", resources) + } + fhr, ok := res.(resource.Workload) + if !ok { + t.Fatalf("expected resource to be a Workload, instead got %#v", res) + } + + containers := fhr.Containers() + if len(containers) != 1 { + t.Fatalf("expected 1 container; got %#v", containers) + } + container := containers[0].Name + if container != expectedContainer { + t.Errorf("expected container container %q, got %q", expectedContainer, container) + } + image := containers[0].Image.String() + if image != expectedImage { + t.Errorf("expected container image %q, got %q", expectedImage, image) + } + + newImage := containers[0].Image.WithNewTag("some-other-tag") + if err := fhr.SetContainerImage(expectedContainer, newImage); err != nil { + t.Error(err) + } + + containers = fhr.Containers() + if len(containers) != 1 { + t.Fatalf("expected 1 container; got %#v", containers) + } + image = containers[0].Image.String() + if image != newImage.String() { + t.Errorf("expected container image %q, got %q", newImage.String(), image) + } + if containers[0].Name != expectedContainer { + t.Errorf("expected container name %q, got %q", expectedContainer, containers[0].Name) + } +} + +func TestParseMappedTagOnly(t *testing.T) { + container := "mariadb" + imageTag := "10.1.30-r1" + doc := `--- +apiVersion: helm.integrations.flux.weave.works/v1alpha2 +kind: FluxHelmRelease +metadata: + name: mariadb + namespace: maria + annotations: + ` + ImageTagPrefix + container + `: customTag + labels: + chart: mariadb +spec: + chartGitPath: mariadb + values: + first: post + customTag: ` + imageTag + ` + persistence: + enabled: false +` + + resources, err := ParseMultidoc([]byte(doc), "test") + if err != nil { + t.Fatal(err) + } + res, ok := resources["maria:fluxhelmrelease/mariadb"] + if !ok { + t.Fatalf("expected resource not found; instead got %#v", resources) + } + fhr, ok := res.(resource.Workload) + if !ok { + t.Fatalf("expected resource to be a Workload, instead got %#v", res) + } + + containers := fhr.Containers() + if len(containers) != 0 { + t.Errorf("expected 0 container; got %#v", containers) + } +} + +func TestParseMappedImageFormats(t *testing.T) { + + expected := []struct { + name, registry, image, tag string + }{ + {"AAA", "", "repo/imageOne", "tagOne"}, + {"CCC", "", "repo/imageTwo", "tagTwo"}, + {"NNN", "", "repo/imageThree", "tagThree"}, + {"ZZZ", "registry.com", "repo/imageFour", "tagFour"}, + } + + doc := `--- +apiVersion: helm.integrations.flux.weave.works/v1alpha2 +kind: FluxHelmRelease +metadata: + name: test + namespace: test + annotations: + # Top level mapping + ` + ImageRepositoryPrefix + expected[0].name + `: customRepository + ` + ImageTagPrefix + expected[0].name + `: customTag + # Sub level mapping + ` + ImageRepositoryPrefix + expected[1].name + `: ` + expected[1].name + `.customRepository + ` + ImageTagPrefix + expected[1].name + `: ` + expected[1].name + `.customTag + # Sub level mapping 2 + ` + ImageRepositoryPrefix + expected[2].name + `: ` + expected[2].name + `.image.customRepository + # Sub level mapping 3 + ` + ImageRegistryPrefix + expected[3].name + `: ` + expected[3].name + `.nested.deep.customRegistry + ` + ImageRepositoryPrefix + expected[3].name + `: ` + expected[3].name + `.nested.deep.customRepository + ` + ImageTagPrefix + expected[3].name + `: ` + expected[3].name + `.nested.deep.customTag +spec: + chartGitPath: test + values: + # Top level image + customRepository: ` + expected[0].image + ` + customTag: ` + expected[0].tag + ` + + # Sub level image + ` + expected[1].name + `: + customRepository: ` + expected[1].image + ` + customTag: ` + expected[1].tag + ` + + # Sub level image 2 + ` + expected[2].name + `: + image: + customRepository: ` + expected[2].image + `:` + expected[2].tag + ` + + # Sub level image 3 + ` + expected[3].name + `: + nested: + deep: + customRegistry: ` + expected[3].registry + ` + customRepository: ` + expected[3].image + ` + customTag: ` + expected[3].tag + ` +` + + resources, err := ParseMultidoc([]byte(doc), "test") + if err != nil { + t.Fatal(err) + } + res, ok := resources["test:fluxhelmrelease/test"] + if !ok { + t.Fatalf("expected resource not found; instead got %#v", resources) + } + fhr, ok := res.(resource.Workload) + if !ok { + t.Fatalf("expected resource to be a Workload, instead got %#v", res) + } + + containers := fhr.Containers() + if len(containers) != len(expected) { + t.Fatalf("expected %d containers, got %d, %#v", len(expected), len(containers), containers) + } + for i, c0 := range expected { + c1 := containers[i] + if c1.Name != c0.name { + t.Errorf("names do not match %q != %q", c0, c1) + } + var c0image string + if c0.registry != "" { + c0image = c0.registry + "/" + } + c0image += fmt.Sprintf("%s:%s", c0.image, c0.tag) + if c1.Image.String() != c0image { + t.Errorf("images do not match %q != %q", c0image, c1.Image.String()) + } + } +} + func TestParseAllFormatsInOne(t *testing.T) { type container struct { @@ -795,7 +1218,8 @@ func TestParseAllFormatsInOne(t *testing.T) { {"DDD", "", "repo/imageThree", "tagThree"}, {"HHH", "registry.com", "repo/imageFour", "tagFour"}, {"NNN", "registry.com", "repo/imageFive", "tagFive"}, - {"ZZZ", "registry.com", "repo/imageSix", "tagSix"}, + {"XXX", "registry.com", "repo/imageSix", "tagSix"}, + {"ZZZ", "", "repo/imageSeven", "tagSeven"}, } doc := `--- @@ -804,6 +1228,9 @@ kind: HelmRelease metadata: name: test namespace: test + annotations: + ` + ImageRepositoryPrefix + expected[6].name + `: ` + expected[6].name + `.customRepository + ` + ImageTagPrefix + expected[6].name + `: ` + expected[6].name + `.customTag spec: chart: git: git@github.com:fluxcd/flux-get-started @@ -845,6 +1272,11 @@ spec: registry: ` + expected[5].registry + ` repository: ` + expected[5].image + ` tag: ` + expected[5].tag + ` + + # mapped by user annotations + ` + expected[6].name + `: + customRepository: ` + expected[6].image + ` + customTag: ` + expected[6].tag + ` ` resources, err := ParseMultidoc([]byte(doc), "test") diff --git a/cluster/kubernetes/resource/resource.go b/cluster/kubernetes/resource/resource.go index cb8b35849..95a6d84ed 100644 --- a/cluster/kubernetes/resource/resource.go +++ b/cluster/kubernetes/resource/resource.go @@ -3,6 +3,7 @@ package resource import ( "strings" + jsonyaml "github.com/ghodss/yaml" "gopkg.in/yaml.v2" fluxerr "github.com/weaveworks/flux/errors" @@ -11,7 +12,8 @@ import ( ) const ( - PolicyPrefix = "fluxcd.io/" + PolicyPrefix = "fluxcd.io/" + FilterPolicyPrefix = "filter.fluxcd.io/" // This is the previously-used prefix for annotations; many // manifests in the wild will still be using it, so it's included // here for backward-compatibility. @@ -99,6 +101,8 @@ func PoliciesFromAnnotations(annotations map[string]string) policy.Set { p = strings.TrimPrefix(k, PolicyPrefix) case strings.HasPrefix(k, AlternatePolicyPrefix): p = strings.TrimPrefix(k, AlternatePolicyPrefix) + case strings.HasPrefix(k, FilterPolicyPrefix): + p = "tag." + strings.TrimPrefix(k, FilterPolicyPrefix) default: continue } @@ -121,8 +125,11 @@ func (o baseObject) Policies() policy.Set { // than one way of using annotations for policy. If the policy is not // present, returns `"", false`. func (o baseObject) PolicyAnnotationKey(p string) (string, bool) { - for _, prefix := range []string{PolicyPrefix, AlternatePolicyPrefix} { + for _, prefix := range []string{PolicyPrefix, AlternatePolicyPrefix, FilterPolicyPrefix} { key := prefix + p + if prefix == FilterPolicyPrefix { + key = prefix + strings.TrimPrefix(p, "tag.") + } if _, ok := o.Meta.Annotations[key]; ok { return key, true } @@ -198,7 +205,11 @@ func unmarshalKind(base baseObject, bytes []byte) (KubeManifest, error) { return &list, nil case base.Kind == "FluxHelmRelease" || base.Kind == "HelmRelease": var hr = HelmRelease{baseObject: base} - if err := yaml.Unmarshal(bytes, &hr); err != nil { + // NB: workaround for go-yaml/yaml/issues/139 + // By using github.com/ghodss/yaml to unmarshal HelmReleases. + // This effectively results in all keys of `Value`s being strings + // and not interface{}. + if err := jsonyaml.Unmarshal(bytes, &hr); err != nil { return nil, err } return &hr, nil diff --git a/cluster/kubernetes/resourcekinds.go b/cluster/kubernetes/resourcekinds.go index e2fac6f10..05e43dafc 100644 --- a/cluster/kubernetes/resourcekinds.go +++ b/cluster/kubernetes/resourcekinds.go @@ -436,7 +436,7 @@ func (fhr *fluxHelmReleaseKind) getWorkloads(ctx context.Context, c *Cluster, na } func makeFluxHelmReleaseWorkload(fluxHelmRelease *fhr_v1alpha2.FluxHelmRelease) workload { - containers := createK8sHRContainers(fluxHelmRelease.Spec.Values) + containers := createK8sHRContainers(fluxHelmRelease.ObjectMeta.Annotations, fluxHelmRelease.Spec.Values) podTemplate := apiv1.PodTemplateSpec{ ObjectMeta: fluxHelmRelease.ObjectMeta, @@ -447,7 +447,7 @@ func makeFluxHelmReleaseWorkload(fluxHelmRelease *fhr_v1alpha2.FluxHelmRelease) } // apiVersion & kind must be set, since TypeMeta is not populated fluxHelmRelease.APIVersion = "helm.integrations.flux.weave.works/v1alpha2" - fluxHelmRelease.Kind = "HelmRelease" + fluxHelmRelease.Kind = "FluxHelmRelease" return workload{ status: fluxHelmRelease.Status.ReleaseStatus, podTemplate: podTemplate, @@ -458,9 +458,9 @@ func makeFluxHelmReleaseWorkload(fluxHelmRelease *fhr_v1alpha2.FluxHelmRelease) // createK8sContainers creates a list of k8s containers by // interpreting the HelmRelease resource. The interpretation is // analogous to that in cluster/kubernetes/resource/fluxhelmrelease.go -func createK8sHRContainers(values map[string]interface{}) []apiv1.Container { +func createK8sHRContainers(annotations map[string]string, values map[string]interface{}) []apiv1.Container { var containers []apiv1.Container - _ = kresource.FindHelmReleaseContainers(values, func(name string, image image.Ref, _ kresource.ImageSetter) error { + kresource.FindHelmReleaseContainers(annotations, values, func(name string, image image.Ref, _ kresource.ImageSetter) error { containers = append(containers, apiv1.Container{ Name: name, Image: image.String(), @@ -531,7 +531,7 @@ func (hr *helmReleaseKind) getWorkloads(ctx context.Context, c *Cluster, namespa } func makeHelmReleaseBetaWorkload(helmRelease *hr_v1beta1.HelmRelease) workload { - containers := createK8sHRContainers(helmRelease.Spec.Values) + containers := createK8sHRContainers(helmRelease.ObjectMeta.Annotations, helmRelease.Spec.Values) podTemplate := apiv1.PodTemplateSpec{ ObjectMeta: helmRelease.ObjectMeta, @@ -551,7 +551,7 @@ func makeHelmReleaseBetaWorkload(helmRelease *hr_v1beta1.HelmRelease) workload { } func makeHelmReleaseStableWorkload(helmRelease *hr_v1.HelmRelease) workload { - containers := createK8sHRContainers(helmRelease.Spec.Values) + containers := createK8sHRContainers(helmRelease.ObjectMeta.Annotations, helmRelease.Spec.Values) podTemplate := apiv1.PodTemplateSpec{ ObjectMeta: helmRelease.ObjectMeta, diff --git a/cluster/kubernetes/update.go b/cluster/kubernetes/update.go index eb9a1fdc8..95b9bba72 100644 --- a/cluster/kubernetes/update.go +++ b/cluster/kubernetes/update.go @@ -1,21 +1,46 @@ package kubernetes import ( + "fmt" "strings" + kresource "github.com/weaveworks/flux/cluster/kubernetes/resource" "github.com/weaveworks/flux/image" "github.com/weaveworks/flux/resource" ) -// updateWorkload takes a YAML document stream (one or more YAML -// docs, as bytes), a resource ID referring to a controller, a +// updateWorkloadContainer takes a YAML document stream (one or more +// YAML docs, as bytes), a resource ID referring to a controller, a // container name, and the name of the new image that should be used // for the container. It returns a new YAML stream where the image for // the container has been replaced with the imageRef supplied. -func updateWorkload(in []byte, resource resource.ID, container string, newImageID image.Ref) ([]byte, error) { +func updateWorkloadContainer(in []byte, resource resource.ID, container string, newImageID image.Ref) ([]byte, error) { namespace, kind, name := resource.Components() if _, ok := resourceKinds[strings.ToLower(kind)]; !ok { return nil, UpdateNotSupportedError(kind) } return (KubeYAML{}).Image(in, namespace, kind, name, container, newImageID.String()) } + +// updateWorkloadImagePaths takes a YAML document stream (one or more +// YAML docs, as bytes), a resource ID referring to a HelmRelease, +// a ContainerImageMap, and the name of the new image that should be +// applied to the mapped paths. It returns a new YAML stream where +// the values of the paths have been replaced with the imageRef +// supplied. +func updateWorkloadImagePaths(in []byte, + resource resource.ID, paths kresource.ContainerImageMap, newImageID image.Ref) ([]byte, error) { + namespace, kind, name := resource.Components() + // We only support HelmRelease resource kinds for now + if kind != "helmrelease" { + return nil, UpdateNotSupportedError(kind) + } + if m, ok := paths.MapImageRef(newImageID); ok { + var args []string + for k, v := range m { + args = append(args, fmt.Sprintf("%s=%s", k, v)) + } + return (KubeYAML{}).Set(in, namespace, kind, name, args...) + } + return nil, fmt.Errorf("failed to map paths %#v to %q for %q", paths, newImageID.String(), resource.String()) +} diff --git a/cluster/kubernetes/update_test.go b/cluster/kubernetes/update_test.go index 15aef62a5..10c37a694 100644 --- a/cluster/kubernetes/update_test.go +++ b/cluster/kubernetes/update_test.go @@ -3,19 +3,21 @@ package kubernetes import ( "testing" + kresource "github.com/weaveworks/flux/cluster/kubernetes/resource" "github.com/weaveworks/flux/image" "github.com/weaveworks/flux/resource" ) type update struct { - name string - resourceID string - containers []string - updatedImage string - caseIn, caseOut string + name string + resourceID string + containers []string + updatedImage string + caseIn, caseOut string + imageAnnotations kresource.ContainerImageMap } -func testUpdate(t *testing.T, u update) { +func testUpdateWorkloadContainer(t *testing.T, u update) { id, err := image.ParseRef(u.updatedImage) if err != nil { t.Fatal(err) @@ -25,7 +27,7 @@ func testUpdate(t *testing.T, u update) { for _, container := range u.containers { var out []byte var err error - if out, err = updateWorkload([]byte(manifest), resource.MustParseID(u.resourceID), container, id); err != nil { + if out, err = updateWorkloadContainer([]byte(manifest), resource.MustParseID(u.resourceID), container, id); err != nil { t.Errorf("Failed: %s", err.Error()) return } @@ -36,32 +38,57 @@ func testUpdate(t *testing.T, u update) { } } -func TestUpdates(t *testing.T) { +func testUpdateWorkloadImagePath(t *testing.T, u update) { + id, err := image.ParseRef(u.updatedImage) + if err != nil { + t.Fatal(err) + } + + manifest := u.caseIn + var out []byte + if out, err = updateWorkloadImagePaths([]byte(manifest), resource.MustParseID(u.resourceID), u.imageAnnotations, id); err != nil { + t.Errorf("Failed: %s", err.Error()) + return + } + manifest = string(out) + if manifest != u.caseOut { + t.Errorf("it did not get expected result:\n\n%s\n\nInstead got:\n\n%s", u.caseOut, manifest) + } +} + +func TestWorkloadContainerUpdates(t *testing.T) { for _, c := range []update{ - {"common case", case1resource, case1container, case1image, case1, case1out}, - {"new version like number", case2resource, case2container, case2image, case2, case2out}, - {"old version like number", case2resource, case2container, case2reverseImage, case2out, case2}, - {"name label out of order", case3resource, case3container, case3image, case3, case3out}, - {"version (tag) with dots", case4resource, case4container, case4image, case4, case4out}, - {"minimal dockerhub image name", case5resource, case5container, case5image, case5, case5out}, - {"reordered keys", case6resource, case6containers, case6image, case6, case6out}, - {"from prod", case7resource, case7containers, case7image, case7, case7out}, - {"single quotes", case8resource, case8containers, case8image, case8, case8out}, - {"in multidoc", case9resource, case9containers, case9image, case9, case9out}, - {"in kubernetes List resource", case10resource, case10containers, case10image, case10, case10out}, - {"FluxHelmRelease (v1alpha2; simple image encoding)", case11resource, case11containers, case11image, case11, case11out}, - {"FluxHelmRelease (v1alpha2; multi image encoding)", case12resource, case12containers, case12image, case12, case12out}, - {"HelmRelease (v1beta1; image with port number)", case13resource, case13containers, case13image, case13, case13out}, - {"HelmRelease (v1)", case14resource, case14containers, case14image, case14, case14out}, - {"initContainer", case15resource, case15containers, case15image, case15, case15out}, + {"common case", case1resource, case1container, case1image, case1, case1out, emptyContainerImageMap}, + {"new version like number", case2resource, case2container, case2image, case2, case2out, emptyContainerImageMap}, + {"old version like number", case2resource, case2container, case2reverseImage, case2out, case2, emptyContainerImageMap}, + {"name label out of order", case3resource, case3container, case3image, case3, case3out, emptyContainerImageMap}, + {"version (tag) with dots", case4resource, case4container, case4image, case4, case4out, emptyContainerImageMap}, + {"minimal dockerhub image name", case5resource, case5container, case5image, case5, case5out, emptyContainerImageMap}, + {"reordered keys", case6resource, case6containers, case6image, case6, case6out, emptyContainerImageMap}, + {"from prod", case7resource, case7containers, case7image, case7, case7out, emptyContainerImageMap}, + {"single quotes", case8resource, case8containers, case8image, case8, case8out, emptyContainerImageMap}, + {"in multidoc", case9resource, case9containers, case9image, case9, case9out, emptyContainerImageMap}, + {"in kubernetes List resource", case10resource, case10containers, case10image, case10, case10out, emptyContainerImageMap}, + {"FluxHelmRelease (v1alpha2; simple image encoding)", case11resource, case11containers, case11image, case11, case11out, emptyContainerImageMap}, + {"FluxHelmRelease (v1alpha2; multi image encoding)", case12resource, case12containers, case12image, case12, case12out, emptyContainerImageMap}, + {"HelmRelease (v1beta1; image with port number)", case13resource, case13containers, case13image, case13, case13out, emptyContainerImageMap}, + {"HelmRelease (v1; with image map)", case14resource, make([]string, 0), case14image, case14, case14out, case14ImageMap}, + {"initContainer", case15resource, case15containers, case15image, case15, case15out, emptyContainerImageMap}, } { t.Run(c.name, func(t *testing.T) { - testUpdate(t, c) + switch c.imageAnnotations { + case emptyContainerImageMap: + testUpdateWorkloadContainer(t, c) + default: + testUpdateWorkloadImagePath(t, c) + } t.Parallel() }) } } +var emptyContainerImageMap = kresource.ContainerImageMap{} + // Unusual but still valid indentation between containers: and the // next line const case1 = `--- @@ -928,6 +955,9 @@ kind: HelmRelease metadata: name: mariadb namespace: maria + annotations: + repository.fluxcd.io/custom: mariadb.customImage + tag.fluxcd.io/custom: mariadb.customTag spec: chart: repository: https://example.com/charts @@ -935,8 +965,8 @@ spec: version: 1.1.2 values: mariadb: - image: localhost/mariadb - tag: 10.1.30-r1 + customImage: localhost:5000/mariadb + customTag: 10.1.30-r1 persistence: enabled: false workProperly: true @@ -945,9 +975,13 @@ spec: ` const case14resource = "maria:helmrelease/mariadb" -const case14image = "localhost/mariadb:10.1.33" +var case14ImageMap = kresource.ContainerImageMap{ + BasePath: kresource.ImageBasePath, + Repository: "mariadb.customImage", + Tag: "mariadb.customTag", +} -var case14containers = []string{"mariadb"} +const case14image = "localhost:5000/mariadb:10.1.33" const case14out = `--- apiVersion: helm.fluxcd.io/v1 @@ -955,6 +989,9 @@ kind: HelmRelease metadata: name: mariadb namespace: maria + annotations: + repository.fluxcd.io/custom: mariadb.customImage + tag.fluxcd.io/custom: mariadb.customTag spec: chart: repository: https://example.com/charts @@ -962,8 +999,8 @@ spec: version: 1.1.2 values: mariadb: - image: localhost/mariadb - tag: 10.1.33 + customImage: localhost:5000/mariadb + customTag: 10.1.33 persistence: enabled: false workProperly: true diff --git a/docker/Dockerfile.flux b/docker/Dockerfile.flux index 72e0667d0..6840bd05a 100644 --- a/docker/Dockerfile.flux +++ b/docker/Dockerfile.flux @@ -34,7 +34,7 @@ LABEL maintainer="Flux CD " \ ENTRYPOINT [ "/sbin/tini", "--", "fluxd" ] # Get the kubeyaml binary (files) and put them on the path -COPY --from=quay.io/squaremo/kubeyaml:0.6.1 /usr/lib/kubeyaml /usr/lib/kubeyaml/ +COPY --from=quay.io/squaremo/kubeyaml:0.7.0 /usr/lib/kubeyaml /usr/lib/kubeyaml/ ENV PATH=/bin:/usr/bin:/usr/local/bin:/usr/lib/kubeyaml # Create minimal nsswitch.conf file to prioritize the usage of /etc/hosts over DNS queries. diff --git a/go.mod b/go.mod index 8bb131bc9..d99d68a78 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/weaveworks/flux go 1.12 require ( + github.com/Jeffail/gabs v1.4.0 github.com/Masterminds/semver v1.4.2 github.com/aws/aws-sdk-go v1.19.11 github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 diff --git a/go.sum b/go.sum index 3b0c9f27f..7df35bae9 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,4 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= @@ -12,11 +11,11 @@ github.com/Azure/go-autorest v11.7.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo= +github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v0.0.0-20190301161902-9f8fceff796f h1:lGvI8+dm9Y/Qr6BfsbmjAz3iC3iq9+vUQLSKCDROE6s= github.com/Masterminds/sprig v0.0.0-20190301161902-9f8fceff796f/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -47,7 +46,6 @@ github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zF github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20170912183627-bc6354cbbc29 h1:llBx5m8Gk0lrAaiLud2wktkX/e8haX7Ru0oVfQqtZQ4= github.com/docker/spdystream v0.0.0-20170912183627-bc6354cbbc29/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= @@ -69,11 +67,9 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -86,9 +82,7 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/googleapis v1.2.0 h1:Z0v3OJDotX9ZBpdz2V+AI7F4fITSZhVE5mg6GQppwMM= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -98,33 +92,27 @@ github.com/golang/gddo v0.0.0-20190312205958-5a2505f3dbf0 h1:CfaPdCDbZu8jSwjq0fl github.com/golang/gddo v0.0.0-20190312205958-5a2505f3dbf0/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0= github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= @@ -145,42 +133,34 @@ github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVo github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0 h1:j30noezaCfvNLcdMYSvHLv81DxYRSt1grlpseG67vhU= github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno= github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/instrumenta/kubeval v0.0.0-20190720105720-70e32d660927 h1:r1cvxQYvoKyFHUbPpDRAJw4QRvfyWyR55cp3mS1fklc= github.com/instrumenta/kubeval v0.0.0-20190720105720-70e32d660927/go.mod h1:HeTbS2psckzaIy3V3lGbcCvSGP9f9MvrQV6s9IWGy0w= github.com/instrumenta/kubeval v0.0.0-20190804145309-805845b47dfc h1:2wBB02X45LugTLC2M5DtxFCAOK4+jgeV4Gtx1lPZu+4= github.com/instrumenta/kubeval v0.0.0-20190804145309-805845b47dfc/go.mod h1:bpiMYvNpVxWjdJsS0hDRu9TrobT5GfWCZwJseGUstxE= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/justinbarrick/go-k8s-portforward v1.0.2 h1:unfs10u6NeEfPoQL7J5nyuOyAsZqGqlRV7I7qpr0+AU= github.com/justinbarrick/go-k8s-portforward v1.0.2/go.mod h1:klMOboLnC1/UlkyJnYFjcMcbOtwAcKop+LkIZ4r428o= github.com/justinbarrick/go-k8s-portforward v1.0.4-0.20190722134107-d79fe1b9d79d h1:xQ/ZtcWCKzWg5QbOhq6RFPvevl+IE580Vm0Vgxuw3xs= github.com/justinbarrick/go-k8s-portforward v1.0.4-0.20190722134107-d79fe1b9d79d/go.mod h1:GkvGI25j2iHpJVINl/hZC+sbf9IJ1XkY1MtjSh3Usuk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -192,7 +172,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o= github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -200,7 +179,6 @@ github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 h1:KXZJFdun9knAVAR8tg/aHJEr5DgtcbqyvzacK+CDCaI= github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -208,7 +186,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833 h1:t4WWQ9I797y7QUgeEjeXnVb+oYuEDQc6gLvrZJTYo94= github.com/ncabatoff/go-seq v0.0.0-20180805175032-b08ef85ed833/go.mod h1:0CznHmXSjMEqs5Tezj/w2emQoM41wzYM9KpDKUHPYag= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= @@ -219,14 +196,12 @@ github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2i github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opentracing-contrib/go-stdlib v0.0.0-20190324214902-3020fec0e66b h1:N/+vVH19UEwFml23XATspYXdbpBU/oPvXy3CnkODjc0= github.com/opentracing-contrib/go-stdlib v0.0.0-20190324214902-3020fec0e66b/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9 h1:QsgXACQhd9QJhEmRumbsMQQvBtmdS0mafoVEBplWXEg= github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/pelletier/go-toml v0.0.0-20180724185102-c2dbbc24a979 h1:Uh8pTMDzw+nuDTW7lyxcpmYqQJFE8SnO93F3lyY4XzY= github.com/pelletier/go-toml v0.0.0-20180724185102-c2dbbc24a979/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -263,22 +238,17 @@ github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I= github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cobra v0.0.0-20180820174524-ff0d02e85550/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 h1:kJI9pPzfsULT/72wy7mxkRQZPtKWgFdCA2RTGZ4v8/E= github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20180821114517-d929dcbb1086/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.1.0 h1:V7OZpY8i3C1x/pDmU0zNNlfVoDz112fSYvtWMjjS3f4= github.com/spf13/viper v1.1.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -309,7 +279,6 @@ go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= @@ -331,14 +300,12 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -377,14 +344,12 @@ golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138 h1:H3uGjxCR/6Ds0Mjgyp7LMK81+LvmbvWWEnJhzk1Pi9E= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= -google.golang.org/api v0.3.1 h1:oJra/lMfmtm13/rgY/8i3MzjFWYXvQIAKjQ3HqofMk8= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.2 h1:iTp+3yyl/KOtxa/d1/JUE0GGSoR6FuW5udver22iwpw= google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= @@ -392,17 +357,14 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815 h1:p3qKkjcSW6m32Lr1CInA3jW53vG29/JB6QOvQWie5WI= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0 h1:DlsSIrgEBuZAUFJcta2B5i/lzeHHbnfkNFAfFXLVFYQ= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -436,15 +398,12 @@ k8s.io/code-generator v0.0.0-20190511023357-639c964206c2/go.mod h1:YMQ7Lt97nW/I6 k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/helm v2.13.1+incompatible h1:qt0LBsHQ7uxCtS3F2r3XI0DNm8ml0xQeSJixUorDyn0= k8s.io/helm v2.13.1+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= -k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c= k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/kube-openapi v0.0.0-20180509051136-39cb288412c4 h1:gW+EUB2I96nbxVenV/8ctfbACsHP+yxlT2dhMCsiy+s= k8s.io/kube-openapi v0.0.0-20180509051136-39cb288412c4/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c h1:kJCzg2vGCzah5icgkKR7O1Dzn0NA2iGlym27sb0ZfGE= k8s.io/kube-openapi v0.0.0-20190401085232-94e1e7b7574c/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7 h1:8r+l4bNWjRlsFYlQJnKJ2p7s1YQPj4XyXiJVqDHRx7c= k8s.io/utils v0.0.0-20190308190857-21c4ce38f2a7/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= k8s.io/utils v0.0.0-20190712204705-3dccf664f023 h1:1H4Jyzb0z2X0GfBMTwRjnt5ejffRHrGftUgJcV/ZfDc= k8s.io/utils v0.0.0-20190712204705-3dccf664f023/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=