Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add addon checks to fix null pointer error bug in Kubernetes versions prior to 1.25. (#7114) #7185

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions controllers/extensions/addon_controller_stages.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,15 @@ func (r *deletionStage) Handle(ctx context.Context) {

func (r *installableCheckStage) Handle(ctx context.Context) {
r.process(func(addon *extensionsv1alpha1.Addon) {
// XValidation was introduced as an alpha feature in Kubernetes v1.23 and requires additional enablement.
// It became more stable after Kubernetes 1.25. Users may encounter error in Kubernetes versions prior to 1.25.
// additional check to the addon YAML to ensure support for Kubernetes versions prior to 1.25
if err := checkAddonSpec(addon); err != nil {
setAddonErrorConditions(ctx, &r.stageCtx, addon, true, true, AddonCheckError, err.Error())
r.setReconciled()
return
}

r.reqCtx.Log.V(1).Info("installableCheckStage", "phase", addon.Status.Phase)
if addon.Spec.Installable == nil {
return
Expand Down Expand Up @@ -1099,6 +1108,14 @@ func findDataKey[V string | []byte](data map[string]V, refObj extensionsv1alpha1
return false
}

func checkAddonSpec(addon *extensionsv1alpha1.Addon) error {
if addon.Spec.Type == "Helm" {
if addon.Spec.Helm == nil || addon.Spec.Helm.ChartLocationURL == "" {
return fmt.Errorf("invalid Helm configuration: either 'Helm' is not specified or 'ChartLocationURL' is empty")
}
}
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 {
Expand All @@ -1117,4 +1134,5 @@ func setAddonProviderAndVersion(ctx context.Context, stageCtx *stageCtx, addon *
if len(addon.Labels[AddonVersion]) == 0 && len(addon.Spec.Version) != 0 {
addon.Labels[AddonVersion] = addon.Spec.Version
}

}
41 changes: 41 additions & 0 deletions controllers/extensions/addon_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -314,6 +316,21 @@ var _ = Describe("Addon controller", func() {
Expect(testCtx.CreateObj(ctx, helmRelease)).Should(Succeed())
}

CheckKubernetesVersion := func() bool {
var serverVersion *version.Info
var isBeforeVersion bool
Eventually(func(g Gomega) {
clientset, err := kubernetes.NewForConfig(cfg)
g.Expect(err).NotTo(HaveOccurred())
discoveryClient := clientset.Discovery()
serverVersion, err = discoveryClient.ServerVersion()
g.Expect(err).NotTo(HaveOccurred())
isBeforeVersion = (version.CompareKubeAwareVersionStrings(serverVersion.String(), "v1.23.0") > 0)
}).Should(Succeed())
fmt.Printf("Kubernetes server version: %s\n", serverVersion.String())
return isBeforeVersion
}

It("should successfully reconcile a custom resource for Addon with spec.type=Helm", func() {
By("By create an addon")
createAddonSpecWithRequiredAttributes(func(newOjb *extensionsv1alpha1.Addon) {
Expand Down Expand Up @@ -631,6 +648,29 @@ var _ = Describe("Addon controller", func() {
addonStatusPhaseCheck(2, extensionsv1alpha1.AddonFailed, nil)
})

It("should failed reconcile a custom resource for Addon with missing spec helm of helm chartLocationURL", func() {
By("By check Kubernetes Version to decide if check")
// if k8s version > 1.23, it has kubebuilder X-Validation, no need to check again
if CheckKubernetesVersion() {
By("By create an addon with spec.helm = nil")
createAddonSpecWithRequiredAttributes(func(newOjb *extensionsv1alpha1.Addon) {
newOjb.Spec.Installable.AutoInstall = true
newOjb.Spec.Type = extensionsv1alpha1.HelmType
newOjb.Spec.Helm = nil
})

By("By check addon status")
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())
}
})

It("should have provider and version in spec for Addon with provider and version in label initially", func() {
By("By create an addon with auto-install and labels contains provider and version information")
createAddonSpecWithRequiredAttributes(func(newOjb *extensionsv1alpha1.Addon) {
Expand Down Expand Up @@ -691,6 +731,7 @@ var _ = Describe("Addon controller", func() {
g.Expect(addon.Labels).Should(HaveKeyWithValue(AddonVersion, "0.9.0"))
g.Expect(addon.Status.Phase).Should(Equal(extensionsv1alpha1.AddonEnabling))
}).Should(Succeed())

})
})

Expand Down
1 change: 1 addition & 0 deletions controllers/extensions/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const (
UninstallationFailed = "UninstallationFailed"
UninstallationFailedLogs = "UninstallationFailedLogs"
AddonRefObjError = "ReferenceObjectError"
AddonCheckError = "AddonCheckError"

// config keys used in viper
maxConcurrentReconcilesKey = "MAXCONCURRENTRECONCILES_ADDON"
Expand Down
Loading