Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include exclude namespaces #104

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
registry-creds
.vscode/
.vscode/
.history/
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
# MAINTAINER: Steve Sloka <[email protected]>
# If you update this image please bump the tag value before pushing.

TAG = 1.10
TAG = 1.11
PREFIX = upmcenterprises

BIN = registry-creds

GO111MODULE=off
GO_BINARY=go

# docker build arguments for internal proxy
ifneq ($(http_proxy),)
Expand All @@ -27,7 +28,7 @@ all: container

.PHONY: build
build: main.go
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -a -installsuffix cgo -o $(BIN) --ldflags '-w' $<
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 $(GO_BINARY) build -a -installsuffix cgo -o $(BIN) --ldflags '-w' $<

.PHONY: container
container: build
Expand All @@ -45,4 +46,4 @@ clean:

.PHONY: test
test: clean
go test -v $(go list ./... | grep -v vendor)
$(GO_BINARY) test -v $(go list ./... | grep -v vendor)
59 changes: 55 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ var (
argACRPassword = flags.String("acr-password", "", "Azure Container Registry password (client secret)")
argRefreshMinutes = flags.Int("refresh-mins", 60, `Default time to wait before refreshing (60 minutes)`)
argSkipKubeSystem = flags.Bool("skip-kube-system", true, `If true, will not attempt to set ImagePullSecrets on the kube-system namespace`)
argExcludeNamespaces = flags.String("exclude-namespaces", "", "Comma separated list of namespaces to avoid configuring ImagePullSecrets")
argIncludeNamespaces = flags.String("include-namespaces", "", "Comma separated list of namespaces to configure ImagePullSecrets. '--exclude-namespaces' is ignored when both are specified. Defaults to all namespaces (\"\")")
argAWSAssumeRole = flags.String("aws_assume_role", "", `If specified AWS will assume this role and use it to retrieve tokens`)
argTokenGenFxnRetryType = flags.String("token-retry-type", defaultTokenGenRetryType, `The type of retry timer to use when generating a secret token; either simple or exponential (simple)`)
argTokenGenFxnRetries = flags.Int("token-retries", defaultTokenGenRetries, `Default number of times to retry generating a secret token (3)`)
Expand All @@ -94,6 +96,10 @@ var (
var (
awsAccountIDs []string

// Namespaces inclusion and exclusion lists
excludeNamespaces []string
includeNamespaces []string

// RetryCfg represents the currently-configured number of retries + retry delay
RetryCfg RetryConfig

Expand Down Expand Up @@ -503,6 +509,29 @@ func validateParams() {
acrPassword := os.Getenv(acrPasswordKey)
gcrURLEnv := os.Getenv("gcrurl")

// initialize namespace inclusions and exclusions
if len(*argIncludeNamespaces) > 0 && len(*argExcludeNamespaces) > 0 {
logrus.Errorf("Ignoring 'exclude-namespaces', as 'include-namespaces' is provided.")
*argExcludeNamespaces = ""
excludeNamespaces = []string{}
}

if len(*argIncludeNamespaces) > 0 {
includeNamespaces = strings.Split(*argIncludeNamespaces, ",")
} else {
includeNamespaces = []string{}
}

if len(*argExcludeNamespaces) > 0 {
excludeNamespaces = strings.Split(*argExcludeNamespaces, ",")
} else {
excludeNamespaces = []string{}
}

if *argSkipKubeSystem {
excludeNamespaces = append(excludeNamespaces, "kube-system")
}

// initialize the retry configuration using command line values
RetryCfg = RetryConfig{
Type: *argTokenGenFxnRetryType,
Expand Down Expand Up @@ -607,14 +636,36 @@ func validateParams() {
}

func handler(c *controller, ns *v1.Namespace) error {
logrus.Infof("Handling namespace watch event for %s", ns.GetName())

// process exclusions first, if set
for _, e := range excludeNamespaces {
if ns.GetName() == e {
logrus.Infof("Namespace %s in exclusion list. Skipping.", ns.GetName())
return nil
}
}

// inclusion list is set
if len(includeNamespaces) > 0 {
ok := false
for _, i := range includeNamespaces {
if ns.GetName() == i {
ok = true
break
}
}

if !ok {
logrus.Infof("Namespace %s is not in the inclusion list. Skipping.", ns.GetName())
return nil
}
}

logrus.Infof("Refreshing credentials for namespace %s", ns.GetName())
secrets := c.generateSecrets()
logrus.Infof("Got %d refreshed credentials for namespace %s", len(secrets), ns.GetName())
for _, secret := range secrets {
if *argSkipKubeSystem && ns.GetName() == "kube-system" {
continue
}

logrus.Infof("Processing secret for namespace %s, secret %s", ns.Name, secret.Name)

if err := c.processNamespace(ns, secret); err != nil {
Expand Down
122 changes: 110 additions & 12 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,8 @@ func newFakeFailingACRClient() *fakeFailingACRClient {
}

func process(t *testing.T, c *controller) {
namespaces, _ := c.k8sutil.Kclient.Namespaces().List(v1.ListOptions{})
for _, ns := range namespaces.Items {
all_nss, _ := c.k8sutil.Kclient.Namespaces().List(v1.ListOptions{})
for _, ns := range all_nss.Items {
err := handler(c, &ns)
assert.Nil(t, err)
}
Expand Down Expand Up @@ -443,9 +443,13 @@ func assertAllSecretsPresent(t *testing.T, secrets []v1.LocalObjectReference) {
assertSecretPresent(t, secrets, *argACRSecretName)
}

func assertAllExpectedSecrets(t *testing.T, c *controller) {
func assertAllExpectedSecrets(t *testing.T, c *controller, namespaces ...string) {
if len(namespaces) == 0 {
namespaces = []string{"namespace1", "namespace2"}
}

// Test GCR
for _, ns := range []string{"namespace1", "namespace2"} {
for _, ns := range namespaces {
secret, err := c.k8sutil.GetSecret(ns, *argGCRSecretName)
assert.Nil(t, err)
assert.Equal(t, *argGCRSecretName, secret.Name)
Expand All @@ -459,7 +463,7 @@ func assertAllExpectedSecrets(t *testing.T, c *controller) {
assert.NotNil(t, err)

// Test AWS
for _, ns := range []string{"namespace1", "namespace2"} {
for _, ns := range namespaces {
secret, err := c.k8sutil.GetSecret(ns, *argAWSSecretName)
assert.Nil(t, err)
assert.Equal(t, *argAWSSecretName, secret.Name)
Expand All @@ -471,7 +475,7 @@ func assertAllExpectedSecrets(t *testing.T, c *controller) {
assert.NotNil(t, err)

// Test Azure Container Registry support
for _, ns := range []string{"namespace1", "namespace2"} {
for _, ns := range namespaces {
secret, err := c.k8sutil.GetSecret(ns, *argACRSecretName)
assert.Nil(t, err)
assert.Equal(t, *argACRSecretName, secret.Name)
Expand All @@ -483,17 +487,19 @@ func assertAllExpectedSecrets(t *testing.T, c *controller) {
assert.NotNil(t, err)

// Verify that all expected secrets have been created in all namespaces
serviceAccount, err := c.k8sutil.GetServiceAccount("namespace1", "default")
for _, ns := range namespaces {
serviceAccount, err := c.k8sutil.GetServiceAccount(ns, "default")
assert.Nil(t, err)
assertAllSecretsPresent(t, serviceAccount.ImagePullSecrets)
}
}

serviceAccount, err = c.k8sutil.GetServiceAccount("namespace2", "default")
assert.Nil(t, err)
assertAllSecretsPresent(t, serviceAccount.ImagePullSecrets)
func assertExpectedSecretNumber(t *testing.T, c *controller, n int, namespaces ...string) {
if len(namespaces) == 0 {
namespaces = []string{"namespace1", "namespace2"}
}

func assertExpectedSecretNumber(t *testing.T, c *controller, n int) {
for _, ns := range []string{"namespace1", "namespace2"} {
for _, ns := range namespaces {
serviceAccount, err := c.k8sutil.GetServiceAccount(ns, "default")
assert.Nil(t, err)
assert.Exactly(t, n, len(serviceAccount.ImagePullSecrets))
Expand All @@ -504,15 +510,105 @@ func TestProcessOnce(t *testing.T) {
*argGCRURL = "fakeEndpoint"
awsAccountIDs = []string{""}
c := newFakeController()
validateParams()

process(t, c)

assertAllExpectedSecrets(t, c)

// Verify that secrets have not been created twice
assertExpectedSecretNumber(t, c, 4)
}

func TestProcessOnceIncludeNamespacesBaseCase(t *testing.T) {
*argGCRURL = "fakeEndpoint"
*argIncludeNamespaces = ""
awsAccountIDs = []string{""}
c := newFakeController()
validateParams()

process(t, c)

assertAllExpectedSecrets(t, c)

// Verify that secrets have not been created twice
assertExpectedSecretNumber(t, c, 4)
}

func TestProcessOnceIncludeAllIndividualNamespaces(t *testing.T) {
*argGCRURL = "fakeEndpoint"
*argIncludeNamespaces = "namespace1,namespace2"
awsAccountIDs = []string{""}
c := newFakeController()
validateParams()

process(t, c)

assertAllExpectedSecrets(t, c)

// Verify that secrets have not been created twice
assertExpectedSecretNumber(t, c, 4)
}

func TestProcessOnceIncludeOneNamespace(t *testing.T) {
*argGCRURL = "fakeEndpoint"
*argIncludeNamespaces = "namespace1"
awsAccountIDs = []string{""}
c := newFakeController()
validateParams()

process(t, c)

assertAllExpectedSecrets(t, c, "namespace1")

// Verify that secrets have not been created twice
assertExpectedSecretNumber(t, c, 4, "namespace1")
// Verify that kube-system namespace is excluded
assertExpectedSecretNumber(t, c, 0, "kube-system")
}

func TestProcessWithBothInclusionAndExclusionListsForNamespaces(t *testing.T) {
*argGCRURL = "fakeEndpoint"
*argIncludeNamespaces = "namespace1,namespace2"
*argExcludeNamespaces = "namespace1"
awsAccountIDs = []string{""}
c := newFakeController()
validateParams()

process(t, c)

assertAllExpectedSecrets(t, c)

// Verify that secrets have not been created twice
assertExpectedSecretNumber(t, c, 4)
}

func TestProcessWithOnlyExclusionListsForNamespaces(t *testing.T) {
*argGCRURL = "fakeEndpoint"
*argIncludeNamespaces = "" // reset to all namespaces
*argExcludeNamespaces = "namespace1"
awsAccountIDs = []string{""}
c := newFakeController()
validateParams()

process(t, c)

assertAllExpectedSecrets(t, c, "namespace2")

// Verify that secrets have not been created twice
assertExpectedSecretNumber(t, c, 4, "namespace2")
// Verify no secrets exist for excluded namespace
assertExpectedSecretNumber(t, c, 0, "namespace1")
// Verify that kube-system namespace is excluded
assertExpectedSecretNumber(t, c, 0, "kube-system")
}

func TestProcessTwice(t *testing.T) {
*argIncludeNamespaces = "" // all namespaces
*argExcludeNamespaces = "" // exclude none
*argGCRURL = "fakeEndpoint"
c := newFakeController()
validateParams()

process(t, c)
// test processing twice for idempotency
Expand All @@ -527,6 +623,7 @@ func TestProcessTwice(t *testing.T) {
func TestProcessWithExistingSecrets(t *testing.T) {
*argGCRURL = "fakeEndpoint"
c := newFakeController()
validateParams()

secretGCR := &v1.Secret{
ObjectMeta: v1.ObjectMeta{
Expand Down Expand Up @@ -599,6 +696,7 @@ func TestProcessWithExistingSecrets(t *testing.T) {

func TestProcessWithExistingImagePullSecrets(t *testing.T) {
c := newFakeController()
validateParams()

for _, ns := range []string{"namespace1", "namespace2"} {
serviceAccount, err := c.k8sutil.GetServiceAccount(ns, "default")
Expand Down