Alerting: Migrate notification models to generated by grafnaa-app-sdk (#95430)

* create notifications module and generate models

* switch template group to app models

* switch time intervals to use app models

* switch receiver to use app models

* switch routing tree to use app models

* move schema registration to resource packages

* fix package names to match app

* fix codeowners

* fix UI to use metadata.name instead of uid

* update dockerfile

* move generated models to pkg

* remove provenance from field selector

* move client factories to test files

* rename GenericClient to TypedClient
This commit is contained in:
Yuri Tseretyan
2024-12-09 10:29:05 -05:00
committed by GitHub
parent c4e19f3570
commit 2d386e6704
131 changed files with 4665 additions and 5933 deletions
@@ -15,11 +15,13 @@ import (
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
"github.com/grafana/grafana/pkg/apis/alerting_notifications/v0alpha1"
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/timeinterval/v0alpha1"
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/resource/timeinterval/v0alpha1/fakes"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/generated/clientset/versioned"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
@@ -61,22 +63,20 @@ func TestIntegrationResourceIdentifier(t *testing.T) {
ctx := context.Background()
helper := getTestHelper(t)
adminK8sClient, err := versioned.NewForConfig(helper.Org1.Admin.NewRestConfig())
require.NoError(t, err)
client := adminK8sClient.NotificationsV0alpha1().TimeIntervals("default")
client := newClient(t, helper.Org1.Admin)
newInterval := &v0alpha1.TimeInterval{
ObjectMeta: v1.ObjectMeta{
Namespace: "default",
},
Spec: v0alpha1.TimeIntervalSpec{
Spec: v0alpha1.Spec{
Name: "time-newInterval",
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2),
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(2),
},
}
t.Run("create should fail if object name is specified", func(t *testing.T) {
interval := newInterval.DeepCopy()
interval := newInterval.Copy().(*v0alpha1.TimeInterval)
interval.Name = "time-newInterval"
_, err := client.Create(ctx, interval, v1.CreateOptions{})
require.Truef(t, errors.IsBadRequest(err), "Expected BadRequest but got %s", err)
@@ -104,7 +104,7 @@ func TestIntegrationResourceIdentifier(t *testing.T) {
if existingInterval == nil {
t.Skip()
}
updated := existingInterval.DeepCopy()
updated := existingInterval.Copy().(*v0alpha1.TimeInterval)
updated.Spec.Name = "another-newInterval"
actual, err := client.Update(ctx, updated, v1.UpdateOptions{})
require.NoError(t, err)
@@ -197,24 +197,18 @@ func TestIntegrationTimeIntervalAccessControl(t *testing.T) {
},
}
admin := org1.Admin
adminK8sClient, err := versioned.NewForConfig(admin.NewRestConfig())
require.NoError(t, err)
adminClient := adminK8sClient.NotificationsV0alpha1().TimeIntervals("default")
adminClient := newClient(t, helper.Org1.Admin)
for _, tc := range testCases {
t.Run(fmt.Sprintf("user '%s'", tc.user.Identity.GetLogin()), func(t *testing.T) {
k8sClient, err := versioned.NewForConfig(tc.user.NewRestConfig())
require.NoError(t, err)
client := k8sClient.NotificationsV0alpha1().TimeIntervals("default")
client := newClient(t, tc.user)
var expected = &v0alpha1.TimeInterval{
ObjectMeta: v1.ObjectMeta{
Namespace: "default",
},
Spec: v0alpha1.TimeIntervalSpec{
Spec: v0alpha1.Spec{
Name: fmt.Sprintf("time-interval-1-%s", tc.user.Identity.GetLogin()),
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2),
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(2),
},
}
expected.SetProvenanceStatus("")
@@ -280,8 +274,8 @@ func TestIntegrationTimeIntervalAccessControl(t *testing.T) {
})
}
updatedExpected := expected.DeepCopy()
updatedExpected.Spec.TimeIntervals = v0alpha1.IntervalGenerator{}.GenerateMany(2)
updatedExpected := expected.Copy().(*v0alpha1.TimeInterval)
updatedExpected.Spec.TimeIntervals = fakes.IntervalGenerator{}.GenerateMany(2)
d, err = json.Marshal(updatedExpected)
require.NoError(t, err)
@@ -294,7 +288,7 @@ func TestIntegrationTimeIntervalAccessControl(t *testing.T) {
expected = updated
t.Run("should get NotFound if name does not exist", func(t *testing.T) {
up := updatedExpected.DeepCopy()
up := updatedExpected.Copy().(*v0alpha1.TimeInterval)
up.Name = "notFound"
_, err := client.Update(ctx, up, v1.UpdateOptions{})
require.Truef(t, errors.IsNotFound(err), "Should get NotFound error but got: %s", err)
@@ -306,7 +300,7 @@ func TestIntegrationTimeIntervalAccessControl(t *testing.T) {
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
t.Run("should get forbidden even if resource does not exist", func(t *testing.T) {
up := updatedExpected.DeepCopy()
up := updatedExpected.Copy().(*v0alpha1.TimeInterval)
up.Name = "notFound"
_, err := client.Update(ctx, up, v1.UpdateOptions{})
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
@@ -361,9 +355,7 @@ func TestIntegrationTimeIntervalProvisioning(t *testing.T) {
org := helper.Org1
admin := org.Admin
adminK8sClient, err := versioned.NewForConfig(admin.NewRestConfig())
require.NoError(t, err)
adminClient := adminK8sClient.NotificationsV0alpha1().TimeIntervals("default")
adminClient := newClient(t, helper.Org1.Admin)
env := helper.GetEnv()
ac := acimpl.ProvideAccessControl(env.FeatureToggles, zanzana.NewNoopClient())
@@ -374,9 +366,9 @@ func TestIntegrationTimeIntervalProvisioning(t *testing.T) {
ObjectMeta: v1.ObjectMeta{
Namespace: "default",
},
Spec: v0alpha1.TimeIntervalSpec{
Spec: v0alpha1.Spec{
Name: "time-interval-1",
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2),
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(2),
},
}, v1.CreateOptions{})
require.NoError(t, err)
@@ -394,8 +386,8 @@ func TestIntegrationTimeIntervalProvisioning(t *testing.T) {
require.Equal(t, "API", got.GetProvenanceStatus())
})
t.Run("should not let update if provisioned", func(t *testing.T) {
updated := created.DeepCopy()
updated.Spec.TimeIntervals = v0alpha1.IntervalGenerator{}.GenerateMany(2)
updated := created.Copy().(*v0alpha1.TimeInterval)
updated.Spec.TimeIntervals = fakes.IntervalGenerator{}.GenerateMany(2)
_, err := adminClient.Update(ctx, updated, v1.UpdateOptions{})
require.Truef(t, errors.IsForbidden(err), "should get Forbidden error but got %s", err)
@@ -415,17 +407,15 @@ func TestIntegrationTimeIntervalOptimisticConcurrency(t *testing.T) {
ctx := context.Background()
helper := getTestHelper(t)
adminK8sClient, err := versioned.NewForConfig(helper.Org1.Admin.NewRestConfig())
require.NoError(t, err)
adminClient := adminK8sClient.NotificationsV0alpha1().TimeIntervals("default")
adminClient := newClient(t, helper.Org1.Admin)
interval := v0alpha1.TimeInterval{
ObjectMeta: v1.ObjectMeta{
Namespace: "default",
},
Spec: v0alpha1.TimeIntervalSpec{
Spec: v0alpha1.Spec{
Name: "time-interval",
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2),
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(2),
},
}
@@ -435,23 +425,23 @@ func TestIntegrationTimeIntervalOptimisticConcurrency(t *testing.T) {
require.NotEmpty(t, created.ResourceVersion)
t.Run("should forbid if version does not match", func(t *testing.T) {
updated := created.DeepCopy()
updated := created.Copy().(*v0alpha1.TimeInterval)
updated.ResourceVersion = "test"
_, err := adminClient.Update(ctx, updated, v1.UpdateOptions{})
require.Truef(t, errors.IsConflict(err), "should get Forbidden error but got %s", err)
})
t.Run("should update if version matches", func(t *testing.T) {
updated := created.DeepCopy()
updated.Spec.TimeIntervals = v0alpha1.IntervalGenerator{}.GenerateMany(2)
updated := created.Copy().(*v0alpha1.TimeInterval)
updated.Spec.TimeIntervals = fakes.IntervalGenerator{}.GenerateMany(2)
actualUpdated, err := adminClient.Update(ctx, updated, v1.UpdateOptions{})
require.NoError(t, err)
require.EqualValues(t, updated.Spec, actualUpdated.Spec)
require.NotEqual(t, updated.ResourceVersion, actualUpdated.ResourceVersion)
})
t.Run("should update if version is empty", func(t *testing.T) {
updated := created.DeepCopy()
updated := created.Copy().(*v0alpha1.TimeInterval)
updated.ResourceVersion = ""
updated.Spec.TimeIntervals = v0alpha1.IntervalGenerator{}.GenerateMany(2)
updated.Spec.TimeIntervals = fakes.IntervalGenerator{}.GenerateMany(2)
actualUpdated, err := adminClient.Update(ctx, updated, v1.UpdateOptions{})
require.NoError(t, err)
@@ -501,17 +491,15 @@ func TestIntegrationTimeIntervalPatch(t *testing.T) {
ctx := context.Background()
helper := getTestHelper(t)
adminK8sClient, err := versioned.NewForConfig(helper.Org1.Admin.NewRestConfig())
require.NoError(t, err)
adminClient := adminK8sClient.NotificationsV0alpha1().TimeIntervals("default")
adminClient := newClient(t, helper.Org1.Admin)
interval := v0alpha1.TimeInterval{
ObjectMeta: v1.ObjectMeta{
Namespace: "default",
},
Spec: v0alpha1.TimeIntervalSpec{
Spec: v0alpha1.Spec{
Name: "time-interval",
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2),
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(2),
},
}
@@ -534,7 +522,7 @@ func TestIntegrationTimeIntervalPatch(t *testing.T) {
})
t.Run("should patch with json patch", func(t *testing.T) {
expected := v0alpha1.IntervalGenerator{}.Generate()
expected := fakes.IntervalGenerator{}.Generate()
patch := []map[string]interface{}{
{
@@ -549,9 +537,11 @@ func TestIntegrationTimeIntervalPatch(t *testing.T) {
result, err := adminClient.Patch(ctx, current.Name, types.JSONPatchType, patchData, v1.PatchOptions{})
require.NoError(t, err)
expectedSpec := *current.Spec.DeepCopy()
expectedSpec.TimeIntervals = []v0alpha1.Interval{
expected,
expectedSpec := v0alpha1.Spec{
Name: current.Spec.Name,
TimeIntervals: []v0alpha1.Interval{
expected,
},
}
require.EqualValues(t, expectedSpec, result.Spec)
current = result
@@ -566,29 +556,27 @@ func TestIntegrationTimeIntervalListSelector(t *testing.T) {
ctx := context.Background()
helper := getTestHelper(t)
adminK8sClient, err := versioned.NewForConfig(helper.Org1.Admin.NewRestConfig())
require.NoError(t, err)
adminClient := adminK8sClient.NotificationsV0alpha1().TimeIntervals("default")
adminClient := newClient(t, helper.Org1.Admin)
interval1 := &v0alpha1.TimeInterval{
ObjectMeta: v1.ObjectMeta{
Namespace: "default",
},
Spec: v0alpha1.TimeIntervalSpec{
Spec: v0alpha1.Spec{
Name: "test1",
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2),
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(2),
},
}
interval1, err = adminClient.Create(ctx, interval1, v1.CreateOptions{})
interval1, err := adminClient.Create(ctx, interval1, v1.CreateOptions{})
require.NoError(t, err)
interval2 := &v0alpha1.TimeInterval{
ObjectMeta: v1.ObjectMeta{
Namespace: "default",
},
Spec: v0alpha1.TimeIntervalSpec{
Spec: v0alpha1.Spec{
Name: "test2",
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(2),
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(2),
},
}
interval2, err = adminClient.Create(ctx, interval2, v1.CreateOptions{})
@@ -630,7 +618,7 @@ func TestIntegrationTimeIntervalListSelector(t *testing.T) {
t.Run("should filter by multiple filters", func(t *testing.T) {
list, err := adminClient.List(ctx, v1.ListOptions{
FieldSelector: fmt.Sprintf("metadata.name=%s,metadata.provenance=%s", interval2.Name, "API"),
FieldSelector: fmt.Sprintf("metadata.name=%s,spec.name=%s", interval2.Name, interval2.Spec.Name),
})
require.NoError(t, err)
require.Len(t, list.Items, 1)
@@ -639,7 +627,7 @@ func TestIntegrationTimeIntervalListSelector(t *testing.T) {
t.Run("should be empty when filter does not match", func(t *testing.T) {
list, err := adminClient.List(ctx, v1.ListOptions{
FieldSelector: fmt.Sprintf("metadata.name=%s,metadata.provenance=%s", interval2.Name, "unknown"),
FieldSelector: fmt.Sprintf("metadata.name=%s", "unknown"),
})
require.NoError(t, err)
require.Empty(t, list.Items)
@@ -684,9 +672,7 @@ func TestIntegrationTimeIntervalReferentialIntegrity(t *testing.T) {
currentRoute := legacyCli.GetRoute(t)
currentRuleGroup := legacyCli.GetRulesGroup(t, folderUID, ruleGroup.Name)
adminK8sClient, err := versioned.NewForConfig(cliCfg)
require.NoError(t, err)
adminClient := adminK8sClient.NotificationsV0alpha1().TimeIntervals("default")
adminClient := newClient(t, helper.Org1.Admin)
intervals, err := adminClient.List(ctx, v1.ListOptions{})
require.NoError(t, err)
@@ -710,7 +696,7 @@ func TestIntegrationTimeIntervalReferentialIntegrity(t *testing.T) {
t.Run("Update", func(t *testing.T) {
t.Run("should rename all references if name changes", func(t *testing.T) {
renamed := interval.DeepCopy()
renamed := interval.Copy().(*v0alpha1.TimeInterval)
renamed.Spec.Name += "-new"
actual, err := adminClient.Update(ctx, renamed, v1.UpdateOptions{})
@@ -734,7 +720,7 @@ func TestIntegrationTimeIntervalReferentialIntegrity(t *testing.T) {
t.Run("should fail if at least one resource is provisioned", func(t *testing.T) {
require.NoError(t, err)
renamed := interval.DeepCopy()
renamed := interval.Copy().(*v0alpha1.TimeInterval)
renamed.Spec.Name += util.GenerateShortUID()
t.Run("provisioned route", func(t *testing.T) {
@@ -787,24 +773,22 @@ func TestIntegrationTimeIntervalValidation(t *testing.T) {
ctx := context.Background()
helper := getTestHelper(t)
adminK8sClient, err := versioned.NewForConfig(helper.Org1.Admin.NewRestConfig())
require.NoError(t, err)
adminClient := adminK8sClient.NotificationsV0alpha1().TimeIntervals("default")
adminClient := newClient(t, helper.Org1.Admin)
testCases := []struct {
name string
interval v0alpha1.TimeIntervalSpec
interval v0alpha1.Spec
}{
{
name: "missing name",
interval: v0alpha1.TimeIntervalSpec{
interval: v0alpha1.Spec{
Name: "",
TimeIntervals: v0alpha1.IntervalGenerator{}.GenerateMany(1),
TimeIntervals: fakes.IntervalGenerator{}.GenerateMany(1),
},
},
{
name: "invalid interval",
interval: v0alpha1.TimeIntervalSpec{
interval: v0alpha1.Spec{
Name: "test",
TimeIntervals: []v0alpha1.Interval{
{
@@ -823,9 +807,25 @@ func TestIntegrationTimeIntervalValidation(t *testing.T) {
},
Spec: tc.interval,
}
_, err = adminClient.Create(ctx, i, v1.CreateOptions{})
_, err := adminClient.Create(ctx, i, v1.CreateOptions{})
require.Error(t, err)
require.Truef(t, errors.IsBadRequest(err), "Expected BadRequest, got: %s", err)
})
}
}
func newClient(t *testing.T, user apis.User) *apis.TypedClient[v0alpha1.TimeInterval, v0alpha1.TimeIntervalList] {
t.Helper()
client, err := dynamic.NewForConfig(user.NewRestConfig())
require.NoError(t, err)
return &apis.TypedClient[v0alpha1.TimeInterval, v0alpha1.TimeIntervalList]{
Client: client.Resource(
schema.GroupVersionResource{
Group: v0alpha1.Kind().Group(),
Version: v0alpha1.Kind().Version(),
Resource: v0alpha1.Kind().Plural(),
}).Namespace("default"),
}
}