From 680409079ec6031af5678319877bafbe49dad915 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Mon, 14 Jan 2019 18:12:58 +0200 Subject: [PATCH 1/7] Allow images to be excluded from scanning - add registry-exclude-image flag that accepts a list of glob expressions - exclude Kubernetes system images by default (k8s.gcr.io/*) --- cluster/kubernetes/images.go | 12 ++++++++ cluster/kubernetes/kubernetes.go | 47 +++++++++++++++++--------------- cmd/fluxd/main.go | 3 +- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/cluster/kubernetes/images.go b/cluster/kubernetes/images.go index b0436c419..ba5807dd2 100644 --- a/cluster/kubernetes/images.go +++ b/cluster/kubernetes/images.go @@ -2,6 +2,7 @@ package kubernetes import ( "fmt" + "github.com/ryanuber/go-glob" "github.com/go-kit/kit/log" "github.com/pkg/errors" @@ -140,5 +141,16 @@ func (c *Cluster) ImagesToFetch() registry.ImageCreds { } } + // remove images based on the glob exclusion list + for imageID := range allImageCreds { + imageName := imageID.CanonicalName().Name.String() + for _, exp := range c.imageExcludeList { + if glob.Glob(exp, imageName) { + //c.logger.Log("debug", fmt.Sprintf("image %s excluded from scanning by %s", imageName, exp)) + delete(allImageCreds, imageID) + } + } + } + return allImageCreds } diff --git a/cluster/kubernetes/kubernetes.go b/cluster/kubernetes/kubernetes.go index 833990ba1..7f4459f12 100644 --- a/cluster/kubernetes/kubernetes.go +++ b/cluster/kubernetes/kubernetes.go @@ -104,7 +104,8 @@ type Cluster struct { nsWhitelist []string nsWhitelistLogged map[string]bool // to keep track of whether we've logged a problem with seeing a whitelisted ns - mu sync.Mutex + imageExcludeList []string + mu sync.Mutex } // NewCluster returns a usable cluster. @@ -113,7 +114,8 @@ func NewCluster(clientset k8sclient.Interface, applier Applier, sshKeyRing ssh.KeyRing, logger log.Logger, - nsWhitelist []string) *Cluster { + nsWhitelist []string, + imageExcludeList []string) *Cluster { c := &Cluster{ client: extendedClient{ @@ -125,6 +127,7 @@ func NewCluster(clientset k8sclient.Interface, sshKeyRing: sshKeyRing, nsWhitelist: nsWhitelist, nsWhitelistLogged: map[string]bool{}, + imageExcludeList: imageExcludeList, } return c @@ -178,16 +181,16 @@ func (c *Cluster) AllControllers(namespace string) (res []cluster.Controller, er podControllers, err := resourceKind.getPodControllers(c, ns.Name) if err != nil { if se, ok := err.(*apierrors.StatusError); ok { - switch (se.ErrStatus.Reason) { - case meta_v1.StatusReasonNotFound: - // Kind not supported by API server, skip - continue - case meta_v1.StatusReasonForbidden: - // K8s can return forbidden instead of not found for non super admins - c.logger.Log("warning", "not allowed to list resources", "err", err) - continue - default: - return nil, err + switch se.ErrStatus.Reason { + case meta_v1.StatusReasonNotFound: + // Kind not supported by API server, skip + continue + case meta_v1.StatusReasonForbidden: + // K8s can return forbidden instead of not found for non super admins + c.logger.Log("warning", "not allowed to list resources", "err", err) + continue + default: + return nil, err } } else { return nil, err @@ -291,16 +294,16 @@ func (c *Cluster) Export() ([]byte, error) { podControllers, err := resourceKind.getPodControllers(c, ns.Name) if err != nil { if se, ok := err.(*apierrors.StatusError); ok { - switch (se.ErrStatus.Reason) { - case meta_v1.StatusReasonNotFound: - // Kind not supported by API server, skip - continue - case meta_v1.StatusReasonForbidden: - // K8s can return forbidden instead of not found for non super admins - c.logger.Log("warning", "not allowed to list resources", "err", err) - continue - default: - return nil, err + switch se.ErrStatus.Reason { + case meta_v1.StatusReasonNotFound: + // Kind not supported by API server, skip + continue + case meta_v1.StatusReasonForbidden: + // K8s can return forbidden instead of not found for non super admins + c.logger.Log("warning", "not allowed to list resources", "err", err) + continue + default: + return nil, err } } else { return nil, err diff --git a/cmd/fluxd/main.go b/cmd/fluxd/main.go index afceb47f0..a6e897912 100644 --- a/cmd/fluxd/main.go +++ b/cmd/fluxd/main.go @@ -105,6 +105,7 @@ func main() { registryBurst = fs.Int("registry-burst", defaultRemoteConnections, "maximum number of warmer connections to remote and memcache") registryTrace = fs.Bool("registry-trace", false, "output trace of image registry requests to log") registryInsecure = fs.StringSlice("registry-insecure-host", []string{}, "use HTTP for this image registry domain (e.g., registry.cluster.local), instead of HTTPS") + registryExcludeImage = fs.StringSlice("registry-exclude-image", []string{"k8s.gcr.io/*"}, "Do not scan images that match these glob expressions; the default is to exclude the 'k8s.gcr.io/*' images") // AWS authentication registryAWSRegions = fs.StringSlice("registry-ecr-region", nil, "Restrict ECR scanning to these AWS regions; if empty, only the cluster's region will be scanned") @@ -258,7 +259,7 @@ func main() { logger.Log("kubectl", kubectl) kubectlApplier := kubernetes.NewKubectl(kubectl, restClientConfig) - k8sInst := kubernetes.NewCluster(clientset, ifclientset, kubectlApplier, sshKeyRing, logger, *k8sNamespaceWhitelist) + k8sInst := kubernetes.NewCluster(clientset, ifclientset, kubectlApplier, sshKeyRing, logger, *k8sNamespaceWhitelist, *registryExcludeImage) if err := k8sInst.Ping(); err != nil { logger.Log("ping", err) From dafba854c2c6562a8c1ecfa8b80f7bd391994c91 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Mon, 14 Jan 2019 18:20:28 +0200 Subject: [PATCH 2/7] Fix kubernetes tests --- cluster/kubernetes/kubernetes_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cluster/kubernetes/kubernetes_test.go b/cluster/kubernetes/kubernetes_test.go index 6911301b3..e11538d17 100644 --- a/cluster/kubernetes/kubernetes_test.go +++ b/cluster/kubernetes/kubernetes_test.go @@ -26,7 +26,7 @@ func testGetAllowedNamespaces(t *testing.T, namespace []string, expected []strin clientset := fakekubernetes.NewSimpleClientset(newNamespace("default"), newNamespace("kube-system")) - c := NewCluster(clientset, nil, nil, nil, log.NewNopLogger(), namespace) + c := NewCluster(clientset, nil, nil, nil, log.NewNopLogger(), namespace, []string{}) namespaces, err := c.getAllowedNamespaces() if err != nil { From dd9b4ca30c88719f0f54804ffcb773f130be7938 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Mon, 14 Jan 2019 21:43:46 +0200 Subject: [PATCH 3/7] Set flags usage message to lowercase --- cmd/fluxd/main.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/cmd/fluxd/main.go b/cmd/fluxd/main.go index a6e897912..d7656993c 100644 --- a/cmd/fluxd/main.go +++ b/cmd/fluxd/main.go @@ -74,17 +74,17 @@ func main() { } // This mirrors how kubectl extracts information from the environment. var ( - listenAddr = fs.StringP("listen", "l", ":3030", "Listen address where /metrics and API will be served") - listenMetricsAddr = fs.String("listen-metrics", "", "Listen address for /metrics endpoint") - kubernetesKubectl = fs.String("kubernetes-kubectl", "", "Optional, explicit path to kubectl tool") - versionFlag = fs.Bool("version", false, "Get version number") + listenAddr = fs.StringP("listen", "l", ":3030", "listen address where /metrics and API will be served") + listenMetricsAddr = fs.String("listen-metrics", "", "listen address for /metrics endpoint") + kubernetesKubectl = fs.String("kubernetes-kubectl", "", "optional, explicit path to kubectl tool") + versionFlag = fs.Bool("version", false, "get version number") // Git repo & key etc. gitURL = fs.String("git-url", "", "URL of git repo with Kubernetes manifests; e.g., git@github.com:weaveworks/flux-get-started") gitBranch = fs.String("git-branch", "master", "branch of git repo to use for Kubernetes manifests") gitPath = fs.StringSlice("git-path", []string{}, "relative paths within the git repo to locate Kubernetes manifests") gitUser = fs.String("git-user", "Weave Flux", "username to use as git committer") gitEmail = fs.String("git-email", "support@weave.works", "email to use as git committer") - gitSetAuthor = fs.Bool("git-set-author", false, "If set, the author of git commits will reflect the user who initiated the commit and will differ from the git committer.") + gitSetAuthor = fs.Bool("git-set-author", false, "if set, the author of git commits will reflect the user who initiated the commit and will differ from the git committer.") gitLabel = fs.String("git-label", "", "label to keep track of sync progress; overrides both --git-sync-tag and --git-notes-ref") // Old git config; still used if --git-label is not supplied, but --git-label is preferred. gitSyncTag = fs.String("git-sync-tag", defaultGitSyncTag, "tag to use to mark sync progress for this cluster") @@ -97,33 +97,33 @@ func main() { // syncing syncInterval = fs.Duration("sync-interval", 5*time.Minute, "apply config in git to cluster at least this often, even if there are no new commits") // registry - memcachedHostname = fs.String("memcached-hostname", "memcached", "Hostname for memcached service.") - memcachedTimeout = fs.Duration("memcached-timeout", time.Second, "Maximum time to wait before giving up on memcached requests.") + memcachedHostname = fs.String("memcached-hostname", "memcached", "hostname for memcached service.") + memcachedTimeout = fs.Duration("memcached-timeout", time.Second, "maximum time to wait before giving up on memcached requests.") memcachedService = fs.String("memcached-service", "memcached", "SRV service used to discover memcache servers.") registryPollInterval = fs.Duration("registry-poll-interval", 5*time.Minute, "period at which to check for updated images") registryRPS = fs.Float64("registry-rps", 50, "maximum registry requests per second per host") registryBurst = fs.Int("registry-burst", defaultRemoteConnections, "maximum number of warmer connections to remote and memcache") registryTrace = fs.Bool("registry-trace", false, "output trace of image registry requests to log") registryInsecure = fs.StringSlice("registry-insecure-host", []string{}, "use HTTP for this image registry domain (e.g., registry.cluster.local), instead of HTTPS") - registryExcludeImage = fs.StringSlice("registry-exclude-image", []string{"k8s.gcr.io/*"}, "Do not scan images that match these glob expressions; the default is to exclude the 'k8s.gcr.io/*' images") + registryExcludeImage = fs.StringSlice("registry-exclude-image", []string{"k8s.gcr.io/*"}, "do not scan images that match these glob expressions; the default is to exclude the 'k8s.gcr.io/*' images") // AWS authentication - registryAWSRegions = fs.StringSlice("registry-ecr-region", nil, "Restrict ECR scanning to these AWS regions; if empty, only the cluster's region will be scanned") - registryAWSAccountIDs = fs.StringSlice("registry-ecr-include-id", nil, "Restrict ECR scanning to these AWS account IDs; if empty, all account IDs that aren't excluded may be scanned") - registryAWSBlockAccountIDs = fs.StringSlice("registry-ecr-exclude-id", []string{registry.EKS_SYSTEM_ACCOUNT}, "Do not scan ECR for images in these AWS account IDs; the default is to exclude the EKS system account") + registryAWSRegions = fs.StringSlice("registry-ecr-region", nil, "restrict ECR scanning to these AWS regions; if empty, only the cluster's region will be scanned") + registryAWSAccountIDs = fs.StringSlice("registry-ecr-include-id", nil, "restrict ECR scanning to these AWS account IDs; if empty, all account IDs that aren't excluded may be scanned") + registryAWSBlockAccountIDs = fs.StringSlice("registry-ecr-exclude-id", []string{registry.EKS_SYSTEM_ACCOUNT}, "do not scan ECR for images in these AWS account IDs; the default is to exclude the EKS system account") // k8s-secret backed ssh keyring configuration - k8sSecretName = fs.String("k8s-secret-name", "flux-git-deploy", "Name of the k8s secret used to store the private SSH key") - k8sSecretVolumeMountPath = fs.String("k8s-secret-volume-mount-path", "/etc/fluxd/ssh", "Mount location of the k8s secret storing the private SSH key") - k8sSecretDataKey = fs.String("k8s-secret-data-key", "identity", "Data key holding the private SSH key within the k8s secret") - k8sNamespaceWhitelist = fs.StringSlice("k8s-namespace-whitelist", []string{}, "Experimental, optional: restrict the view of the cluster to the namespaces listed. All namespaces are included if this is not set.") + k8sSecretName = fs.String("k8s-secret-name", "flux-git-deploy", "name of the k8s secret used to store the private SSH key") + k8sSecretVolumeMountPath = fs.String("k8s-secret-volume-mount-path", "/etc/fluxd/ssh", "mount location of the k8s secret storing the private SSH key") + k8sSecretDataKey = fs.String("k8s-secret-data-key", "identity", "data key holding the private SSH key within the k8s secret") + k8sNamespaceWhitelist = fs.StringSlice("k8s-namespace-whitelist", []string{}, "experimental, optional: restrict the view of the cluster to the namespaces listed. All namespaces are included if this is not set.") // SSH key generation sshKeyBits = optionalVar(fs, &ssh.KeyBitsValue{}, "ssh-keygen-bits", "-b argument to ssh-keygen (default unspecified)") sshKeyType = optionalVar(fs, &ssh.KeyTypeValue{}, "ssh-keygen-type", "-t argument to ssh-keygen (default unspecified)") sshKeygenDir = fs.String("ssh-keygen-dir", "", "directory, ideally on a tmpfs volume, in which to generate new SSH keys when necessary") - upstreamURL = fs.String("connect", "", "Connect to an upstream service e.g., Weave Cloud, at this base address") - token = fs.String("token", "", "Authentication token for upstream service") + upstreamURL = fs.String("connect", "", "connect to an upstream service e.g., Weave Cloud, at this base address") + token = fs.String("token", "", "authentication token for upstream service") dockerConfig = fs.String("docker-config", "", "path to a docker config to use for image registry credentials") From 241a53dc918d01bf9b2ea2f3da95a96657db89b6 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Tue, 15 Jan 2019 01:59:07 +0200 Subject: [PATCH 4/7] Filter images before merging the registry credentials - add tests for image filtering --- cluster/kubernetes/images.go | 80 ++++++++++++++++++++++--------- cluster/kubernetes/images_test.go | 43 ++++++++++++++++- 2 files changed, 99 insertions(+), 24 deletions(-) diff --git a/cluster/kubernetes/images.go b/cluster/kubernetes/images.go index ba5807dd2..58be01637 100644 --- a/cluster/kubernetes/images.go +++ b/cluster/kubernetes/images.go @@ -2,10 +2,10 @@ package kubernetes import ( "fmt" - "github.com/ryanuber/go-glob" "github.com/go-kit/kit/log" "github.com/pkg/errors" + "github.com/ryanuber/go-glob" apiv1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -15,7 +15,18 @@ import ( "github.com/weaveworks/flux/registry" ) -func mergeCredentials(log func(...interface{}) error, client extendedClient, namespace string, podTemplate apiv1.PodTemplateSpec, imageCreds registry.ImageCreds, seenCreds map[string]registry.Credentials) { +func mergeCredentials(log func(...interface{}) error, + filterImages func(podTemplate apiv1.PodTemplateSpec) []image.Name, + client extendedClient, + namespace string, podTemplate apiv1.PodTemplateSpec, + imageCreds registry.ImageCreds, + seenCreds map[string]registry.Credentials) { + // filter the images based on the exclusion list + images := filterImages(podTemplate) + if len(images) < 1 { + return + } + creds := registry.NoCredentials() var imagePullSecrets []string saName := podTemplate.Spec.ServiceAccountName @@ -82,21 +93,8 @@ func mergeCredentials(log func(...interface{}) error, client extendedClient, nam } // Now create the service and attach the credentials - for _, container := range podTemplate.Spec.Containers { - r, err := image.ParseRef(container.Image) - if err != nil { - log("err", err.Error()) - continue - } - imageCreds[r.Name] = creds - } - for _, container := range podTemplate.Spec.InitContainers { - r, err := image.ParseRef(container.Image) - if err != nil { - log("err", err.Error()) - continue - } - imageCreds[r.Name] = creds + for _, image := range images { + imageCreds[image] = creds } } @@ -126,7 +124,7 @@ func (c *Cluster) ImagesToFetch() registry.ImageCreds { imageCreds := make(registry.ImageCreds) for _, podController := range podControllers { logger := log.With(c.logger, "resource", flux.MakeResourceID(ns.Name, kind, podController.name)) - mergeCredentials(logger.Log, c.client, ns.Name, podController.podTemplate, imageCreds, seenCreds) + mergeCredentials(logger.Log, c.filterImages, c.client, ns.Name, podController.podTemplate, imageCreds, seenCreds) } // Merge creds @@ -141,16 +139,52 @@ func (c *Cluster) ImagesToFetch() registry.ImageCreds { } } - // remove images based on the glob exclusion list - for imageID := range allImageCreds { + return allImageCreds +} + +// filterImages returns an image list from a pod spec +// by removing those matching the exclusion list +func (c *Cluster) filterImages(podTemplate apiv1.PodTemplateSpec) []image.Name { + images := []image.Name{} + + for _, container := range podTemplate.Spec.InitContainers { + r, err := image.ParseRef(container.Image) + if err != nil { + c.logger.Log("err", err.Error()) + continue + } + images = append(images, r.Name) + } + + for _, container := range podTemplate.Spec.Containers { + r, err := image.ParseRef(container.Image) + if err != nil { + c.logger.Log("err", err.Error()) + continue + } + + images = append(images, r.Name) + } + + if len(c.imageExcludeList) < 1 { + return images + } + + result := []image.Name{} + for _, imageID := range images { imageName := imageID.CanonicalName().Name.String() + include := true for _, exp := range c.imageExcludeList { if glob.Glob(exp, imageName) { - //c.logger.Log("debug", fmt.Sprintf("image %s excluded from scanning by %s", imageName, exp)) - delete(allImageCreds, imageID) + include = false } } + if include { + result = append(result, imageID) + } else { + //c.logger.Log("debug", fmt.Sprintf("image %s excluded", imageName)) + } } - return allImageCreds + return result } diff --git a/cluster/kubernetes/images_test.go b/cluster/kubernetes/images_test.go index ecb486020..4b083a06b 100644 --- a/cluster/kubernetes/images_test.go +++ b/cluster/kubernetes/images_test.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "testing" + "github.com/go-kit/kit/log" "github.com/stretchr/testify/assert" apiv1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" @@ -66,7 +67,8 @@ func TestMergeCredentials(t *testing.T) { client := extendedClient{clientset, nil} creds := registry.ImageCreds{} - mergeCredentials(noopLog, client, ns, spec, creds, make(map[string]registry.Credentials)) + cluster := NewCluster(clientset, nil, nil, nil, log.NewNopLogger(), []string{}, []string{}) + mergeCredentials(noopLog, cluster.filterImages, client, ns, spec, creds, make(map[string]registry.Credentials)) // check that we accumulated some credentials assert.Contains(t, creds, ref.Name) @@ -74,3 +76,42 @@ func TestMergeCredentials(t *testing.T) { hosts := c.Hosts() assert.ElementsMatch(t, []string{"docker.io", "quay.io"}, hosts) } + +func TestMergeCredentials_ImageExclusion(t *testing.T) { + creds := registry.ImageCreds{} + gcrImage, _ := image.ParseRef("gcr.io/foo/bar:tag") + k8sImage, _ := image.ParseRef("k8s.gcr.io/foo/bar:tag") + testImage, _ := image.ParseRef("docker.io/test/bar:tag") + + spec := apiv1.PodTemplateSpec{ + Spec: apiv1.PodSpec{ + InitContainers: []apiv1.Container{ + {Name: "container1", Image: testImage.String()}, + }, + Containers: []apiv1.Container{ + {Name: "container1", Image: k8sImage.String()}, + {Name: "container2", Image: gcrImage.String()}, + }, + }, + } + + clientset := fake.NewSimpleClientset() + client := extendedClient{clientset, nil} + + // set exclusion list + cluster := NewCluster(clientset, nil, nil, nil, log.NewNopLogger(), []string{}, + []string{"k8s.gcr.io/*", "*test*"}) + + // filter images + mergeCredentials(noopLog, cluster.filterImages, client, "default", spec, creds, + make(map[string]registry.Credentials)) + + // check test image has been excluded + assert.NotContains(t, creds, testImage.Name) + + // check k8s.gcr.io image has been excluded + assert.NotContains(t, creds, k8sImage.Name) + + // check gcr.io image exists + assert.Contains(t, creds, gcrImage.Name) +} From cf5c23833dd057cec6f9d84b045eb6250dac4c81 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Tue, 15 Jan 2019 02:13:40 +0200 Subject: [PATCH 5/7] Add registry exclude image flag to docs --- site/daemon.md | 1 + 1 file changed, 1 insertion(+) diff --git a/site/daemon.md b/site/daemon.md index 4b236e65b..8133c058a 100644 --- a/site/daemon.md +++ b/site/daemon.md @@ -73,6 +73,7 @@ fluxd requires setup and offers customization though a multitude of flags. |--registry-rps | `200` | maximum registry requests per second per host| |--registry-burst | `125` | maximum number of warmer connections to remote and memcache| |--registry-insecure-host| [] | registry hosts to use HTTP for (instead of HTTPS) | +|--registry-exclude-image| `["k8s.gcr.io/*"]` | do not scan images that match these glob expressions | |--docker-config | `""` | path to a Docker config file with default image registry credentials | |--registry-ecr-region | `[]` | Allow these AWS regions when scanning images from ECR (multiple values alllowed); defaults to the detected cluster region | |--registry-ecr-include-id | `[]` | Include these AWS account ID(s) when scanning images in ECR (multiple values allowed); empty means allow all, unless excluded | From d078f92062951d57f5b901f15a779310e07b1ddf Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Wed, 23 Jan 2019 18:06:47 +0200 Subject: [PATCH 6/7] Refactor filterImages as per Michael's comments --- cluster/kubernetes/images.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cluster/kubernetes/images.go b/cluster/kubernetes/images.go index 58be01637..cb55a98e3 100644 --- a/cluster/kubernetes/images.go +++ b/cluster/kubernetes/images.go @@ -145,7 +145,7 @@ func (c *Cluster) ImagesToFetch() registry.ImageCreds { // filterImages returns an image list from a pod spec // by removing those matching the exclusion list func (c *Cluster) filterImages(podTemplate apiv1.PodTemplateSpec) []image.Name { - images := []image.Name{} + var images []image.Name for _, container := range podTemplate.Spec.InitContainers { r, err := image.ParseRef(container.Image) @@ -177,12 +177,11 @@ func (c *Cluster) filterImages(podTemplate apiv1.PodTemplateSpec) []image.Name { for _, exp := range c.imageExcludeList { if glob.Glob(exp, imageName) { include = false + break } } if include { result = append(result, imageID) - } else { - //c.logger.Log("debug", fmt.Sprintf("image %s excluded", imageName)) } } From f251ef957b14d08e7ea119a9c24582eb39d8a360 Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Thu, 24 Jan 2019 20:03:57 +0200 Subject: [PATCH 7/7] Make image filtering testable without a cluster instance --- cluster/kubernetes/images.go | 76 ++++++++++++------------------- cluster/kubernetes/images_test.go | 21 +++++---- 2 files changed, 43 insertions(+), 54 deletions(-) diff --git a/cluster/kubernetes/images.go b/cluster/kubernetes/images.go index cb55a98e3..059f9c6c4 100644 --- a/cluster/kubernetes/images.go +++ b/cluster/kubernetes/images.go @@ -16,13 +16,34 @@ import ( ) func mergeCredentials(log func(...interface{}) error, - filterImages func(podTemplate apiv1.PodTemplateSpec) []image.Name, + includeImage func(imageName string) bool, client extendedClient, namespace string, podTemplate apiv1.PodTemplateSpec, imageCreds registry.ImageCreds, seenCreds map[string]registry.Credentials) { - // filter the images based on the exclusion list - images := filterImages(podTemplate) + var images []image.Name + for _, container := range podTemplate.Spec.InitContainers { + r, err := image.ParseRef(container.Image) + if err != nil { + log("err", err.Error()) + continue + } + if includeImage(r.CanonicalName().Name.String()) { + images = append(images, r.Name) + } + } + + for _, container := range podTemplate.Spec.Containers { + r, err := image.ParseRef(container.Image) + if err != nil { + log("err", err.Error()) + continue + } + if includeImage(r.CanonicalName().Name.String()) { + images = append(images, r.Name) + } + } + if len(images) < 1 { return } @@ -124,7 +145,7 @@ func (c *Cluster) ImagesToFetch() registry.ImageCreds { imageCreds := make(registry.ImageCreds) for _, podController := range podControllers { logger := log.With(c.logger, "resource", flux.MakeResourceID(ns.Name, kind, podController.name)) - mergeCredentials(logger.Log, c.filterImages, c.client, ns.Name, podController.podTemplate, imageCreds, seenCreds) + mergeCredentials(logger.Log, c.includeImage, c.client, ns.Name, podController.podTemplate, imageCreds, seenCreds) } // Merge creds @@ -142,48 +163,11 @@ func (c *Cluster) ImagesToFetch() registry.ImageCreds { return allImageCreds } -// filterImages returns an image list from a pod spec -// by removing those matching the exclusion list -func (c *Cluster) filterImages(podTemplate apiv1.PodTemplateSpec) []image.Name { - var images []image.Name - - for _, container := range podTemplate.Spec.InitContainers { - r, err := image.ParseRef(container.Image) - if err != nil { - c.logger.Log("err", err.Error()) - continue - } - images = append(images, r.Name) - } - - for _, container := range podTemplate.Spec.Containers { - r, err := image.ParseRef(container.Image) - if err != nil { - c.logger.Log("err", err.Error()) - continue - } - - images = append(images, r.Name) - } - - if len(c.imageExcludeList) < 1 { - return images - } - - result := []image.Name{} - for _, imageID := range images { - imageName := imageID.CanonicalName().Name.String() - include := true - for _, exp := range c.imageExcludeList { - if glob.Glob(exp, imageName) { - include = false - break - } - } - if include { - result = append(result, imageID) +func (c *Cluster) includeImage(imageName string) bool { + for _, exp := range c.imageExcludeList { + if glob.Glob(exp, imageName) { + return false } } - - return result + return true } diff --git a/cluster/kubernetes/images_test.go b/cluster/kubernetes/images_test.go index 4b083a06b..ba63812d8 100644 --- a/cluster/kubernetes/images_test.go +++ b/cluster/kubernetes/images_test.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "testing" - "github.com/go-kit/kit/log" + "github.com/ryanuber/go-glob" "github.com/stretchr/testify/assert" apiv1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" @@ -67,8 +67,9 @@ func TestMergeCredentials(t *testing.T) { client := extendedClient{clientset, nil} creds := registry.ImageCreds{} - cluster := NewCluster(clientset, nil, nil, nil, log.NewNopLogger(), []string{}, []string{}) - mergeCredentials(noopLog, cluster.filterImages, client, ns, spec, creds, make(map[string]registry.Credentials)) + + mergeCredentials(noopLog, func(imageName string) bool { return true }, + client, ns, spec, creds, make(map[string]registry.Credentials)) // check that we accumulated some credentials assert.Contains(t, creds, ref.Name) @@ -98,12 +99,16 @@ func TestMergeCredentials_ImageExclusion(t *testing.T) { clientset := fake.NewSimpleClientset() client := extendedClient{clientset, nil} - // set exclusion list - cluster := NewCluster(clientset, nil, nil, nil, log.NewNopLogger(), []string{}, - []string{"k8s.gcr.io/*", "*test*"}) + var includeImage = func(imageName string) bool { + for _, exp := range []string{"k8s.gcr.io/*", "*test*"} { + if glob.Glob(exp, imageName) { + return false + } + } + return true + } - // filter images - mergeCredentials(noopLog, cluster.filterImages, client, "default", spec, creds, + mergeCredentials(noopLog, includeImage, client, "default", spec, creds, make(map[string]registry.Credentials)) // check test image has been excluded