Skip to content

Commit

Permalink
Test: Verify field immutability for all CRDs (#1755)
Browse files Browse the repository at this point in the history
* fix: AlertRuleGroup editable only testing value, not presence

* test: AlertRuleGroup Editable

* fix: NotificationPolicy editable only testing value, not presence

* test: NotificationPolicy editable

* test: Dashboard uid

* test: ContactPoint uid

* test: Folder uid

* test: Datasource uid

* Chore: Disable Chainsaw test after being replaced with envtest
  • Loading branch information
Baarsgaard authored Nov 18, 2024
1 parent 73ef281 commit 1ac8d8d
Show file tree
Hide file tree
Showing 16 changed files with 371 additions and 3 deletions.
1 change: 1 addition & 0 deletions api/v1beta1/grafanaalertrulegroup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

// GrafanaAlertRuleGroupSpec defines the desired state of GrafanaAlertRuleGroup
// +kubebuilder:validation:XValidation:rule="(has(self.folderUID) && !(has(self.folderRef))) || (has(self.folderRef) && !(has(self.folderUID)))", message="Only one of FolderUID or FolderRef can be set"
// +kubebuilder:validation:XValidation:rule="((!has(oldSelf.editable) && !has(self.editable)) || (has(oldSelf.editable) && has(self.editable)))", message="spec.editable is immutable"
type GrafanaAlertRuleGroupSpec struct {
// +optional
// Name of the alert rule group. If not specified, the resource name will be used.
Expand Down
71 changes: 71 additions & 0 deletions api/v1beta1/grafanaalertrulegroup_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package v1beta1

import (
"context"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func newAlertRuleGroup(name string, editable *bool) *GrafanaAlertRuleGroup {
return &GrafanaAlertRuleGroup{
TypeMeta: v1.TypeMeta{
APIVersion: APIVersion,
Kind: "GrafanaAlertRuleGroup",
},
ObjectMeta: v1.ObjectMeta{
Name: name,
Namespace: "default",
},
Spec: GrafanaAlertRuleGroupSpec{
Name: name,
Editable: editable,
FolderRef: "DummyFolderRef",
InstanceSelector: &v1.LabelSelector{
MatchLabels: map[string]string{
"test": "alertrulegroup",
},
},
Rules: []AlertRule{},
},
}
}

var _ = Describe("AlertRuleGroup type", func() {
Context("Ensure AlertRuleGroup spec.editable is immutable", func() {
ctx := context.Background()
refTrue := true
refFalse := false

It("Should block adding editable field when missing", func() {
arg := newAlertRuleGroup("missing-editable", nil)
By("Create new AlertRuleGroup without editable")
Expect(k8sClient.Create(ctx, arg)).To(Succeed())

By("Adding a editable")
arg.Spec.Editable = &refTrue
Expect(k8sClient.Update(ctx, arg)).To(HaveOccurred())
})

It("Should block removing editable field when set", func() {
arg := newAlertRuleGroup("existing-editable", &refTrue)
By("Creating AlertRuleGroup with existing editable")
Expect(k8sClient.Create(ctx, arg)).To(Succeed())

By("And setting editable to ''")
arg.Spec.Editable = nil
Expect(k8sClient.Update(ctx, arg)).To(HaveOccurred())
})

It("Should block changing value of editable", func() {
arg := newAlertRuleGroup("removing-editable", &refTrue)
By("Create new AlertRuleGroup with existing editable")
Expect(k8sClient.Create(ctx, arg)).To(Succeed())

By("Changing the existing editable")
arg.Spec.Editable = &refFalse
Expect(k8sClient.Update(ctx, arg)).To(HaveOccurred())
})
})
})
74 changes: 74 additions & 0 deletions api/v1beta1/grafanacontactpoint_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package v1beta1

import (
"context"
"encoding/json"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

// apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func newContactPoint(name string, uid string) *GrafanaContactPoint {
settings := new(apiextensionsv1.JSON)
json.Unmarshal([]byte("{}"), settings) //nolint:errcheck

return &GrafanaContactPoint{
TypeMeta: v1.TypeMeta{
APIVersion: APIVersion,
Kind: "GrafanaContactPoint",
},
ObjectMeta: v1.ObjectMeta{
Name: name,
Namespace: "default",
},
Spec: GrafanaContactPointSpec{
CustomUID: uid,
InstanceSelector: &v1.LabelSelector{
MatchLabels: map[string]string{
"test": "datasource",
},
},
Settings: settings,
},
}
}

var _ = Describe("ContactPoint type", func() {
Context("Ensure ContactPoint spec.uid is immutable", func() {
ctx := context.Background()

It("Should block adding uid field when missing", func() {
contactpoint := newContactPoint("missing-uid", "")
By("Create new ContactPoint without uid")
Expect(k8sClient.Create(ctx, contactpoint)).To(Succeed())

By("Adding a uid")
contactpoint.Spec.CustomUID = "new-contactpoint-uid"
Expect(k8sClient.Update(ctx, contactpoint)).To(HaveOccurred())
})

It("Should block removing uid field when set", func() {
contactpoint := newContactPoint("existing-uid", "existing-uid")
By("Creating ContactPoint with existing UID")
Expect(k8sClient.Create(ctx, contactpoint)).To(Succeed())

By("And setting UID to ''")
contactpoint.Spec.CustomUID = ""
Expect(k8sClient.Update(ctx, contactpoint)).To(HaveOccurred())
})

It("Should block changing value of uid", func() {
contactpoint := newContactPoint("removing-uid", "existing-uid")
By("Create new ContactPoint with existing UID")
Expect(k8sClient.Create(ctx, contactpoint)).To(Succeed())

By("Changing the existing UID")
contactpoint.Spec.CustomUID = "new-contactpoint-uid"
Expect(k8sClient.Update(ctx, contactpoint)).To(HaveOccurred())
})
})
})
62 changes: 62 additions & 0 deletions api/v1beta1/grafanadashboard_types_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package v1beta1

import (
"context"
"encoding/json"
"testing"
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

Expand Down Expand Up @@ -276,3 +280,61 @@ func getDashboardCR(t *testing.T, crUID string, statusUID string, specUID string

return cr
}

func newDashboard(name string, uid string) *GrafanaDashboard {
return &GrafanaDashboard{
TypeMeta: v1.TypeMeta{
APIVersion: APIVersion,
Kind: "GrafanaDashboard",
},
ObjectMeta: v1.ObjectMeta{
Name: name,
Namespace: "default",
},
Spec: GrafanaDashboardSpec{
CustomUID: uid,
InstanceSelector: &v1.LabelSelector{
MatchLabels: map[string]string{
"test": "datasource",
},
},
Json: "",
},
}
}

var _ = Describe("Dashboard type", func() {
Context("Ensure Dashboard spec.uid is immutable", func() {
ctx := context.Background()

It("Should block adding uid field when missing", func() {
dash := newDashboard("missing-uid", "")
By("Create new Dashboard without uid")
Expect(k8sClient.Create(ctx, dash)).To(Succeed())

By("Adding a uid")
dash.Spec.CustomUID = "new-dash-uid"
Expect(k8sClient.Update(ctx, dash)).To(HaveOccurred())
})

It("Should block removing uid field when set", func() {
dash := newDashboard("existing-uid", "existing-uid")
By("Creating Dashboard with existing UID")
Expect(k8sClient.Create(ctx, dash)).To(Succeed())

By("And setting UID to ''")
dash.Spec.CustomUID = ""
Expect(k8sClient.Update(ctx, dash)).To(HaveOccurred())
})

It("Should block changing value of uid", func() {
dash := newDashboard("removing-uid", "existing-uid")
By("Create new Dashboard with existing UID")
Expect(k8sClient.Create(ctx, dash)).To(Succeed())

By("Changing the existing UID")
dash.Spec.CustomUID = "new-dash-uid"
Expect(k8sClient.Update(ctx, dash)).To(HaveOccurred())
})
})
})
5 changes: 3 additions & 2 deletions api/v1beta1/grafanadatasource_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ func newDatasource(name string, uid string) *GrafanaDatasource {
var _ = Describe("Datasource type", func() {
Context("Ensure Datasource spec.uid is immutable", func() {
ctx := context.Background()

It("Should block adding uid field when missing", func() {
ds := newDatasource("missing-uid", "")
By("Create new Datasource without uid")
Expect(k8sClient.Create(ctx, ds)).To(Succeed())

By("Adding a uid")
ds.Spec.CustomUID = "new-uid"
ds.Spec.CustomUID = "new-ds-uid"
Expect(k8sClient.Update(ctx, ds)).To(HaveOccurred())
})

Expand All @@ -63,7 +64,7 @@ var _ = Describe("Datasource type", func() {
Expect(k8sClient.Create(ctx, ds)).To(Succeed())

By("Changing the existing UID")
ds.Spec.CustomUID = "new-uid"
ds.Spec.CustomUID = "new-ds-uid"
Expect(k8sClient.Update(ctx, ds)).To(HaveOccurred())
})
})
Expand Down
61 changes: 61 additions & 0 deletions api/v1beta1/grafanafolder_types_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package v1beta1

import (
"context"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestGrafanaFolder_GetTitle(t *testing.T) {
Expand Down Expand Up @@ -72,3 +76,60 @@ func TestGrafanaFolder_GetUID(t *testing.T) {
})
}
}

func newFolder(name string, uid string) *GrafanaFolder {
return &GrafanaFolder{
TypeMeta: v1.TypeMeta{
APIVersion: APIVersion,
Kind: "GrafanaFolder",
},
ObjectMeta: v1.ObjectMeta{
Name: name,
Namespace: "default",
},
Spec: GrafanaFolderSpec{
CustomUID: uid,
InstanceSelector: &v1.LabelSelector{
MatchLabels: map[string]string{
"test": "folder",
},
},
},
}
}

var _ = Describe("Folder type", func() {
Context("Ensure Folder spec.uid is immutable", func() {
ctx := context.Background()

It("Should block adding uid field when missing", func() {
folder := newFolder("missing-uid", "")
By("Create new Folder without uid")
Expect(k8sClient.Create(ctx, folder)).To(Succeed())

By("Adding a uid")
folder.Spec.CustomUID = "new-folder-uid"
Expect(k8sClient.Update(ctx, folder)).To(HaveOccurred())
})

It("Should block removing uid field when set", func() {
folder := newFolder("existing-uid", "existing-uid")
By("Creating Folder with existing UID")
Expect(k8sClient.Create(ctx, folder)).To(Succeed())

By("And setting UID to ''")
folder.Spec.CustomUID = ""
Expect(k8sClient.Update(ctx, folder)).To(HaveOccurred())
})

It("Should block changing value of uid", func() {
folder := newFolder("removing-uid", "existing-uid")
By("Create new Folder with existing UID")
Expect(k8sClient.Create(ctx, folder)).To(Succeed())

By("Changing the existing UID")
folder.Spec.CustomUID = "new-folder-uid"
Expect(k8sClient.Update(ctx, folder)).To(HaveOccurred())
})
})
})
1 change: 1 addition & 0 deletions api/v1beta1/grafananotificationpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
)

// GrafanaNotificationPolicySpec defines the desired state of GrafanaNotificationPolicy
// +kubebuilder:validation:XValidation:rule="((!has(oldSelf.editable) && !has(self.editable)) || (has(oldSelf.editable) && has(self.editable)))", message="spec.editable is immutable"
type GrafanaNotificationPolicySpec struct {
// +optional
// +kubebuilder:validation:Type=string
Expand Down
Loading

0 comments on commit 1ac8d8d

Please sign in to comment.