Skip to content

Commit

Permalink
allow reference to existing secret as imagePullSecret (#7657)
Browse files Browse the repository at this point in the history
* allow reference to existing secret as imagePullSecret

allows reference to existing secrets for imagePullSecrets without
passing the secret itself. this enables management of secrets by an external
system like sealedsecrets and prevents the secret data from being stored in helm.

it works by allowing use of the installation's imagePullSecret field
directly instead of the toplevel imagePullSecrets field
  • Loading branch information
ozdanborne authored May 25, 2023
1 parent 8372da5 commit d4029e0
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 31 deletions.
29 changes: 29 additions & 0 deletions charts/test/helm_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package charttest

import (
"os/exec"
"testing"

"github.com/onsi/ginkgo/reporters"
"github.com/projectcalico/calico/libcalico-go/lib/testutils"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func init() {
testutils.HookLogrusForGinkgo()
}

func TestHelm(t *testing.T) {
// testutils.HookLogrusForGinkgo()
RegisterFailHandler(Fail)
junitReporter := reporters.NewJUnitReporter("../../report/helm_suite.xml")

_, err := exec.LookPath("helm")
if err != nil {
t.Skip("skipping exec tests since 'helm' is not installed")
}

RunSpecsWithDefaultAndCustomReporters(t, "Helm Suite", []Reporter{junitReporter})
}
116 changes: 116 additions & 0 deletions charts/test/tigera_operator_chart_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Package charttest uses 'helm template' to render the helm package with various input values,
// unmarshals the resulting yaml into kubernetes resource types, and then tests that the correct fields
// are set accordingly.
package charttest

import (
"path/filepath"
"testing"

corev1 "k8s.io/api/core/v1"

"github.com/gruntwork-io/terratest/modules/helm"

. "github.com/onsi/gomega"
)

func TestTigeraOperatorHelmChart(t *testing.T) {
t.Run("image pull secrets", func(t *testing.T) {
t.Run("using toplevel config field", func(t *testing.T) {
opts := &helm.Options{
SetValues: map[string]string{
"imagePullSecrets.my-secret": "secret1",
},
}

t.Run("sets imagePullSecrets on serviceaccount", func(t *testing.T) {
g := NewWithT(t)
var serviceAccount corev1.ServiceAccount
err := renderChartResource(t, opts, "templates/tigera-operator/02-serviceaccount-tigera-operator.yaml", &serviceAccount)
g.Expect(err).To(HaveOccurred())
g.Expect(serviceAccount.ImagePullSecrets).To(ConsistOf(
corev1.LocalObjectReference{Name: "my-secret"},
))
})

t.Run("creates a secret", func(t *testing.T) {
g := NewWithT(t)
var secret corev1.Secret
err := renderChartResource(t, opts, "templates/tigera-operator/01-imagepullsecret.yaml", &secret)
g.Expect(err).To(HaveOccurred())
g.Expect(secret.Name).To(Equal("my-secret"))
g.Expect(secret.Data).To(Equal(map[string][]byte{
".dockerconfigjson": []byte("secret1"),
}))
})
})

t.Run("using installation's config field", func(t *testing.T) {
opts := &helm.Options{
SetValues: map[string]string{
"installation.imagePullSecrets[0].name": "my-secret",
},
}

t.Run("sets imagePullSecrets on serviceaccount", func(t *testing.T) {
g := NewWithT(t)
var serviceAccount corev1.ServiceAccount
err := renderChartResource(t, opts, "templates/tigera-operator/02-serviceaccount-tigera-operator.yaml", &serviceAccount)
g.Expect(err).To(HaveOccurred())
g.Expect(serviceAccount.ImagePullSecrets).To(ConsistOf(
corev1.LocalObjectReference{Name: "my-secret"},
))
})

t.Run("does not create a secret", func(t *testing.T) {
g := NewWithT(t)
// assert an error occured. no other way to assert "file was not rendered"
err := renderChartResource(t, opts, "templates/tigera-operator/01-imagepullsecret.yaml", &corev1.Secret{})
g.Expect(err).To(HaveOccurred())
})
})

t.Run("using both toplevel and installation fields", func(t *testing.T) {
opts := &helm.Options{
SetValues: map[string]string{
"imagePullSecrets.secret-1": "secret1",
"installation.imagePullSecrets[0].name": "secret-2",
},
}

t.Run("sets both imagePullSecrets on serviceaccount", func(t *testing.T) {
g := NewWithT(t)
var serviceAccount corev1.ServiceAccount
err := renderChartResource(t, opts, "templates/tigera-operator/02-serviceaccount-tigera-operator.yaml", &serviceAccount)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(serviceAccount.ImagePullSecrets).To(ConsistOf(
corev1.LocalObjectReference{Name: "secret-1"},
corev1.LocalObjectReference{Name: "secret-2"},
))
})

t.Run("only creates a secret for the toplevel secret", func(t *testing.T) {
g := NewWithT(t)
var secret corev1.Secret
err := renderChartResource(t, opts, "templates/tigera-operator/01-imagepullsecret.yaml", &secret)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(secret.Name).To(Equal("secret-1"))
g.Expect(secret.Data).To(Equal(map[string][]byte{
".dockerconfigjson": []byte("secret1"),
}))
})
})
})
}

func renderChartResource(t *testing.T, options *helm.Options, templatePath string, into any) error {
helmChartPath, err := filepath.Abs("../tigera-operator")
Expect(err).ToNot(HaveOccurred())

output, err := helm.RenderTemplateE(t, options, helmChartPath, "tigera-operator", []string{templatePath})
if err != nil {
return err
}
helm.UnmarshalK8SYaml(t, output, &into)
return nil
}
15 changes: 12 additions & 3 deletions charts/tigera-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,11 @@ ownership of the helm resources to the new chart location.
The default values.yaml should be suitable for most basic deployments.

```
# Image pull secrets to provision for pulling images from private registries.
# This field is a map of desired Secret name to .dockerconfigjson formatted data to use for the secret.
# Populates the `imagePullSecrets` property for all Pods controlled by the `Installation` resource.
# imagePullSecrets is a special helm field which, when specified, creates a secret
# containing the pull secret which is used to pull all images deployed by this helm chart and the resulting operator.
# this field is a map where the key is the desired secret name and the value is the contents of the imagePullSecret.
#
# Example: --set-file imagePullSecrets.gcr=./pull-secret.json
imagePullSecrets: {}

# Configures general installation parameters for Calico. Schema is based
Expand All @@ -99,6 +101,13 @@ installation:
enabled: true
kubernetesProvider: ""

# imagePullSecrets are configured on all images deployed by the tigera-operator.
# secrets specified here must exist in the tigera-operator namespace; they won't be created by the operator or helm.
# imagePullSecrets are a slice of LocalObjectReferences, which is the same format they appear as on deployments.
#
# Example: --set installation.imagePullSecrets[0].name=my-existing-secret
imagePullSecrets: []

# Configures general installation parameters for Calico. Schema is based
# on the operator.tigera.io/Installation API documented
# here: https://projectcalico.docs.tigera.io/reference/installation/api#operator.tigera.io/v1.APIServerSpec
Expand Down
13 changes: 13 additions & 0 deletions charts/tigera-operator/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,16 @@
{{- end -}}
{{- .image -}}:{{- .version -}}
{{- end -}}

{{/*
generate imagePullSecrets for installation and deployments
by combining installation.imagePullSecrets with toplevel imagePullSecrets.
*/}}

{{- define "tigera-operator.imagePullSecrets" -}}
{{- $secrets := default list .Values.installation.imagePullSecrets -}}
{{- range $key, $val := .Values.imagePullSecrets -}}
{{- $secrets = append $secrets (dict "name" $key) -}}
{{- end -}}
{{ $secrets | toYaml }}
{{- end -}}
7 changes: 1 addition & 6 deletions charts/tigera-operator/templates/crs/custom-resources.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
{{ if .Values.installation.enabled }}
{{ $installSpec := omit .Values.installation "enabled" }}
{{ $secrets := list }}
{{ range $name := keys .Values.imagePullSecrets -}}
{{ $item := dict "name" $name }}
{{ $secrets = append $secrets $item }}
{{ end }}
{{ $_ := set $installSpec "imagePullSecrets" $secrets }}
{{ $_ := set $installSpec "imagePullSecrets" (include "tigera-operator.imagePullSecrets" . | fromYamlArray) }}
{{ $_ := set $installSpec "kubeletVolumePluginPath" .Values.kubeletVolumePluginPath }}

apiVersion: operator.tigera.io/v1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
{{ $secrets := list }}
{{ range $name := keys .Values.imagePullSecrets -}}
{{ $item := dict "name" $name }}
{{ $secrets = append $secrets $item }}
{{ end }}

apiVersion: v1
kind: ServiceAccount
metadata:
name: tigera-operator
namespace: {{.Release.Namespace}}
imagePullSecrets: {{- $secrets | toYaml | nindent 2 }}
imagePullSecrets: {{- include "tigera-operator.imagePullSecrets" . | nindent 2 }}
11 changes: 11 additions & 0 deletions charts/tigera-operator/values.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
# imagePullSecrets is a special helm field which, when specified, creates a secret
# containing the pull secret which is used to pull all images deployed by this helm chart and the resulting operator.
# this field is a map where the key is the desired secret name and the value is the contents of the imagePullSecret.
#
# Example: --set-file imagePullSecrets.gcr=./pull-secret.json
imagePullSecrets: {}

installation:
enabled: true
kubernetesProvider: ""
# imagePullSecrets are configured on all images deployed by the tigera-operator.
# secrets specified here must exist in the tigera-operator namespace; they won't be created by the operator or helm.
# imagePullSecrets are a slice of LocalObjectReferences, which is the same format they appear as on deployments.
#
# Example: --set installation.imagePullSecrets[0].name=my-existing-secret
imagePullSecrets: []

apiServer:
enabled: true
Expand Down
20 changes: 16 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ require (
github.com/google/netstack v0.0.0-20191123085552-55fcc16cd0eb
github.com/google/safetext v0.0.0-20230106111101-7156a760e523
github.com/google/uuid v1.3.0
github.com/gruntwork-io/terratest v0.41.24
github.com/ishidawataru/sctp v0.0.0-20191218070446-00ab2ac2db07
github.com/joho/godotenv v1.4.0
github.com/json-iterator/go v1.1.12
Expand Down Expand Up @@ -110,7 +111,7 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/mocks v0.4.2 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.1.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/GoogleCloudPlatform/k8s-cloud-provider v1.18.1-0.20220218231025-f11817397a1b // indirect
Expand All @@ -122,7 +123,7 @@ require (
github.com/alexflint/go-filemutex v1.1.0 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e // indirect
github.com/aws/aws-sdk-go v1.44.116 // indirect
github.com/aws/aws-sdk-go v1.44.122 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.6.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.0.0 // indirect
Expand All @@ -132,6 +133,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.9.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/checkpoint-restore/go-criu/v5 v5.3.0 // indirect
Expand All @@ -142,6 +144,7 @@ require (
github.com/containerd/ttrpc v1.1.0 // indirect
github.com/coreos/go-iptables v0.6.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/docker/distribution v2.8.1+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
Expand All @@ -151,6 +154,7 @@ require (
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.2.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.4 // indirect
Expand All @@ -159,6 +163,7 @@ require (
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-playground/locales v0.12.1 // indirect
github.com/go-playground/universal-translator v0.0.0-20170327191703-71201497bace // indirect
github.com/go-sql-driver/mysql v1.4.1 // indirect
github.com/godbus/dbus/v5 v5.0.6 // indirect
github.com/gofrs/uuid v4.0.0+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
Expand All @@ -173,8 +178,11 @@ require (
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/gruntwork-io/go-commons v0.8.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.8 // indirect
github.com/imdario/mergo v0.3.11 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand All @@ -187,6 +195,7 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/mdlayher/genetlink v1.0.0 // indirect
github.com/mdlayher/netlink v1.1.0 // indirect
Expand All @@ -210,8 +219,10 @@ require (
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/otp v1.2.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1 // indirect
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 // indirect
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
Expand All @@ -223,6 +234,7 @@ require (
github.com/stretchr/testify v1.8.1 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
github.com/urfave/cli v1.22.2 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
github.com/vmware/govmomi v0.20.3 // indirect
go.opencensus.io v0.24.0 // indirect
Expand All @@ -242,7 +254,7 @@ require (
go.uber.org/zap v1.21.0 // indirect
golang.org/x/crypto v0.1.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
golang.org/x/oauth2 v0.1.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/time v0.1.0 // indirect
golang.org/x/tools v0.6.0 // indirect
Expand Down
Loading

0 comments on commit d4029e0

Please sign in to comment.