Skip to content
This repository has been archived by the owner on Jan 13, 2021. It is now read-only.

Commit

Permalink
Enqueue Bundle on ConfigMap/Secret event
Browse files Browse the repository at this point in the history
If there is a Deployment in a Bundle that uses ConfigMap/Secret and
this object is updated, Bundle is enqueued for processing.
  • Loading branch information
ash2k committed Jan 5, 2019
1 parent c027578 commit 9a54577
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 11 deletions.
10 changes: 7 additions & 3 deletions cmd/smith/app/bundle_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,9 @@ func (c *BundleControllerConstructor) New(config *ctrl.Config, cctx *ctrl.Contex
if err != nil {
return nil, err
}
crdGVK := apiext_v1b1.SchemeGroupVersion.WithKind("CustomResourceDefinition")
crdInf, err := apiExtensionsInformer(config, cctx, apiExtClient,
apiext_v1b1.SchemeGroupVersion.WithKind("CustomResourceDefinition"),
crdGVK,
apiext_v1b1inf.NewCustomResourceDefinitionInformer)
if err != nil {
return nil, err
Expand Down Expand Up @@ -146,7 +147,7 @@ func (c *BundleControllerConstructor) New(config *ctrl.Config, cctx *ctrl.Contex
if err != nil {
return nil, err
}
resourceInfs[apiext_v1b1.SchemeGroupVersion.WithKind("CustomResourceDefinition")] = crdInf
resourceInfs[crdGVK] = crdInf
resourceInfs[smith_v1.BundleGVK] = bundleInf
for gvk, inf := range resourceInfs {
if err = multiStore.AddInformer(gvk, inf); err != nil {
Expand Down Expand Up @@ -224,7 +225,10 @@ func (c *BundleControllerConstructor) New(config *ctrl.Config, cctx *ctrl.Contex
Broadcaster: broadcaster,
Recorder: recorder,
}
cntrlr.Prepare(crdInf, resourceInfs)
err = cntrlr.Prepare(crdInf, resourceInfs)
if err != nil {
return nil, err
}

return &ctrl.Constructed{
Interface: cntrlr,
Expand Down
1 change: 1 addition & 0 deletions pkg/controller/bundlec/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ go_library(
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
"//vendor/go.uber.org/zap:go_default_library",
"//vendor/k8s.io/api/apps/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
Expand Down
153 changes: 148 additions & 5 deletions pkg/controller/bundlec/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import (
"github.com/atlassian/smith/pkg/plugin"
"github.com/atlassian/smith/pkg/statuschecker"
"github.com/atlassian/smith/pkg/store"
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap"
apps_v1 "k8s.io/api/apps/v1"
core_v1 "k8s.io/api/core/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand All @@ -24,6 +27,14 @@ import (
"k8s.io/client-go/tools/record"
)

const (
byConfigMapNamespaceNameIndexName = "ByConfigMap"
bySecretNamespaceNameIndexName = "BySecret"
)

type byIndexFunc func(indexName, indexKey string) ([]interface{}, error)
type indexKeyFunc func(namespace, name string) string

type Controller struct {
// wg.Wait() is called from Run() and first wg.Add() may be called concurrently from CRD listener
// to start an Informer. This is a data race. This mutex is used to ensure ordering.
Expand Down Expand Up @@ -66,23 +77,50 @@ type Controller struct {
}

// Prepare prepares the controller to be run.
func (c *Controller) Prepare(crdInf cache.SharedIndexInformer, resourceInfs map[schema.GroupVersionKind]cache.SharedIndexInformer) {
func (c *Controller) Prepare(crdInf cache.SharedIndexInformer, resourceInfs map[schema.GroupVersionKind]cache.SharedIndexInformer) error {
c.crdContext, c.crdContextCancel = context.WithCancel(context.Background())
crdInf.AddEventHandler(&crdEventHandler{
controller: c,
watchers: make(map[string]watchState),
})

deploymentInf := resourceInfs[apps_v1.SchemeGroupVersion.WithKind("Deployment")]
err := deploymentInf.AddIndexers(cache.Indexers{
byConfigMapNamespaceNameIndexName: byConfigMapNamespaceNameIndex,
bySecretNamespaceNameIndexName: bySecretNamespaceNameIndex,
})
if err != nil {
return errors.WithStack(err)
}
deploymentByIndex := deploymentInf.GetIndexer().ByIndex
// ConfigMap -> Deployment -> Bundle event propagation
configMapGVK := core_v1.SchemeGroupVersion.WithKind("ConfigMap")
configMapInf := resourceInfs[configMapGVK]
configMapInf.AddEventHandler(&handlers.LookupHandler{
Logger: c.Logger,
WorkQueue: c.WorkQueue,
Gvk: configMapGVK,
Lookup: c.lookupBundleByDeploymentByIndex(deploymentByIndex, byConfigMapNamespaceNameIndexName, byConfigMapNamespaceNameIndexKey),
})
// Secret -> Deployment -> Bundle event propagation
secretGVK := core_v1.SchemeGroupVersion.WithKind("Secret")
secretInf := resourceInfs[secretGVK]
secretInf.AddEventHandler(&handlers.LookupHandler{
Logger: c.Logger,
WorkQueue: c.WorkQueue,
Gvk: secretGVK,
Lookup: c.lookupBundleByDeploymentByIndex(deploymentByIndex, bySecretNamespaceNameIndexName, bySecretNamespaceNameIndexKey),
})
// Standard handler
for gvk, resourceInf := range resourceInfs {
resourceHandler := &handlers.ControlledResourceHandler{
resourceInf.AddEventHandler(&handlers.ControlledResourceHandler{
Logger: c.Logger,
WorkQueue: c.WorkQueue,
ControllerIndex: &controllerIndexAdapter{bundleStore: c.BundleStore},
ControllerGvk: smith_v1.BundleGVK,
Gvk: gvk,
}
resourceInf.AddEventHandler(resourceHandler)
})
}
return nil
}

// Run begins watching and syncing.
Expand Down Expand Up @@ -110,6 +148,37 @@ func (c *Controller) Run(ctx context.Context) {
<-ctx.Done()
}

// lookupBundleByDeploymentByIndex returns a function that can be used to perform lookups of Bundles that contain
// Deployment objects that reference ConfigMap/Secret objects.
func (c *Controller) lookupBundleByDeploymentByIndex(byIndex byIndexFunc, indexName string, indexKey indexKeyFunc) func(runtime.Object) ([]runtime.Object, error) {
deploymentGK := schema.GroupKind{
Group: apps_v1.GroupName,
Kind: "Deployment",
}
return func(obj runtime.Object) ([]runtime.Object /*bundles*/, error) {
// obj is ConfigMap or Secret
objMeta := obj.(meta_v1.Object)
// find all Deployments that reference this obj
deploymentsFromIndex, err := byIndex(indexName, indexKey(objMeta.GetNamespace(), objMeta.GetName()))
if err != nil {
return nil, err
}
var bundles []runtime.Object
for _, deploymentInterface := range deploymentsFromIndex {
deployment := deploymentInterface.(*apps_v1.Deployment)
// find all Bundles that reference this Deployment
bundlesForDeployment, err := c.BundleStore.GetBundlesByObject(deploymentGK, deployment.Namespace, deployment.Name)
if err != nil {
return nil, err
}
for _, bundle := range bundlesForDeployment {
bundles = append(bundles, bundle)
}
}
return bundles, nil
}
}

type controllerIndexAdapter struct {
bundleStore BundleStore
}
Expand All @@ -125,3 +194,77 @@ func (c *controllerIndexAdapter) ControllerByObject(gk schema.GroupKind, namespa
}
return objs, nil
}

func byConfigMapNamespaceNameIndex(obj interface{}) ([]string, error) {
d := obj.(*apps_v1.Deployment)
index := configMapNamespaceNameIndexKeysForContainers(d.Namespace, d.Spec.Template.Spec.Containers)
index = append(index, configMapNamespaceNameIndexKeysForContainers(d.Namespace, d.Spec.Template.Spec.InitContainers)...)
return index, nil
}

func configMapNamespaceNameIndexKeysForContainers(namespace string, containers []core_v1.Container) []string {
var indexKeys []string
for _, container := range containers {
for _, envFrom := range container.EnvFrom {
configMapRef := envFrom.ConfigMapRef
if configMapRef == nil {
continue
}
indexKeys = append(indexKeys, bySecretNamespaceNameIndexKey(namespace, configMapRef.Name))
}
for _, env := range container.Env {
valueFrom := env.ValueFrom
if valueFrom == nil {
continue
}

configMapKeyRef := valueFrom.ConfigMapKeyRef
if configMapKeyRef == nil {
continue
}
indexKeys = append(indexKeys, bySecretNamespaceNameIndexKey(namespace, configMapKeyRef.Name))
}
}
return indexKeys
}

func byConfigMapNamespaceNameIndexKey(configMapNamespace, configMapName string) string {
return configMapNamespace + "/" + configMapName
}

func bySecretNamespaceNameIndex(obj interface{}) ([]string, error) {
d := obj.(*apps_v1.Deployment)
index := secretNamespaceNameIndexKeysForContainers(d.Namespace, d.Spec.Template.Spec.Containers)
index = append(index, secretNamespaceNameIndexKeysForContainers(d.Namespace, d.Spec.Template.Spec.InitContainers)...)
return index, nil
}

func secretNamespaceNameIndexKeysForContainers(namespace string, containers []core_v1.Container) []string {
var indexKeys []string
for _, container := range containers {
for _, envFrom := range container.EnvFrom {
secretRef := envFrom.SecretRef
if secretRef == nil {
continue
}
indexKeys = append(indexKeys, bySecretNamespaceNameIndexKey(namespace, secretRef.Name))
}
for _, env := range container.Env {
valueFrom := env.ValueFrom
if valueFrom == nil {
continue
}

secretKeyRef := valueFrom.SecretKeyRef
if secretKeyRef == nil {
continue
}
indexKeys = append(indexKeys, bySecretNamespaceNameIndexKey(namespace, secretKeyRef.Name))
}
}
return indexKeys
}

func bySecretNamespaceNameIndexKey(secretNamespace, secretName string) string {
return secretNamespace + "/" + secretName
}
7 changes: 4 additions & 3 deletions pkg/specchecker/builtin/process_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,19 +171,20 @@ func (deployment) generateHashForContainers(store specchecker.Store, namespace s
}
}
for _, env := range container.Env {
if env.ValueFrom == nil {
valueFrom := env.ValueFrom
if valueFrom == nil {
continue
}

secretKeyRef := env.ValueFrom.SecretKeyRef
secretKeyRef := valueFrom.SecretKeyRef
if secretKeyRef != nil {
err := specchecker.HashSecretRef(store, namespace, secretKeyRef.Name, sets.NewString(secretKeyRef.Key), secretKeyRef.Optional, hasher)
if err != nil {
return err
}
}

configMapKeyRef := env.ValueFrom.ConfigMapKeyRef
configMapKeyRef := valueFrom.ConfigMapKeyRef
if configMapKeyRef != nil {
err := specchecker.HashConfigMapRef(store, namespace, configMapKeyRef.Name, sets.NewString(configMapKeyRef.Key), configMapKeyRef.Optional, hasher)
if err != nil {
Expand Down

0 comments on commit 9a54577

Please sign in to comment.