Skip to content

Commit 3f9b886

Browse files
authored
✨ Add a configmap to handle the proxy ca bundle (open-cluster-management-io#772)
* Add a configmap to handle the proxy ca bundle Signed-off-by: zhujian <[email protected]> * Use contextual logger Signed-off-by: zhujian <[email protected]> --------- Signed-off-by: zhujian <[email protected]>
1 parent 6d3184a commit 3f9b886

File tree

5 files changed

+400
-24
lines changed

5 files changed

+400
-24
lines changed

pkg/addon/templateagent/decorator.go

+158-14
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package templateagent
22

33
import (
44
"fmt"
5+
"path"
56
"strings"
67

78
corev1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
810
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
911
"k8s.io/apimachinery/pkg/runtime"
12+
"k8s.io/klog/v2"
1013

1114
"open-cluster-management.io/addon-framework/pkg/addonfactory"
1215
"open-cluster-management.io/addon-framework/pkg/utils"
@@ -84,22 +87,25 @@ func setUnstructuredNestedField(obj interface{}, val string, paths []string) err
8487
}
8588

8689
type deploymentDecorator struct {
90+
logger klog.Logger
8791
decorators []podTemplateSpecDecorator
8892
}
8993

9094
func newDeploymentDecorator(
95+
logger klog.Logger,
9196
addonName string,
9297
template *addonapiv1alpha1.AddOnTemplate,
9398
orderedValues orderedValues,
9499
privateValues addonfactory.Values,
95100
) decorator {
96101
return &deploymentDecorator{
102+
logger: logger,
97103
decorators: []podTemplateSpecDecorator{
98104
newEnvironmentDecorator(orderedValues),
99105
newVolumeDecorator(addonName, template),
100106
newNodePlacementDecorator(privateValues),
101107
newImageDecorator(privateValues),
102-
newProxyDecorator(privateValues),
108+
newProxyHandler(logger, addonName, privateValues),
103109
},
104110
}
105111
}
@@ -127,22 +133,25 @@ func (d *deploymentDecorator) decorate(obj *unstructured.Unstructured) (*unstruc
127133
}
128134

129135
type daemonSetDecorator struct {
136+
logger klog.Logger
130137
decorators []podTemplateSpecDecorator
131138
}
132139

133140
func newDaemonSetDecorator(
141+
logger klog.Logger,
134142
addonName string,
135143
template *addonapiv1alpha1.AddOnTemplate,
136144
orderedValues orderedValues,
137145
privateValues addonfactory.Values,
138146
) decorator {
139147
return &daemonSetDecorator{
148+
logger: logger,
140149
decorators: []podTemplateSpecDecorator{
141150
newEnvironmentDecorator(orderedValues),
142151
newVolumeDecorator(addonName, template),
143152
newNodePlacementDecorator(privateValues),
144153
newImageDecorator(privateValues),
145-
newProxyDecorator(privateValues),
154+
newProxyHandler(logger, addonName, privateValues),
146155
},
147156
}
148157
}
@@ -331,27 +340,40 @@ func (d *imageDecorator) decorate(pod *corev1.PodTemplateSpec) error {
331340
return nil
332341
}
333342

334-
type proxyDecorator struct {
343+
// objectsInjector injects additional runtime objects to the manifests, these objects will be created
344+
// in the managed clusters
345+
type objectsInjector interface {
346+
// inject returns a list of runtime objects to be created in the managed cluster
347+
inject() ([]runtime.Object, error)
348+
}
349+
350+
// podTemplateHandler is a combination of podTemplateSpecDecorator and objectsInjector, it can decorate
351+
// the pod in the deployments/daemonsets and inject additional runtime objects into the manifests
352+
type podTemplateHandler interface {
353+
podTemplateSpecDecorator
354+
objectsInjector
355+
}
356+
357+
type proxyHandler struct {
358+
logger klog.Logger
359+
addonName string
335360
privateValues addonfactory.Values
336361
}
337362

338-
func newProxyDecorator(privateValues addonfactory.Values) podTemplateSpecDecorator {
339-
return &proxyDecorator{
363+
func newProxyHandler(logger klog.Logger, addonName string, privateValues addonfactory.Values) podTemplateHandler {
364+
return &proxyHandler{
365+
logger: logger,
366+
addonName: addonName,
340367
privateValues: privateValues,
341368
}
342369
}
343370

344-
func (d *proxyDecorator) decorate(pod *corev1.PodTemplateSpec) error {
345-
proxyConfig, ok := d.privateValues[ProxyPrivateValueKey]
371+
func (d *proxyHandler) decorate(pod *corev1.PodTemplateSpec) error {
372+
pc, ok := d.getProxyConfig()
346373
if !ok {
347374
return nil
348375
}
349376

350-
pc, ok := proxyConfig.(addonapiv1alpha1.ProxyConfig)
351-
if !ok {
352-
return fmt.Errorf("proxy config value is invalid")
353-
}
354-
355377
keyValues := []keyValuePair{}
356378
if len(pc.HTTPProxy) > 0 {
357379
keyValues = append(keyValues,
@@ -376,8 +398,114 @@ func (d *proxyDecorator) decorate(pod *corev1.PodTemplateSpec) error {
376398
return nil
377399
}
378400

379-
return newEnvironmentDecorator(keyValues).decorate(pod)
380-
// TODO: consider to create a configmap to store the proxyConfig.CABundle and mount it to the Deployment/DaemonSet
401+
err := newEnvironmentDecorator(keyValues).decorate(pod)
402+
if err != nil {
403+
return err
404+
}
405+
406+
if len(pc.CABundle) == 0 {
407+
return nil
408+
}
409+
410+
return newCABundleDecorator(d.addonName, pc.CABundle).decorate(pod)
411+
}
412+
413+
func (d *proxyHandler) getProxyConfig() (addonapiv1alpha1.ProxyConfig, bool) {
414+
proxyConfig, ok := d.privateValues[ProxyPrivateValueKey]
415+
if !ok {
416+
return addonapiv1alpha1.ProxyConfig{}, false
417+
}
418+
419+
pc, ok := proxyConfig.(addonapiv1alpha1.ProxyConfig)
420+
if !ok {
421+
d.logger.Error(nil, "proxy config value is invalid", "value", proxyConfig)
422+
return addonapiv1alpha1.ProxyConfig{}, false
423+
}
424+
425+
return pc, true
426+
}
427+
428+
func (d *proxyHandler) inject() ([]runtime.Object, error) {
429+
pc, ok := d.getProxyConfig()
430+
if !ok {
431+
return nil, nil
432+
}
433+
434+
if len(pc.CABundle) == 0 {
435+
return nil, nil
436+
}
437+
438+
return []runtime.Object{
439+
&corev1.ConfigMap{
440+
// add TypeMeta to prevent error:
441+
// "failed to generate required mapper.err got empty kind/version from object"
442+
TypeMeta: metav1.TypeMeta{
443+
Kind: "ConfigMap",
444+
APIVersion: "v1",
445+
},
446+
ObjectMeta: metav1.ObjectMeta{
447+
Name: proxyCABundleConfigMapName(d.addonName),
448+
// use the default namespace, will be decorated by the namespaceDecorator
449+
Namespace: "open-cluster-management-agent-addon",
450+
},
451+
Data: map[string]string{
452+
proxyCABundleConfigMapDataKey(): string(pc.CABundle),
453+
},
454+
},
455+
}, nil
456+
}
457+
458+
type caBundleDecorator struct {
459+
addonName string
460+
caBundle []byte
461+
envDecorator podTemplateSpecDecorator
462+
}
463+
464+
func newCABundleDecorator(addonName string, caBundle []byte) podTemplateSpecDecorator {
465+
keyValues := []keyValuePair{}
466+
keyValues = append(keyValues,
467+
keyValuePair{name: "CA_BUNDLE_FILE_PATH", value: proxyCABundleFilePath()},
468+
)
469+
470+
return &caBundleDecorator{
471+
addonName: addonName,
472+
caBundle: caBundle,
473+
envDecorator: newEnvironmentDecorator(keyValues),
474+
}
475+
}
476+
477+
func (d *caBundleDecorator) decorate(pod *corev1.PodTemplateSpec) error {
478+
err := d.envDecorator.decorate(pod)
479+
if err != nil {
480+
return err
481+
}
482+
483+
volumeMounts := []corev1.VolumeMount{
484+
{
485+
Name: "proxy-ca-bundle",
486+
MountPath: proxyCABundleConfigMapMountPath(),
487+
},
488+
}
489+
volumes := []corev1.Volume{
490+
{
491+
Name: "proxy-ca-bundle",
492+
VolumeSource: corev1.VolumeSource{
493+
ConfigMap: &corev1.ConfigMapVolumeSource{
494+
LocalObjectReference: corev1.LocalObjectReference{
495+
Name: proxyCABundleConfigMapName(d.addonName),
496+
},
497+
},
498+
},
499+
},
500+
}
501+
502+
for j := range pod.Spec.Containers {
503+
pod.Spec.Containers[j].VolumeMounts = append(
504+
pod.Spec.Containers[j].VolumeMounts, volumeMounts...)
505+
}
506+
507+
pod.Spec.Volumes = append(pod.Spec.Volumes, volumes...)
508+
return nil
381509
}
382510

383511
func hubKubeconfigSecretMountPath() string {
@@ -395,3 +523,19 @@ func CustomSignedSecretName(addonName, signerName string) string {
395523
func customSignedSecretMountPath(signerName string) string {
396524
return fmt.Sprintf("/managed/%s", strings.ReplaceAll(signerName, "/", "-"))
397525
}
526+
527+
func proxyCABundleConfigMapMountPath() string {
528+
return "/managed/proxy-ca"
529+
}
530+
531+
func proxyCABundleConfigMapName(addonName string) string {
532+
return fmt.Sprintf("%s-proxy-ca", addonName)
533+
}
534+
535+
func proxyCABundleConfigMapDataKey() string {
536+
return "ca-bundle.crt"
537+
}
538+
539+
func proxyCABundleFilePath() string {
540+
return path.Join(proxyCABundleConfigMapMountPath(), proxyCABundleConfigMapDataKey())
541+
}

pkg/addon/templateagent/decorator_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package templateagent
22

33
import (
4+
"context"
45
"testing"
56

67
corev1 "k8s.io/api/core/v1"
78
rbacv1 "k8s.io/api/rbac/v1"
89
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
910
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1011
"k8s.io/apimachinery/pkg/runtime"
12+
"k8s.io/klog/v2"
1113

1214
"open-cluster-management.io/addon-framework/pkg/addonfactory"
1315
addonapiv1alpha1 "open-cluster-management.io/api/addon/v1alpha1"
@@ -244,7 +246,9 @@ func TestProxyDecorator(t *testing.T) {
244246
if err != nil {
245247
t.Fatal(err)
246248
}
247-
d := newProxyDecorator(values)
249+
ctx := context.TODO()
250+
logger := klog.FromContext(ctx)
251+
d := newProxyHandler(logger, "addon1", values)
248252
err = d.decorate(tc.pod)
249253
if err != nil {
250254
t.Fatal(err)

pkg/addon/templateagent/template_agent.go

+52-2
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,13 @@ func (a *CRDTemplateAgentAddon) renderObjects(
186186
}
187187
objects = append(objects, object)
188188
}
189+
190+
additionalObjects, err := a.injectAdditionalObjects(template, presetValues, privateValues)
191+
if err != nil {
192+
return objects, err
193+
}
194+
objects = append(objects, additionalObjects...)
195+
189196
return objects, nil
190197
}
191198

@@ -195,8 +202,8 @@ func (a *CRDTemplateAgentAddon) decorateObject(
195202
orderedValues orderedValues,
196203
privateValues addonfactory.Values) (*unstructured.Unstructured, error) {
197204
decorators := []decorator{
198-
newDeploymentDecorator(a.addonName, template, orderedValues, privateValues),
199-
newDaemonSetDecorator(a.addonName, template, orderedValues, privateValues),
205+
newDeploymentDecorator(a.logger, a.addonName, template, orderedValues, privateValues),
206+
newDaemonSetDecorator(a.logger, a.addonName, template, orderedValues, privateValues),
200207
newNamespaceDecorator(privateValues),
201208
}
202209

@@ -211,6 +218,49 @@ func (a *CRDTemplateAgentAddon) decorateObject(
211218
return obj, nil
212219
}
213220

221+
func (a *CRDTemplateAgentAddon) injectAdditionalObjects(
222+
template *addonapiv1alpha1.AddOnTemplate,
223+
orderedValues orderedValues,
224+
privateValues addonfactory.Values) ([]runtime.Object, error) {
225+
injectors := []objectsInjector{
226+
newProxyHandler(a.logger, a.addonName, privateValues),
227+
}
228+
229+
decorators := []decorator{
230+
// decorate the namespace of the additional objects
231+
newNamespaceDecorator(privateValues),
232+
}
233+
234+
var objs []runtime.Object
235+
for _, injector := range injectors {
236+
objects, err := injector.inject()
237+
if err != nil {
238+
return nil, err
239+
}
240+
241+
for _, object := range objects {
242+
// convert the runtime.Object to unstructured.Unstructured
243+
unstructuredMapObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(object)
244+
if err != nil {
245+
return nil, err
246+
}
247+
unstructuredObject := &unstructured.Unstructured{Object: unstructuredMapObj}
248+
249+
for _, decorator := range decorators {
250+
unstructuredObject, err = decorator.decorate(unstructuredObject)
251+
if err != nil {
252+
return nil, err
253+
}
254+
}
255+
256+
objs = append(objs, unstructuredObject)
257+
}
258+
259+
}
260+
261+
return objs, nil
262+
}
263+
214264
// getDesiredAddOnTemplateInner returns the desired template of the addon,
215265
// if the desired template is not found in the configReferences, it will
216266
// return nil and no error, the caller should handle the nil template case.

0 commit comments

Comments
 (0)