Skip to content

Commit 51742bc

Browse files
authored
✨ Support configuring install namespace for helm addon (#218)
* Support configuring install namespace for helm addon Signed-off-by: zhujian <[email protected]> * Add e2e test Signed-off-by: zhujian <[email protected]> * Add more comments for the agent instlal namespace Signed-off-by: zhujian <[email protected]> --------- Signed-off-by: zhujian <[email protected]>
1 parent ec48c5d commit 51742bc

File tree

5 files changed

+172
-37
lines changed

5 files changed

+172
-37
lines changed

Diff for: cmd/example/helloworld_helm/main.go

+12
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ func runController(ctx context.Context, kubeConfig *rest.Config) error {
9999
kubeConfig,
100100
helloworld_helm.AddonName,
101101
utilrand.String(5))
102+
// Set agent install namespace from addon deployment config if it exists
103+
// Note: If the agentAddonFactory.WithAgentInstallNamespace is set, we recommend
104+
// setting this to the same value or omitting this.
105+
registrationOption.AgentInstallNamespace = utils.AgentInstallNamespaceFromDeploymentConfigFunc(
106+
utils.NewAddOnDeploymentConfigGetter(addonClient),
107+
)
102108

103109
agentAddon, err := addonfactory.NewAgentAddonFactory(helloworld_helm.AddonName, helloworld_helm.FS, "manifests/charts/helloworld").
104110
WithConfigGVRs(
@@ -121,6 +127,12 @@ func runController(ctx context.Context, kubeConfig *rest.Config) error {
121127
helloworld_helm.GetImageValues(kubeClient),
122128
addonfactory.GetValuesFromAddonAnnotation,
123129
).WithAgentRegistrationOption(registrationOption).
130+
WithAgentInstallNamespace(
131+
// Set agent install namespace from addon deployment config if it exists
132+
utils.AgentInstallNamespaceFromDeploymentConfigFunc(
133+
utils.NewAddOnDeploymentConfigGetter(addonClient),
134+
),
135+
).
124136
BuildHelmAgentAddon()
125137
if err != nil {
126138
klog.Errorf("failed to build agent %v", err)

Diff for: pkg/addonfactory/addonfactory.go

+26-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ type AgentAddonFactory struct {
3434
getValuesFuncs []GetValuesFunc
3535
agentAddonOptions agent.AgentAddonOptions
3636
// trimCRDDescription flag is used to trim the description of CRDs in manifestWork. disabled by default.
37-
trimCRDDescription bool
38-
hostingCluster *clusterv1.ManagedCluster
37+
trimCRDDescription bool
38+
hostingCluster *clusterv1.ManagedCluster
39+
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
3940
}
4041

4142
// NewAgentAddonFactory builds an addonAgentFactory instance with addon name and fs.
@@ -144,8 +145,29 @@ func (f *AgentAddonFactory) WithAgentDeployTriggerClusterFilter(
144145
return f
145146
}
146147

148+
// WithAgentInstallNamespace defines the namespace where the agent resources will be deployed, this will
149+
// override the default built-in namespace value; And if the registrationOption is not nil but the
150+
// registrationOption.AgentInstallNamespace is nil, this will also set it to this.
151+
func (f *AgentAddonFactory) WithAgentInstallNamespace(
152+
nsFunc func(addon *addonapiv1alpha1.ManagedClusterAddOn) string,
153+
) *AgentAddonFactory {
154+
f.agentInstallNamespace = nsFunc
155+
return f
156+
}
157+
158+
// preBuildAddon sets the default values for the agentAddonOptions.
159+
func (f *AgentAddonFactory) preBuildAddon() {
160+
if f.agentInstallNamespace != nil {
161+
if f.agentAddonOptions.Registration != nil && f.agentAddonOptions.Registration.AgentInstallNamespace == nil {
162+
f.agentAddonOptions.Registration.AgentInstallNamespace = f.agentInstallNamespace
163+
}
164+
}
165+
}
166+
147167
// BuildHelmAgentAddon builds a helm agentAddon instance.
148168
func (f *AgentAddonFactory) BuildHelmAgentAddon() (agent.AgentAddon, error) {
169+
f.preBuildAddon()
170+
149171
if err := validateSupportedConfigGVRs(f.agentAddonOptions.SupportedConfigGVRs); err != nil {
150172
return nil, err
151173
}
@@ -162,6 +184,8 @@ func (f *AgentAddonFactory) BuildHelmAgentAddon() (agent.AgentAddon, error) {
162184

163185
// BuildTemplateAgentAddon builds a template agentAddon instance.
164186
func (f *AgentAddonFactory) BuildTemplateAgentAddon() (agent.AgentAddon, error) {
187+
f.preBuildAddon()
188+
165189
if err := validateSupportedConfigGVRs(f.agentAddonOptions.SupportedConfigGVRs); err != nil {
166190
return nil, err
167191
}

Diff for: pkg/addonfactory/helm_agentaddon.go

+33-23
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,24 @@ type helmDefaultValues struct {
4242
}
4343

4444
type HelmAgentAddon struct {
45-
decoder runtime.Decoder
46-
chart *chart.Chart
47-
getValuesFuncs []GetValuesFunc
48-
agentAddonOptions agent.AgentAddonOptions
49-
trimCRDDescription bool
50-
hostingCluster *clusterv1.ManagedCluster
45+
decoder runtime.Decoder
46+
chart *chart.Chart
47+
getValuesFuncs []GetValuesFunc
48+
agentAddonOptions agent.AgentAddonOptions
49+
trimCRDDescription bool
50+
hostingCluster *clusterv1.ManagedCluster
51+
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
5152
}
5253

5354
func newHelmAgentAddon(factory *AgentAddonFactory, chart *chart.Chart) *HelmAgentAddon {
5455
return &HelmAgentAddon{
55-
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
56-
chart: chart,
57-
getValuesFuncs: factory.getValuesFuncs,
58-
agentAddonOptions: factory.agentAddonOptions,
59-
trimCRDDescription: factory.trimCRDDescription,
60-
hostingCluster: factory.hostingCluster,
56+
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
57+
chart: chart,
58+
getValuesFuncs: factory.getValuesFuncs,
59+
agentAddonOptions: factory.agentAddonOptions,
60+
trimCRDDescription: factory.trimCRDDescription,
61+
hostingCluster: factory.hostingCluster,
62+
agentInstallNamespace: factory.agentInstallNamespace,
6163
}
6264
}
6365

@@ -176,7 +178,7 @@ func (a *HelmAgentAddon) getValues(
176178
overrideValues = MergeValues(overrideValues, builtinValues)
177179

178180
values, err := chartutil.ToRenderValues(a.chart, overrideValues,
179-
a.releaseOptions(cluster, addon), a.capabilities(cluster, addon))
181+
a.releaseOptions(addon), a.capabilities(cluster, addon))
180182
if err != nil {
181183
klog.Errorf("failed to render helm chart with values %v. err:%v", overrideValues, err)
182184
return values, err
@@ -185,17 +187,27 @@ func (a *HelmAgentAddon) getValues(
185187
return values, nil
186188
}
187189

190+
func (a *HelmAgentAddon) getValueAgentInstallNamespace(addon *addonapiv1alpha1.ManagedClusterAddOn) string {
191+
installNamespace := addon.Spec.InstallNamespace
192+
if len(installNamespace) == 0 {
193+
installNamespace = AddonDefaultInstallNamespace
194+
}
195+
if a.agentInstallNamespace != nil {
196+
ns := a.agentInstallNamespace(addon)
197+
if len(ns) > 0 {
198+
installNamespace = ns
199+
}
200+
}
201+
return installNamespace
202+
}
203+
188204
func (a *HelmAgentAddon) getBuiltinValues(
189205
cluster *clusterv1.ManagedCluster,
190206
addon *addonapiv1alpha1.ManagedClusterAddOn) (Values, error) {
191207
builtinValues := helmBuiltinValues{}
192208
builtinValues.ClusterName = cluster.GetName()
193209

194-
installNamespace := addon.Spec.InstallNamespace
195-
if len(installNamespace) == 0 {
196-
installNamespace = AddonDefaultInstallNamespace
197-
}
198-
builtinValues.AddonInstallNamespace = installNamespace
210+
builtinValues.AddonInstallNamespace = a.getValueAgentInstallNamespace(addon)
199211

200212
builtinValues.InstallMode, _ = constants.GetHostedModeInfo(addon.GetAnnotations())
201213

@@ -242,11 +254,9 @@ func (a *HelmAgentAddon) capabilities(
242254

243255
// only support Release.Name, Release.Namespace
244256
func (a *HelmAgentAddon) releaseOptions(
245-
cluster *clusterv1.ManagedCluster,
246257
addon *addonapiv1alpha1.ManagedClusterAddOn) chartutil.ReleaseOptions {
247-
installNamespace := addon.Spec.InstallNamespace
248-
if len(installNamespace) == 0 {
249-
installNamespace = AddonDefaultInstallNamespace
258+
return chartutil.ReleaseOptions{
259+
Name: a.agentAddonOptions.AddonName,
260+
Namespace: a.getValueAgentInstallNamespace(addon),
250261
}
251-
return chartutil.ReleaseOptions{Name: a.agentAddonOptions.AddonName, Namespace: installNamespace}
252262
}

Diff for: pkg/addonfactory/template_agentaddon.go

+17-9
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,21 @@ type templateFile struct {
3737
}
3838

3939
type TemplateAgentAddon struct {
40-
decoder runtime.Decoder
41-
templateFiles []templateFile
42-
getValuesFuncs []GetValuesFunc
43-
agentAddonOptions agent.AgentAddonOptions
44-
trimCRDDescription bool
40+
decoder runtime.Decoder
41+
templateFiles []templateFile
42+
getValuesFuncs []GetValuesFunc
43+
agentAddonOptions agent.AgentAddonOptions
44+
trimCRDDescription bool
45+
agentInstallNamespace func(addon *addonapiv1alpha1.ManagedClusterAddOn) string
4546
}
4647

4748
func newTemplateAgentAddon(factory *AgentAddonFactory) *TemplateAgentAddon {
4849
return &TemplateAgentAddon{
49-
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
50-
getValuesFuncs: factory.getValuesFuncs,
51-
agentAddonOptions: factory.agentAddonOptions,
52-
trimCRDDescription: factory.trimCRDDescription,
50+
decoder: serializer.NewCodecFactory(factory.scheme).UniversalDeserializer(),
51+
getValuesFuncs: factory.getValuesFuncs,
52+
agentAddonOptions: factory.agentAddonOptions,
53+
trimCRDDescription: factory.trimCRDDescription,
54+
agentInstallNamespace: factory.agentInstallNamespace,
5355
}
5456
}
5557

@@ -123,6 +125,12 @@ func (a *TemplateAgentAddon) getBuiltinValues(
123125
if len(installNamespace) == 0 {
124126
installNamespace = AddonDefaultInstallNamespace
125127
}
128+
if a.agentInstallNamespace != nil {
129+
ns := a.agentInstallNamespace(addon)
130+
if len(ns) > 0 {
131+
installNamespace = ns
132+
}
133+
}
126134
builtinValues.AddonInstallNamespace = installNamespace
127135

128136
builtinValues.InstallMode, _ = constants.GetHostedModeInfo(addon.GetAnnotations())

Diff for: test/e2e/helloworld_helm_test.go

+84-3
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,30 @@ var _ = ginkgo.Describe("install/uninstall helloworld helm addons", func() {
5050
_, err = hubAddOnClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Get(context.Background(), helloWorldHelmAddonName, metav1.GetOptions{})
5151
if err != nil {
5252
if errors.IsNotFound(err) {
53-
_, err = hubAddOnClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), &helloworldhelmAddon, metav1.CreateOptions{})
54-
if err != nil {
55-
return err
53+
_, cerr := hubAddOnClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Create(context.Background(), &helloworldhelmAddon, metav1.CreateOptions{})
54+
if cerr != nil {
55+
return cerr
5656
}
5757
}
5858
return err
5959
}
6060

61+
ginkgo.By("Make sure the agent namespace is created")
62+
testNs := &corev1.Namespace{
63+
ObjectMeta: metav1.ObjectMeta{
64+
Name: agentInstallNamespaceConfig,
65+
},
66+
}
67+
_, err = hubKubeClient.CoreV1().Namespaces().Create(context.Background(), testNs, metav1.CreateOptions{})
68+
if err != nil {
69+
if errors.IsAlreadyExists(err) {
70+
ginkgo.By("The agent namespace is already created")
71+
return nil
72+
}
73+
return err
74+
}
75+
ginkgo.By("The agent namespace is created")
76+
6177
return nil
6278
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
6379
})
@@ -106,6 +122,23 @@ var _ = ginkgo.Describe("install/uninstall helloworld helm addons", func() {
106122
return fmt.Errorf("addon agent deployment %s/%s is not deleted", addonInstallNamespace, agentDeploymentName)
107123
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
108124

125+
gomega.Eventually(func() error {
126+
_, err := hubKubeClient.CoreV1().Namespaces().Get(context.Background(),
127+
agentInstallNamespaceConfig, metav1.GetOptions{})
128+
if errors.IsNotFound(err) {
129+
return nil
130+
}
131+
132+
if err == nil {
133+
errd := hubKubeClient.CoreV1().Namespaces().Delete(context.Background(),
134+
agentInstallNamespaceConfig, metav1.DeleteOptions{})
135+
if errd != nil {
136+
return errd
137+
}
138+
}
139+
140+
return err
141+
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
109142
})
110143

111144
ginkgo.It("addon should be available", func() {
@@ -565,4 +598,52 @@ var _ = ginkgo.Describe("install/uninstall helloworld helm addons", func() {
565598
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
566599
})
567600

601+
ginkgo.It("addon registraion agent install namespace should work", func() {
602+
ginkgo.By("Prepare a AddOnDeploymentConfig for addon agent install namespace")
603+
gomega.Eventually(func() error {
604+
return prepareAgentInstallNamespaceAddOnDeploymentConfig(managedClusterName)
605+
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
606+
607+
ginkgo.By("Add the configs to ManagedClusterAddOn")
608+
gomega.Eventually(func() error {
609+
addon, err := hubAddOnClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Get(
610+
context.Background(), helloWorldHelmAddonName, metav1.GetOptions{})
611+
if err != nil {
612+
return err
613+
}
614+
newAddon := addon.DeepCopy()
615+
newAddon.Spec.Configs = []addonapiv1alpha1.AddOnConfig{
616+
{
617+
ConfigGroupResource: addonapiv1alpha1.ConfigGroupResource{
618+
Group: "addon.open-cluster-management.io",
619+
Resource: "addondeploymentconfigs",
620+
},
621+
ConfigReferent: addonapiv1alpha1.ConfigReferent{
622+
Namespace: managedClusterName,
623+
Name: deployAgentInstallNamespaceConfigName,
624+
},
625+
},
626+
}
627+
_, err = hubAddOnClient.AddonV1alpha1().ManagedClusterAddOns(managedClusterName).Update(
628+
context.Background(), newAddon, metav1.UpdateOptions{})
629+
if err != nil {
630+
return err
631+
}
632+
return nil
633+
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
634+
635+
ginkgo.By("Make sure addon is configured")
636+
gomega.Eventually(func() error {
637+
_, err := hubKubeClient.CoreV1().Secrets(agentInstallNamespaceConfig).Get(
638+
context.Background(), "helloworldhelm-hub-kubeconfig", metav1.GetOptions{})
639+
return err
640+
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
641+
642+
gomega.Eventually(func() error {
643+
_, err := hubKubeClient.AppsV1().Deployments(agentInstallNamespaceConfig).Get(
644+
context.Background(), "helloworldhelm-agent", metav1.GetOptions{})
645+
return err
646+
}, eventuallyTimeout, eventuallyInterval).ShouldNot(gomega.HaveOccurred())
647+
})
648+
568649
})

0 commit comments

Comments
 (0)