From 92d309ed8bea60d10f0fdcb9488e3358a78df186 Mon Sep 17 00:00:00 2001 From: skyrise-l <332437807@qq.com> Date: Thu, 29 Aug 2024 14:51:44 +0800 Subject: [PATCH 1/8] add addon dependent api and doc --- apis/extensions/v1alpha1/addon_types.go | 16 ++++ .../v1alpha1/zz_generated.deepcopy.go | 27 +++++++ .../extensions.kubeblocks.io_addons.yaml | 16 ++++ .../extensions/addon_controller_stages.go | 32 ++++++++ .../extensions/addon_controller_test.go | 47 +++++++++++ controllers/extensions/const.go | 1 + .../crds/extensions.kubeblocks.io_addons.yaml | 16 ++++ docs/developer_docs/api-reference/add-on.md | 79 +++++++++++++++++++ .../developer-docs/api-reference/add-on.md | 22 +++++- 9 files changed, 255 insertions(+), 1 deletion(-) diff --git a/apis/extensions/v1alpha1/addon_types.go b/apis/extensions/v1alpha1/addon_types.go index 61fb2ef6242..2e878691085 100644 --- a/apis/extensions/v1alpha1/addon_types.go +++ b/apis/extensions/v1alpha1/addon_types.go @@ -80,6 +80,10 @@ type AddonSpec struct { // // +optional CliPlugins []CliPlugin `json:"cliPlugins,omitempty"` + + // Specify all addons that this addon depends on. + // +optional + AddonDependencies []AddonDependency `json:"addonDependencies,omitempty"` } // AddonStatus defines the observed state of an add-on. @@ -319,6 +323,18 @@ type CliPlugin struct { Description string `json:"description,omitempty"` } +type AddonDependency struct { + // The name of the dependent addon. + // + // +kubebuilder:validation:Required + Name string `json:"name"` + + // All matching versions of the dependent addon. If empty, defaults to the same version as the current addon. + // + // +optional + Version []string `json:"version"` +} + func (r *ResourceMappingItem) HasStorageMapping() bool { return !(r == nil || r.Storage == "") } diff --git a/apis/extensions/v1alpha1/zz_generated.deepcopy.go b/apis/extensions/v1alpha1/zz_generated.deepcopy.go index 4d4ae99a74b..54d04fb8c07 100644 --- a/apis/extensions/v1alpha1/zz_generated.deepcopy.go +++ b/apis/extensions/v1alpha1/zz_generated.deepcopy.go @@ -79,6 +79,26 @@ func (in *AddonDefaultInstallSpecItem) DeepCopy() *AddonDefaultInstallSpecItem { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AddonDependency) DeepCopyInto(out *AddonDependency) { + *out = *in + if in.Version != nil { + in, out := &in.Version, &out.Version + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddonDependency. +func (in *AddonDependency) DeepCopy() *AddonDependency { + if in == nil { + return nil + } + out := new(AddonDependency) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AddonInstallExtraItem) DeepCopyInto(out *AddonInstallExtraItem) { *out = *in @@ -206,6 +226,13 @@ func (in *AddonSpec) DeepCopyInto(out *AddonSpec) { *out = make([]CliPlugin, len(*in)) copy(*out, *in) } + if in.AddonDependencies != nil { + in, out := &in.AddonDependencies, &out.AddonDependencies + *out = make([]AddonDependency, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddonSpec. diff --git a/config/crd/bases/extensions.kubeblocks.io_addons.yaml b/config/crd/bases/extensions.kubeblocks.io_addons.yaml index d99bbea79d0..5e6d0dbfb8d 100644 --- a/config/crd/bases/extensions.kubeblocks.io_addons.yaml +++ b/config/crd/bases/extensions.kubeblocks.io_addons.yaml @@ -62,6 +62,22 @@ spec: spec: description: AddonSpec defines the desired state of an add-on. properties: + addonDependencies: + description: Specifies all addons that this addon depends on. + items: + properties: + name: + description: Specifies the name of the dependent addon. + type: string + version: + description: All matching versions of the dependent addon. If empty, defaults to the same version as the current addon. + items: + type: string + type: array + required: + - name + type: object + type: array cliPlugins: description: Specifies the CLI plugin installation specifications. items: diff --git a/controllers/extensions/addon_controller_stages.go b/controllers/extensions/addon_controller_stages.go index f41bd32fff7..dbe35b69fc6 100644 --- a/controllers/extensions/addon_controller_stages.go +++ b/controllers/extensions/addon_controller_stages.go @@ -303,6 +303,12 @@ func (r *installableCheckStage) Handle(ctx context.Context) { return } + if err := checkAddonDependency(addon, r.reconciler.Client); err != nil { + setAddonErrorConditions(ctx, &r.stageCtx, addon, true, true, AddonCheckDependencyError, err.Error()) + r.setReconciled() + return + } + r.reqCtx.Log.V(1).Info("installableCheckStage", "phase", addon.Status.Phase) // check the annotations constraint about Kubeblocks Version @@ -1259,6 +1265,32 @@ func checkAddonSpec(addon *extensionsv1alpha1.Addon) error { return nil } +func checkAddonDependency(addon *extensionsv1alpha1.Addon, cli client.Client) error { + for _, dependency := range addon.Spec.AddonDependencies { + // Loop through the versions and check if at least one version exists + found := false + for _, version := range dependency.Version { + // If the Addon with the given name is not found, move to the next version + dependentAddon := &extensionsv1alpha1.Addon{} + if err := cli.Get(context.TODO(), client.ObjectKey{Name: dependency.Name, Namespace: addon.Namespace}, dependentAddon); err != nil { + if apierrors.IsNotFound(err) { + continue + } + return err + } + // Check if the version matches + if val, ok := dependentAddon.Labels[constant.AppVersionLabelKey]; ok && val == version { + found = true + break + } + } + if !found { + return fmt.Errorf("dependency %s with any of the specified versions %v not found", dependency.Name, dependency.Version) + } + } + return nil +} + func setAddonProviderAndVersion(ctx context.Context, stageCtx *stageCtx, addon *extensionsv1alpha1.Addon) { // if not set provider and version in spec, set it from labels if addon.Labels == nil { diff --git a/controllers/extensions/addon_controller_test.go b/controllers/extensions/addon_controller_test.go index e63ce76ce4e..4712ca2455f 100644 --- a/controllers/extensions/addon_controller_test.go +++ b/controllers/extensions/addon_controller_test.go @@ -99,6 +99,7 @@ var _ = Describe("Addon controller", func() { Context("Addon controller test", func() { var addon *extensionsv1alpha1.Addon + var depend_addon *extensionsv1alpha1.Addon var key types.NamespacedName var clusterKey types.NamespacedName BeforeEach(func() { @@ -577,6 +578,52 @@ var _ = Describe("Addon controller", func() { }).Should(Succeed()) }) + It("should successfully reconcile a custom resource for Addon install with dependency addon", func() { + By("By create addon with not installed dependent addon") + createAddonSpecWithRequiredAttributes(func(newOjb *extensionsv1alpha1.Addon) { + newOjb.Spec.Installable.AutoInstall = true + newOjb.Spec.AddonDependencies = []extensionsv1alpha1.AddonDependency{ + { + Name: "addon-test-123", + Version: []string{"1.0.0"}, + }, + } + }) + + By("By enable addon should failed") + Eventually(func(g Gomega) { + doReconcileOnce(g) + addon = &extensionsv1alpha1.Addon{} + g.Expect(testCtx.Cli.Get(ctx, key, addon)).To(Not(HaveOccurred())) + g.Expect(addon.Status.Phase).Should(Equal(extensionsv1alpha1.AddonFailed)) + g.Expect(addon.Spec.InstallSpec).Should(BeNil()) + g.Expect(addon.Status.ObservedGeneration).Should(BeEquivalentTo(1)) + }).Should(Succeed()) + + By("By install dependent addon") + modifiers := func(newObj *extensionsv1alpha1.Addon) { + newObj.Spec.Installable.AutoInstall = true + newObj.Name = "addon-test-123" + newObj.SetLabels(map[string]string{constant.AppVersionLabelKey: "1.0.0"}) + } + + depend_addon = testapps.CreateCustomizedObj(&testCtx, "addon/addon.yaml", &extensionsv1alpha1.Addon{}, modifiers) + Expect(depend_addon.Spec.DefaultInstallValues).ShouldNot(BeEmpty()) + + By("By enable addon when the dependent addon is installed") + createAddonSpecWithRequiredAttributes(func(newOjb *extensionsv1alpha1.Addon) { + newOjb.Spec.Installable.AutoInstall = true + newOjb.Spec.AddonDependencies = []extensionsv1alpha1.AddonDependency{ + { + Name: "addon-test-123", + Version: []string{"1.0.0"}, + }, + } + }) + enablingPhaseCheck(2) + fakeInstallationFailedJob(2) + }) + It("should successfully reconcile a custom resource for Addon with autoInstall=true with status.phase=Failed", func() { createAutoInstallAddon() diff --git a/controllers/extensions/const.go b/controllers/extensions/const.go index 46b6cac4a3c..8d2cccd4542 100644 --- a/controllers/extensions/const.go +++ b/controllers/extensions/const.go @@ -51,6 +51,7 @@ const ( UninstallationFailedLogs = "UninstallationFailedLogs" AddonRefObjError = "ReferenceObjectError" AddonCheckError = "AddonCheckError" + AddonCheckDependencyError = "AddonCheckDependencyError" // config keys used in viper maxConcurrentReconcilesKey = "MAXCONCURRENTRECONCILES_ADDON" diff --git a/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml b/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml index d99bbea79d0..5e6d0dbfb8d 100644 --- a/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml +++ b/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml @@ -62,6 +62,22 @@ spec: spec: description: AddonSpec defines the desired state of an add-on. properties: + addonDependencies: + description: Specifies all addons that this addon depends on. + items: + properties: + name: + description: Specifies the name of the dependent addon. + type: string + version: + description: All matching versions of the dependent addon. If empty, defaults to the same version as the current addon. + items: + type: string + type: array + required: + - name + type: object + type: array cliPlugins: description: Specifies the CLI plugin installation specifications. items: diff --git a/docs/developer_docs/api-reference/add-on.md b/docs/developer_docs/api-reference/add-on.md index f4e95dff671..50b0a8a3957 100644 --- a/docs/developer_docs/api-reference/add-on.md +++ b/docs/developer_docs/api-reference/add-on.md @@ -194,6 +194,20 @@ the selector and auto-install settings.

Specifies the CLI plugin installation specifications.

+ + +addonDependencies
+ + +[]AddonDependency + + + + +(Optional) +

Specify all addons that this addon depends on.

+ + @@ -624,6 +638,20 @@ the selector and auto-install settings.

Specifies the CLI plugin installation specifications.

+ + +addonDependencies
+ + +[]AddonDependency + + + + +(Optional) +

Specify all addons that this addon depends on.

+ +

AddonStatus @@ -755,6 +783,57 @@ string + + + + + + + + + + +
ValueDescription

"Helm"

+

AddonDependency +

+

+(Appears on:AddonSpec) +

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+name
+ +string + +
+

Name of the dependent addon.

+
+version
+ +string[] + +
+(Optional) +

All matching versions of the dependent addon. Default to the current addon version

+

DataObjectKeySelector

diff --git a/i18n/zh-cn/developer-docs/api-reference/add-on.md b/i18n/zh-cn/developer-docs/api-reference/add-on.md index 1a9dc3355c3..aad2e8f993f 100644 --- a/i18n/zh-cn/developer-docs/api-reference/add-on.md +++ b/i18n/zh-cn/developer-docs/api-reference/add-on.md @@ -40,7 +40,8 @@ Resource Types: defaultInstallValues
[]AddonDefaultInstallSpecItem

Default installation parameters.


install
AddonInstallSpec(Optional)

Installation parameters.


installable
InstallableSpec(Optional)

Addon installable spec. It provides selector and auto-install settings.


-cliPlugins
[]CliPlugin(Optional)

Plugin installation spec.


+cliPlugins
[]CliPlugin(Optional)

Plugin installation spec.


+addonDependencies
[]AddonDependency(Optional)

Addon Dependency spec.


status
AddonStatus @@ -183,6 +184,7 @@ Resource Types: install
AddonInstallSpec(Optional)

Installation parameters.


installable
InstallableSpec(Optional)

Addon installable spec. It provides selector and auto-install settings.


cliPlugins
[]CliPlugin(Optional)

Plugin installation spec.


+addonDependencies
[]AddonDependency(Optional)

Addon Dependency spec.


AddonStatus

@@ -242,6 +244,24 @@ Resource Types: description
string(Optional)

The description of the plugin.


+

AddonDependency

+

+(Appears on:AddonSpec) +

+
+
+ + + + + + + + + + + +
FieldDescription
name
string

Name of the dependent addon.


version
string[]
(Optional)

All matching versions of the dependent addon. Default to the current addon version


DataObjectKeySelector

(Appears on:HelmInstallValues) From 5631d9c8b27ab46da1d235bcd4272b7a59b68572 Mon Sep 17 00:00:00 2001 From: skyrise-l <332437807@qq.com> Date: Thu, 29 Aug 2024 15:08:57 +0800 Subject: [PATCH 2/8] add default addon dependent version --- controllers/extensions/addon_controller_stages.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/controllers/extensions/addon_controller_stages.go b/controllers/extensions/addon_controller_stages.go index dbe35b69fc6..f5bd107e727 100644 --- a/controllers/extensions/addon_controller_stages.go +++ b/controllers/extensions/addon_controller_stages.go @@ -1266,10 +1266,20 @@ func checkAddonSpec(addon *extensionsv1alpha1.Addon) error { } func checkAddonDependency(addon *extensionsv1alpha1.Addon, cli client.Client) error { + if len(addon.Spec.AddonDependencies) == 0 { + return nil + } for _, dependency := range addon.Spec.AddonDependencies { - // Loop through the versions and check if at least one version exists + versions := dependency.Version + if len(versions) == 0 { + currentVersion, ok := addon.Labels[constant.AppVersionLabelKey] + if !ok { + return fmt.Errorf("dependent addon version is nil and current addon does not have a version label") + } + versions = []string{currentVersion} + } found := false - for _, version := range dependency.Version { + for _, version := range versions { // If the Addon with the given name is not found, move to the next version dependentAddon := &extensionsv1alpha1.Addon{} if err := cli.Get(context.TODO(), client.ObjectKey{Name: dependency.Name, Namespace: addon.Namespace}, dependentAddon); err != nil { From 0f30e07fbb1996b138394a385fbe522666223b00 Mon Sep 17 00:00:00 2001 From: skyrise-l <332437807@qq.com> Date: Thu, 29 Aug 2024 15:20:49 +0800 Subject: [PATCH 3/8] fix doc --- docs/developer_docs/api-reference/add-on.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docs/developer_docs/api-reference/add-on.md b/docs/developer_docs/api-reference/add-on.md index 50b0a8a3957..6e6bd99983b 100644 --- a/docs/developer_docs/api-reference/add-on.md +++ b/docs/developer_docs/api-reference/add-on.md @@ -783,17 +783,6 @@ string - - - - - - - - - - -
ValueDescription

"Helm"

AddonDependency

From 9a4eda2f64e3bd6042375fac48dee3a10b01bcdf Mon Sep 17 00:00:00 2001 From: skyrise-l <332437807@qq.com> Date: Thu, 29 Aug 2024 15:44:15 +0800 Subject: [PATCH 4/8] fix doc --- .../extensions.kubeblocks.io_addons.yaml | 7 +- .../crds/extensions.kubeblocks.io_addons.yaml | 7 +- docs/developer_docs/api-reference/add-on.md | 80 +++++++++---------- 3 files changed, 48 insertions(+), 46 deletions(-) diff --git a/config/crd/bases/extensions.kubeblocks.io_addons.yaml b/config/crd/bases/extensions.kubeblocks.io_addons.yaml index 5e6d0dbfb8d..47346bcc045 100644 --- a/config/crd/bases/extensions.kubeblocks.io_addons.yaml +++ b/config/crd/bases/extensions.kubeblocks.io_addons.yaml @@ -63,14 +63,15 @@ spec: description: AddonSpec defines the desired state of an add-on. properties: addonDependencies: - description: Specifies all addons that this addon depends on. + description: Specify all addons that this addon depends on. items: properties: name: - description: Specifies the name of the dependent addon. + description: The name of the dependent addon. type: string version: - description: All matching versions of the dependent addon. If empty, defaults to the same version as the current addon. + description: All matching versions of the dependent addon. If + empty, defaults to the same version as the current addon. items: type: string type: array diff --git a/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml b/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml index 5e6d0dbfb8d..47346bcc045 100644 --- a/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml +++ b/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml @@ -63,14 +63,15 @@ spec: description: AddonSpec defines the desired state of an add-on. properties: addonDependencies: - description: Specifies all addons that this addon depends on. + description: Specify all addons that this addon depends on. items: properties: name: - description: Specifies the name of the dependent addon. + description: The name of the dependent addon. type: string version: - description: All matching versions of the dependent addon. If empty, defaults to the same version as the current addon. + description: All matching versions of the dependent addon. If + empty, defaults to the same version as the current addon. items: type: string type: array diff --git a/docs/developer_docs/api-reference/add-on.md b/docs/developer_docs/api-reference/add-on.md index 6e6bd99983b..612c22f0c8d 100644 --- a/docs/developer_docs/api-reference/add-on.md +++ b/docs/developer_docs/api-reference/add-on.md @@ -272,6 +272,46 @@ all selectors must evaluate to true.

+

AddonDependency +

+

+(Appears on:AddonSpec) +

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+name
+ +string + +
+

The name of the dependent addon.

+
+version
+ +[]string + +
+(Optional) +

All matching versions of the dependent addon. If empty, defaults to the same version as the current addon.

+

AddonInstallExtraItem

@@ -783,46 +823,6 @@ string -

AddonDependency -

-

-(Appears on:AddonSpec) -

-
-
- - - - - - - - - - - - - - - - - -
FieldDescription
-name
- -string - -
-

Name of the dependent addon.

-
-version
- -string[] - -
-(Optional) -

All matching versions of the dependent addon. Default to the current addon version

-

DataObjectKeySelector

From df218891a8820fb536d016535a6850e14d6acd99 Mon Sep 17 00:00:00 2001 From: skyrise-l <332437807@qq.com> Date: Thu, 29 Aug 2024 15:56:07 +0800 Subject: [PATCH 5/8] fix test --- controllers/extensions/addon_controller_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/extensions/addon_controller_test.go b/controllers/extensions/addon_controller_test.go index 4712ca2455f..1c29ca3bf88 100644 --- a/controllers/extensions/addon_controller_test.go +++ b/controllers/extensions/addon_controller_test.go @@ -99,7 +99,7 @@ var _ = Describe("Addon controller", func() { Context("Addon controller test", func() { var addon *extensionsv1alpha1.Addon - var depend_addon *extensionsv1alpha1.Addon + var dependAddon *extensionsv1alpha1.Addon var key types.NamespacedName var clusterKey types.NamespacedName BeforeEach(func() { @@ -607,8 +607,8 @@ var _ = Describe("Addon controller", func() { newObj.SetLabels(map[string]string{constant.AppVersionLabelKey: "1.0.0"}) } - depend_addon = testapps.CreateCustomizedObj(&testCtx, "addon/addon.yaml", &extensionsv1alpha1.Addon{}, modifiers) - Expect(depend_addon.Spec.DefaultInstallValues).ShouldNot(BeEmpty()) + dependAddon = testapps.CreateCustomizedObj(&testCtx, "addon/addon.yaml", &extensionsv1alpha1.Addon{}, modifiers) + Expect(dependAddon.Spec.DefaultInstallValues).ShouldNot(BeEmpty()) By("By enable addon when the dependent addon is installed") createAddonSpecWithRequiredAttributes(func(newOjb *extensionsv1alpha1.Addon) { From a78ff87f09e68782ac5f04810a9844972cd8d43e Mon Sep 17 00:00:00 2001 From: skyrise-l <332437807@qq.com> Date: Thu, 29 Aug 2024 16:28:17 +0800 Subject: [PATCH 6/8] fix label --- controllers/extensions/addon_controller_stages.go | 4 ++-- controllers/extensions/addon_controller_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/extensions/addon_controller_stages.go b/controllers/extensions/addon_controller_stages.go index f5bd107e727..b0de1cbb700 100644 --- a/controllers/extensions/addon_controller_stages.go +++ b/controllers/extensions/addon_controller_stages.go @@ -1272,7 +1272,7 @@ func checkAddonDependency(addon *extensionsv1alpha1.Addon, cli client.Client) er for _, dependency := range addon.Spec.AddonDependencies { versions := dependency.Version if len(versions) == 0 { - currentVersion, ok := addon.Labels[constant.AppVersionLabelKey] + currentVersion, ok := addon.Labels[AddonVersion] if !ok { return fmt.Errorf("dependent addon version is nil and current addon does not have a version label") } @@ -1289,7 +1289,7 @@ func checkAddonDependency(addon *extensionsv1alpha1.Addon, cli client.Client) er return err } // Check if the version matches - if val, ok := dependentAddon.Labels[constant.AppVersionLabelKey]; ok && val == version { + if val, ok := dependentAddon.Labels[AddonVersion]; ok && val == version { found = true break } diff --git a/controllers/extensions/addon_controller_test.go b/controllers/extensions/addon_controller_test.go index 1c29ca3bf88..e0574bcd319 100644 --- a/controllers/extensions/addon_controller_test.go +++ b/controllers/extensions/addon_controller_test.go @@ -604,7 +604,7 @@ var _ = Describe("Addon controller", func() { modifiers := func(newObj *extensionsv1alpha1.Addon) { newObj.Spec.Installable.AutoInstall = true newObj.Name = "addon-test-123" - newObj.SetLabels(map[string]string{constant.AppVersionLabelKey: "1.0.0"}) + newObj.SetLabels(map[string]string{AddonVersion: "1.0.0"}) } dependAddon = testapps.CreateCustomizedObj(&testCtx, "addon/addon.yaml", &extensionsv1alpha1.Addon{}, modifiers) From 9bbe4551fd1d974e087dbe254964fe7ce7112246 Mon Sep 17 00:00:00 2001 From: skyrise-l <332437807@qq.com> Date: Mon, 23 Sep 2024 16:36:30 +0800 Subject: [PATCH 7/8] fix some problems --- apis/extensions/v1alpha1/addon_types.go | 4 +- .../v1alpha1/zz_generated.deepcopy.go | 14 +++--- .../extensions.kubeblocks.io_addons.yaml | 34 ++++++------- .../extensions/addon_controller_stages.go | 48 +++++++++++++------ .../extensions/addon_controller_test.go | 10 ++-- .../crds/extensions.kubeblocks.io_addons.yaml | 34 ++++++------- docs/developer_docs/api-reference/add-on.md | 14 +++--- .../developer-docs/api-reference/add-on.md | 6 +-- 8 files changed, 91 insertions(+), 73 deletions(-) diff --git a/apis/extensions/v1alpha1/addon_types.go b/apis/extensions/v1alpha1/addon_types.go index 2e878691085..f0fe1c18260 100644 --- a/apis/extensions/v1alpha1/addon_types.go +++ b/apis/extensions/v1alpha1/addon_types.go @@ -83,7 +83,7 @@ type AddonSpec struct { // Specify all addons that this addon depends on. // +optional - AddonDependencies []AddonDependency `json:"addonDependencies,omitempty"` + DependentAddons []DependentAddon `json:"dependentAddons,omitempty"` } // AddonStatus defines the observed state of an add-on. @@ -323,7 +323,7 @@ type CliPlugin struct { Description string `json:"description,omitempty"` } -type AddonDependency struct { +type DependentAddon struct { // The name of the dependent addon. // // +kubebuilder:validation:Required diff --git a/apis/extensions/v1alpha1/zz_generated.deepcopy.go b/apis/extensions/v1alpha1/zz_generated.deepcopy.go index 54d04fb8c07..cea756a9b41 100644 --- a/apis/extensions/v1alpha1/zz_generated.deepcopy.go +++ b/apis/extensions/v1alpha1/zz_generated.deepcopy.go @@ -80,7 +80,7 @@ func (in *AddonDefaultInstallSpecItem) DeepCopy() *AddonDefaultInstallSpecItem { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AddonDependency) DeepCopyInto(out *AddonDependency) { +func (in *DependentAddon) DeepCopyInto(out *DependentAddon) { *out = *in if in.Version != nil { in, out := &in.Version, &out.Version @@ -89,12 +89,12 @@ func (in *AddonDependency) DeepCopyInto(out *AddonDependency) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddonDependency. -func (in *AddonDependency) DeepCopy() *AddonDependency { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DependentAddon. +func (in *DependentAddon) DeepCopy() *DependentAddon { if in == nil { return nil } - out := new(AddonDependency) + out := new(DependentAddon) in.DeepCopyInto(out) return out } @@ -226,9 +226,9 @@ func (in *AddonSpec) DeepCopyInto(out *AddonSpec) { *out = make([]CliPlugin, len(*in)) copy(*out, *in) } - if in.AddonDependencies != nil { - in, out := &in.AddonDependencies, &out.AddonDependencies - *out = make([]AddonDependency, len(*in)) + if in.DependentAddons != nil { + in, out := &in.DependentAddons, &out.DependentAddons + *out = make([]DependentAddon, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/config/crd/bases/extensions.kubeblocks.io_addons.yaml b/config/crd/bases/extensions.kubeblocks.io_addons.yaml index 47346bcc045..57a47bc26bc 100644 --- a/config/crd/bases/extensions.kubeblocks.io_addons.yaml +++ b/config/crd/bases/extensions.kubeblocks.io_addons.yaml @@ -62,23 +62,6 @@ spec: spec: description: AddonSpec defines the desired state of an add-on. properties: - addonDependencies: - description: Specify all addons that this addon depends on. - items: - properties: - name: - description: The name of the dependent addon. - type: string - version: - description: All matching versions of the dependent addon. If - empty, defaults to the same version as the current addon. - items: - type: string - type: array - required: - - name - type: object - type: array cliPlugins: description: Specifies the CLI plugin installation specifications. items: @@ -258,6 +241,23 @@ spec: type: object minItems: 1 type: array + dependentAddons: + description: Specify all addons that this addon depends on. + items: + properties: + name: + description: The name of the dependent addon. + type: string + version: + description: All matching versions of the dependent addon. If + empty, defaults to the same version as the current addon. + items: + type: string + type: array + required: + - name + type: object + type: array description: description: Specifies the description of the add-on. type: string diff --git a/controllers/extensions/addon_controller_stages.go b/controllers/extensions/addon_controller_stages.go index b0de1cbb700..c4e01a72a02 100644 --- a/controllers/extensions/addon_controller_stages.go +++ b/controllers/extensions/addon_controller_stages.go @@ -303,7 +303,7 @@ func (r *installableCheckStage) Handle(ctx context.Context) { return } - if err := checkAddonDependency(addon, r.reconciler.Client); err != nil { + if err := checkDependentAddon(addon, r.reconciler.Client); err != nil { setAddonErrorConditions(ctx, &r.stageCtx, addon, true, true, AddonCheckDependencyError, err.Error()) r.setReconciled() return @@ -1265,11 +1265,11 @@ func checkAddonSpec(addon *extensionsv1alpha1.Addon) error { return nil } -func checkAddonDependency(addon *extensionsv1alpha1.Addon, cli client.Client) error { - if len(addon.Spec.AddonDependencies) == 0 { +func checkDependentAddon(addon *extensionsv1alpha1.Addon, cli client.Client) error { + if len(addon.Spec.DependentAddons) == 0 { return nil } - for _, dependency := range addon.Spec.AddonDependencies { + for _, dependency := range addon.Spec.DependentAddons { versions := dependency.Version if len(versions) == 0 { currentVersion, ok := addon.Labels[AddonVersion] @@ -1279,23 +1279,41 @@ func checkAddonDependency(addon *extensionsv1alpha1.Addon, cli client.Client) er versions = []string{currentVersion} } found := false - for _, version := range versions { - // If the Addon with the given name is not found, move to the next version - dependentAddon := &extensionsv1alpha1.Addon{} - if err := cli.Get(context.TODO(), client.ObjectKey{Name: dependency.Name, Namespace: addon.Namespace}, dependentAddon); err != nil { - if apierrors.IsNotFound(err) { - continue - } - return err + + // Retrieve the dependent addon object + dependentAddon := &extensionsv1alpha1.Addon{} + if err := cli.Get(context.TODO(), client.ObjectKey{Name: dependency.Name, Namespace: addon.Namespace}, dependentAddon); err != nil { + if apierrors.IsNotFound(err) { + return fmt.Errorf("dependent addon %s not found", dependency.Name) + } + return err + } + + // Get the version of the dependent addon + depAddonVersionStr, ok := dependentAddon.Labels[AddonVersion] + if !ok { + return fmt.Errorf("dependent addon %s does not have a version label", dependency.Name) + } + depAddonVersion, err := semver.NewVersion(depAddonVersionStr) + if err != nil { + return fmt.Errorf("invalid version %s for addon %s: %v", depAddonVersionStr, dependency.Name, err) + } + + // Check if the dependent addon's version satisfies any of the specified constraints + for _, versionConstraint := range versions { + // Parse the version constraint + constraint, err := semver.NewConstraint(versionConstraint) + if err != nil { + return fmt.Errorf("invalid version constraint %s: %v", versionConstraint, err) } - // Check if the version matches - if val, ok := dependentAddon.Labels[AddonVersion]; ok && val == version { + // Check if the version satisfies the constraint + if constraint.Check(depAddonVersion) { found = true break } } if !found { - return fmt.Errorf("dependency %s with any of the specified versions %v not found", dependency.Name, dependency.Version) + return fmt.Errorf("dependent addon %s does not satisfy any of the specified version constraints %v", dependency.Name, versions) } } return nil diff --git a/controllers/extensions/addon_controller_test.go b/controllers/extensions/addon_controller_test.go index e0574bcd319..624a35adb33 100644 --- a/controllers/extensions/addon_controller_test.go +++ b/controllers/extensions/addon_controller_test.go @@ -582,10 +582,10 @@ var _ = Describe("Addon controller", func() { By("By create addon with not installed dependent addon") createAddonSpecWithRequiredAttributes(func(newOjb *extensionsv1alpha1.Addon) { newOjb.Spec.Installable.AutoInstall = true - newOjb.Spec.AddonDependencies = []extensionsv1alpha1.AddonDependency{ + newOjb.Spec.DependentAddons = []extensionsv1alpha1.DependentAddon{ { Name: "addon-test-123", - Version: []string{"1.0.0"}, + Version: []string{">=1.0.0"}, }, } }) @@ -604,7 +604,7 @@ var _ = Describe("Addon controller", func() { modifiers := func(newObj *extensionsv1alpha1.Addon) { newObj.Spec.Installable.AutoInstall = true newObj.Name = "addon-test-123" - newObj.SetLabels(map[string]string{AddonVersion: "1.0.0"}) + newObj.SetLabels(map[string]string{AddonVersion: "2.0.0"}) } dependAddon = testapps.CreateCustomizedObj(&testCtx, "addon/addon.yaml", &extensionsv1alpha1.Addon{}, modifiers) @@ -613,10 +613,10 @@ var _ = Describe("Addon controller", func() { By("By enable addon when the dependent addon is installed") createAddonSpecWithRequiredAttributes(func(newOjb *extensionsv1alpha1.Addon) { newOjb.Spec.Installable.AutoInstall = true - newOjb.Spec.AddonDependencies = []extensionsv1alpha1.AddonDependency{ + newOjb.Spec.DependentAddons = []extensionsv1alpha1.DependentAddon{ { Name: "addon-test-123", - Version: []string{"1.0.0"}, + Version: []string{">=1.0.0"}, }, } }) diff --git a/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml b/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml index 47346bcc045..57a47bc26bc 100644 --- a/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml +++ b/deploy/helm/crds/extensions.kubeblocks.io_addons.yaml @@ -62,23 +62,6 @@ spec: spec: description: AddonSpec defines the desired state of an add-on. properties: - addonDependencies: - description: Specify all addons that this addon depends on. - items: - properties: - name: - description: The name of the dependent addon. - type: string - version: - description: All matching versions of the dependent addon. If - empty, defaults to the same version as the current addon. - items: - type: string - type: array - required: - - name - type: object - type: array cliPlugins: description: Specifies the CLI plugin installation specifications. items: @@ -258,6 +241,23 @@ spec: type: object minItems: 1 type: array + dependentAddons: + description: Specify all addons that this addon depends on. + items: + properties: + name: + description: The name of the dependent addon. + type: string + version: + description: All matching versions of the dependent addon. If + empty, defaults to the same version as the current addon. + items: + type: string + type: array + required: + - name + type: object + type: array description: description: Specifies the description of the add-on. type: string diff --git a/docs/developer_docs/api-reference/add-on.md b/docs/developer_docs/api-reference/add-on.md index 612c22f0c8d..48309f29196 100644 --- a/docs/developer_docs/api-reference/add-on.md +++ b/docs/developer_docs/api-reference/add-on.md @@ -196,10 +196,10 @@ the selector and auto-install settings.

-addonDependencies
+DependentAddons
- -[]AddonDependency + +[]DependentAddon @@ -272,7 +272,7 @@ all selectors must evaluate to true.

-

AddonDependency +

DependentAddon

(Appears on:AddonSpec) @@ -680,10 +680,10 @@ the selector and auto-install settings.

-addonDependencies
+DependentAddons
- -[]AddonDependency + +[]DependentAddon diff --git a/i18n/zh-cn/developer-docs/api-reference/add-on.md b/i18n/zh-cn/developer-docs/api-reference/add-on.md index aad2e8f993f..971646cb475 100644 --- a/i18n/zh-cn/developer-docs/api-reference/add-on.md +++ b/i18n/zh-cn/developer-docs/api-reference/add-on.md @@ -41,7 +41,7 @@ Resource Types: install
AddonInstallSpec(Optional)

Installation parameters.


installable
InstallableSpec(Optional)

Addon installable spec. It provides selector and auto-install settings.


cliPlugins
[]CliPlugin(Optional)

Plugin installation spec.


-addonDependencies
[]AddonDependency(Optional)

Addon Dependency spec.


+DependentAddons
[]DependentAddon(Optional)

Addon Dependency spec.


status
AddonStatus @@ -184,7 +184,7 @@ Resource Types: install
AddonInstallSpec(Optional)

Installation parameters.


installable
InstallableSpec(Optional)

Addon installable spec. It provides selector and auto-install settings.


cliPlugins
[]CliPlugin(Optional)

Plugin installation spec.


-addonDependencies
[]AddonDependency(Optional)

Addon Dependency spec.


+DependentAddons
[]DependentAddon(Optional)

Addon Dependency spec.


AddonStatus

@@ -244,7 +244,7 @@ Resource Types: description
string(Optional)

The description of the plugin.


-

AddonDependency

+

DependentAddon

(Appears on:AddonSpec)

From 5ce0ebb92ef98bc40420f19e7f0c8e9b6e3b12a8 Mon Sep 17 00:00:00 2001 From: skyrise-l <332437807@qq.com> Date: Mon, 23 Sep 2024 16:41:12 +0800 Subject: [PATCH 8/8] fix doc --- .../v1alpha1/zz_generated.deepcopy.go | 40 ++++----- docs/developer_docs/api-reference/add-on.md | 84 +++++++++---------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/apis/extensions/v1alpha1/zz_generated.deepcopy.go b/apis/extensions/v1alpha1/zz_generated.deepcopy.go index cea756a9b41..966c9447be0 100644 --- a/apis/extensions/v1alpha1/zz_generated.deepcopy.go +++ b/apis/extensions/v1alpha1/zz_generated.deepcopy.go @@ -79,26 +79,6 @@ func (in *AddonDefaultInstallSpecItem) DeepCopy() *AddonDefaultInstallSpecItem { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DependentAddon) DeepCopyInto(out *DependentAddon) { - *out = *in - if in.Version != nil { - in, out := &in.Version, &out.Version - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DependentAddon. -func (in *DependentAddon) DeepCopy() *DependentAddon { - if in == nil { - return nil - } - out := new(DependentAddon) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AddonInstallExtraItem) DeepCopyInto(out *AddonInstallExtraItem) { *out = *in @@ -297,6 +277,26 @@ func (in *DataObjectKeySelector) DeepCopy() *DataObjectKeySelector { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DependentAddon) DeepCopyInto(out *DependentAddon) { + *out = *in + if in.Version != nil { + in, out := &in.Version, &out.Version + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DependentAddon. +func (in *DependentAddon) DeepCopy() *DependentAddon { + if in == nil { + return nil + } + out := new(DependentAddon) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in HelmInstallOptions) DeepCopyInto(out *HelmInstallOptions) { { diff --git a/docs/developer_docs/api-reference/add-on.md b/docs/developer_docs/api-reference/add-on.md index 48309f29196..e3377e7ed13 100644 --- a/docs/developer_docs/api-reference/add-on.md +++ b/docs/developer_docs/api-reference/add-on.md @@ -196,7 +196,7 @@ the selector and auto-install settings.

-DependentAddons
+dependentAddons
[]DependentAddon @@ -272,46 +272,6 @@ all selectors must evaluate to true.

-

DependentAddon -

-

-(Appears on:AddonSpec) -

-
-
- - - - - - - - - - - - - - - - - -
FieldDescription
-name
- -string - -
-

The name of the dependent addon.

-
-version
- -[]string - -
-(Optional) -

All matching versions of the dependent addon. If empty, defaults to the same version as the current addon.

-

AddonInstallExtraItem

@@ -680,7 +640,7 @@ the selector and auto-install settings.

-DependentAddons
+dependentAddons
[]DependentAddon @@ -862,6 +822,46 @@ string +

DependentAddon +

+

+(Appears on:AddonSpec) +

+
+
+ + + + + + + + + + + + + + + + + +
FieldDescription
+name
+ +string + +
+

The name of the dependent addon.

+
+version
+ +[]string + +
+(Optional) +

All matching versions of the dependent addon. If empty, defaults to the same version as the current addon.

+

HelmInstallOptions (map[string]string alias)