Skip to content

Commit 182b41b

Browse files
committed
feat: inject bundle data into configmap
Signed-off-by: Erik Godding Boye <egboye@gmail.com>
1 parent fb6516a commit 182b41b

File tree

3 files changed

+296
-0
lines changed

3 files changed

+296
-0
lines changed

pkg/bundle/inject/controller.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
Copyright 2021 The cert-manager Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package inject
18+
19+
import (
20+
"context"
21+
"crypto/sha256"
22+
"fmt"
23+
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
"k8s.io/apimachinery/pkg/runtime"
26+
v1 "k8s.io/client-go/applyconfigurations/core/v1"
27+
ctrl "sigs.k8s.io/controller-runtime"
28+
"sigs.k8s.io/controller-runtime/pkg/builder"
29+
"sigs.k8s.io/controller-runtime/pkg/client"
30+
"sigs.k8s.io/controller-runtime/pkg/predicate"
31+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
32+
33+
"github.com/cert-manager/trust-manager/pkg/apis/trust/v1alpha1"
34+
"github.com/cert-manager/trust-manager/pkg/bundle/internal/ssa_client"
35+
)
36+
37+
const (
38+
BundleInjectLabelKey = "trust-manager.io/inject-bundle"
39+
40+
fieldManager = "trust-manager-injector"
41+
)
42+
43+
var configMap = &metav1.PartialObjectMetadata{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "ConfigMap"}}
44+
45+
type Injector struct {
46+
client.Client
47+
Scheme *runtime.Scheme
48+
}
49+
50+
func (i *Injector) SetupWithManager(mgr ctrl.Manager) error {
51+
return ctrl.NewControllerManagedBy(mgr).
52+
For(configMap,
53+
builder.WithPredicates(
54+
hasLabel(BundleInjectLabelKey),
55+
)).
56+
Complete(i)
57+
}
58+
59+
func (i *Injector) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
60+
data := map[string]string{"ca.crt": "bundle data"}
61+
dataHash := fmt.Sprintf("%x", sha256.Sum256([]byte("bundle data hash")))
62+
63+
applyConfig := v1.ConfigMap(request.Name, request.Namespace).
64+
WithAnnotations(map[string]string{v1alpha1.BundleHashAnnotationKey: dataHash}).
65+
WithData(data)
66+
67+
return reconcile.Result{}, patchConfigMap(ctx, i.Client, applyConfig)
68+
}
69+
70+
type Cleaner struct {
71+
client.Client
72+
Scheme *runtime.Scheme
73+
}
74+
75+
func (c *Cleaner) SetupWithManager(mgr ctrl.Manager) error {
76+
return ctrl.NewControllerManagedBy(mgr).
77+
For(configMap,
78+
builder.WithPredicates(
79+
hasAnnotation(v1alpha1.BundleHashAnnotationKey),
80+
predicate.Not(hasLabel(BundleInjectLabelKey)),
81+
)).
82+
Complete(c)
83+
}
84+
85+
func (c *Cleaner) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
86+
applyConfig := v1.ConfigMap(request.Name, request.Namespace)
87+
88+
return reconcile.Result{}, patchConfigMap(ctx, c.Client, applyConfig)
89+
}
90+
91+
func patchConfigMap(ctx context.Context, c client.Client, applyConfig *v1.ConfigMapApplyConfiguration) error {
92+
configMap, patch, err := ssa_client.GenerateConfigMapPatch(applyConfig)
93+
if err != nil {
94+
return err
95+
}
96+
97+
return c.Patch(ctx, configMap, patch, client.FieldOwner(fieldManager), client.ForceOwnership)
98+
}
99+
100+
func hasLabel(key string) predicate.Predicate {
101+
return predicate.NewPredicateFuncs(func(obj client.Object) bool {
102+
_, ok := obj.GetLabels()[key]
103+
return ok
104+
})
105+
}
106+
107+
func hasAnnotation(key string) predicate.Predicate {
108+
return predicate.NewPredicateFuncs(func(obj client.Object) bool {
109+
_, ok := obj.GetAnnotations()[key]
110+
return ok
111+
})
112+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2021 The cert-manager Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package inject
18+
19+
import (
20+
"context"
21+
22+
corev1 "k8s.io/api/core/v1"
23+
"sigs.k8s.io/controller-runtime/pkg/envtest/komega"
24+
25+
"github.com/cert-manager/trust-manager/pkg/bundle/inject"
26+
27+
. "github.com/onsi/ginkgo/v2"
28+
. "github.com/onsi/gomega"
29+
)
30+
31+
var _ = Describe("Injector", func() {
32+
var namespace string
33+
34+
BeforeEach(func() {
35+
ctx = context.Background()
36+
37+
ns := &corev1.Namespace{}
38+
ns.GenerateName = "inject-"
39+
Expect(k8sClient.Create(ctx, ns)).To(Succeed())
40+
namespace = ns.Name
41+
})
42+
43+
It("should inject bundle data when ConfigMap labeled", func() {
44+
cm := &corev1.ConfigMap{}
45+
cm.GenerateName = "cm-"
46+
cm.Namespace = namespace
47+
cm.Labels = map[string]string{
48+
inject.BundleInjectLabelKey: "foo-bundle",
49+
"app": "my-app",
50+
}
51+
cm.Data = map[string]string{
52+
"tls.crt": "bar",
53+
"tls.key": "baz",
54+
}
55+
Expect(k8sClient.Create(ctx, cm)).To(Succeed())
56+
57+
// Wait for ConfigMap to be processed by controller
58+
Eventually(komega.Object(cm)).Should(HaveField("Data", HaveKeyWithValue("ca.crt", "bundle data")))
59+
Expect(cm.Labels).To(HaveKeyWithValue("app", "my-app"))
60+
61+
By("removing label from ConfigMap, it should remove bundle data", func() {
62+
Expect(komega.Update(cm, func() {
63+
delete(cm.Labels, inject.BundleInjectLabelKey)
64+
})()).To(Succeed())
65+
66+
// Wait for ConfigMap to be processed by controller
67+
Eventually(komega.Object(cm)).Should(HaveField("Data", Not(HaveKey("ca.crt"))))
68+
Expect(cm.Labels).To(HaveKeyWithValue("app", "my-app"))
69+
Expect(cm.Data).To(Equal(map[string]string{
70+
"tls.crt": "bar",
71+
"tls.key": "baz",
72+
}))
73+
})
74+
})
75+
})
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
Copyright 2021 The cert-manager Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package inject
18+
19+
import (
20+
"context"
21+
"testing"
22+
23+
"k8s.io/client-go/kubernetes/scheme"
24+
"k8s.io/client-go/rest"
25+
ctrl "sigs.k8s.io/controller-runtime"
26+
"sigs.k8s.io/controller-runtime/pkg/client"
27+
"sigs.k8s.io/controller-runtime/pkg/envtest"
28+
"sigs.k8s.io/controller-runtime/pkg/envtest/komega"
29+
logf "sigs.k8s.io/controller-runtime/pkg/log"
30+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
31+
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
32+
33+
"github.com/cert-manager/trust-manager/pkg/bundle/inject"
34+
35+
. "github.com/onsi/ginkgo/v2"
36+
. "github.com/onsi/gomega"
37+
)
38+
39+
var (
40+
cfg *rest.Config
41+
k8sClient client.Client
42+
testEnv *envtest.Environment
43+
ctx context.Context
44+
cancel context.CancelFunc
45+
)
46+
47+
func TestAPIs(t *testing.T) {
48+
RegisterFailHandler(Fail)
49+
50+
RunSpecs(t, "Controller Suite")
51+
}
52+
53+
var _ = BeforeSuite(func() {
54+
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
55+
56+
ctx, cancel = context.WithCancel(context.TODO())
57+
58+
By("bootstrapping test environment")
59+
testEnv = &envtest.Environment{}
60+
61+
var err error
62+
// cfg is defined in this file globally.
63+
cfg, err = testEnv.Start()
64+
Expect(err).NotTo(HaveOccurred())
65+
Expect(cfg).NotTo(BeNil())
66+
67+
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
68+
Expect(err).NotTo(HaveOccurred())
69+
Expect(k8sClient).NotTo(BeNil())
70+
komega.SetClient(k8sClient)
71+
72+
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
73+
Client: client.Options{Cache: &client.CacheOptions{Unstructured: true}},
74+
Scheme: scheme.Scheme,
75+
Metrics: server.Options{
76+
// Disable metrics server to avoid port conflict
77+
BindAddress: "0",
78+
},
79+
})
80+
Expect(err).NotTo(HaveOccurred())
81+
82+
k8sScheme := k8sManager.GetScheme()
83+
84+
injector := &inject.Injector{
85+
Client: k8sManager.GetClient(),
86+
Scheme: k8sScheme,
87+
}
88+
Expect(injector.SetupWithManager(k8sManager)).To(Succeed())
89+
cleaner := &inject.Cleaner{
90+
Client: k8sManager.GetClient(),
91+
Scheme: k8sScheme,
92+
}
93+
Expect(cleaner.SetupWithManager(k8sManager)).To(Succeed())
94+
95+
go func() {
96+
defer GinkgoRecover()
97+
var ctrlCtx context.Context
98+
ctrlCtx, cancel = context.WithCancel(ctrl.SetupSignalHandler())
99+
Expect(k8sManager.Start(ctrlCtx)).To(Succeed())
100+
}()
101+
})
102+
103+
var _ = AfterSuite(func() {
104+
cancel()
105+
106+
By("tearing down the test environment")
107+
err := testEnv.Stop()
108+
Expect(err).NotTo(HaveOccurred())
109+
})

0 commit comments

Comments
 (0)