mirror of https://github.com/grafana/grafana.git
Advisor: Allow to skip a step (#104454)
This commit is contained in:
parent
6f1382a0c8
commit
b2387c1a31
|
@ -33,6 +33,10 @@ func New(cfg app.Config) (app.App, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typesClient, err := clientGenerator.ClientFor(advisorv0alpha1.CheckTypeKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize checks
|
||||
checkMap := map[string]checks.Check{}
|
||||
|
@ -68,7 +72,7 @@ func New(cfg app.Config) (app.App, error) {
|
|||
return
|
||||
}
|
||||
ctx = identity.WithRequester(context.Background(), requester)
|
||||
err = processCheck(ctx, logger, client, req.Object, check)
|
||||
err = processCheck(ctx, logger, client, typesClient, req.Object, check)
|
||||
if err != nil {
|
||||
logger.Error("Error processing check", "error", err)
|
||||
}
|
||||
|
@ -84,7 +88,7 @@ func New(cfg app.Config) (app.App, error) {
|
|||
return
|
||||
}
|
||||
ctx = identity.WithRequester(context.Background(), requester)
|
||||
err = processCheckRetry(ctx, logger, client, req.Object, check)
|
||||
err = processCheckRetry(ctx, logger, client, typesClient, req.Object, check)
|
||||
if err != nil {
|
||||
logger.Error("Error processing check retry", "error", err)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package checks
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
|
@ -15,6 +16,8 @@ const (
|
|||
TypeLabel = "advisor.grafana.app/type"
|
||||
StatusAnnotation = "advisor.grafana.app/status"
|
||||
RetryAnnotation = "advisor.grafana.app/retry"
|
||||
IgnoreStepsAnnotation = "advisor.grafana.app/ignore-steps"
|
||||
IgnoreStepsAnnotationList = "advisor.grafana.app/ignore-steps-list"
|
||||
StatusAnnotationError = "error"
|
||||
StatusAnnotationProcessed = "processed"
|
||||
)
|
||||
|
@ -54,12 +57,28 @@ func GetRetryAnnotation(obj resource.Object) string {
|
|||
return obj.GetAnnotations()[RetryAnnotation]
|
||||
}
|
||||
|
||||
func SetStatusAnnotation(ctx context.Context, client resource.Client, obj resource.Object, status string) error {
|
||||
annotations := obj.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
func AddAnnotations(ctx context.Context, obj resource.Object, annotations map[string]string) map[string]string {
|
||||
existingAnnotations := obj.GetAnnotations()
|
||||
if existingAnnotations == nil {
|
||||
existingAnnotations = map[string]string{}
|
||||
}
|
||||
annotations[StatusAnnotation] = status
|
||||
maps.Copy(existingAnnotations, annotations)
|
||||
return existingAnnotations
|
||||
}
|
||||
|
||||
func DeleteAnnotations(ctx context.Context, obj resource.Object, annotations []string) map[string]string {
|
||||
existingAnnotations := obj.GetAnnotations()
|
||||
if existingAnnotations == nil {
|
||||
existingAnnotations = map[string]string{}
|
||||
}
|
||||
for _, annotation := range annotations {
|
||||
delete(existingAnnotations, annotation)
|
||||
}
|
||||
return existingAnnotations
|
||||
}
|
||||
|
||||
func SetStatusAnnotation(ctx context.Context, client resource.Client, obj resource.Object, status string) error {
|
||||
annotations := AddAnnotations(ctx, obj, map[string]string{StatusAnnotation: status})
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{{
|
||||
Operation: resource.PatchOpAdd,
|
||||
|
|
|
@ -65,6 +65,13 @@ func (r *Runner) createOrUpdate(ctx context.Context, log logging.Logger, obj res
|
|||
if errors.IsAlreadyExists(err) {
|
||||
// Already exists, update
|
||||
log.Debug("Check type already exists, updating", "identifier", id)
|
||||
// Retrieve current annotations to avoid overriding them
|
||||
current, err := r.client.Get(ctx, obj.GetStaticMetadata().Identifier())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
annotations := current.GetAnnotations()
|
||||
obj.SetAnnotations(annotations)
|
||||
_, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{})
|
||||
if err != nil {
|
||||
// Ignore the error, it's probably due to a race condition
|
||||
|
@ -97,7 +104,8 @@ func (r *Runner) Run(ctx context.Context) error {
|
|||
Namespace: r.namespace,
|
||||
Annotations: map[string]string{
|
||||
// Flag to indicate feature availability
|
||||
checks.RetryAnnotation: "1",
|
||||
checks.RetryAnnotation: "1",
|
||||
checks.IgnoreStepsAnnotation: "1",
|
||||
},
|
||||
},
|
||||
Spec: advisorv0alpha1.CheckTypeSpec{
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
k8sErrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
|
@ -18,6 +19,7 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
checks []checks.Check
|
||||
getFunc func(ctx context.Context, id resource.Identifier) (resource.Object, error)
|
||||
createFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error)
|
||||
updateFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error)
|
||||
expectedErr error
|
||||
|
@ -56,6 +58,37 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
|
|||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "create already exists, with custom annotations",
|
||||
checks: []checks.Check{
|
||||
&mockCheck{
|
||||
id: "check1",
|
||||
steps: []checks.Step{
|
||||
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
getFunc: func(ctx context.Context, id resource.Identifier) (resource.Object, error) {
|
||||
return &advisorv0alpha1.CheckType{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "check1",
|
||||
Annotations: map[string]string{
|
||||
checks.IgnoreStepsAnnotationList: "step1",
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
|
||||
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
|
||||
},
|
||||
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
|
||||
if obj.GetAnnotations()[checks.IgnoreStepsAnnotationList] != "step1" {
|
||||
return nil, fmt.Errorf("expected annotation %s, got %s", "step1", obj.GetAnnotations()[checks.IgnoreStepsAnnotationList])
|
||||
}
|
||||
return obj, nil
|
||||
},
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "create error",
|
||||
checks: []checks.Check{
|
||||
|
@ -115,6 +148,7 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
|
|||
r := &Runner{
|
||||
checkRegistry: &mockCheckRegistry{checks: tt.checks},
|
||||
client: &mockClient{
|
||||
getFunc: tt.getFunc,
|
||||
createFunc: tt.createFunc,
|
||||
updateFunc: tt.updateFunc,
|
||||
},
|
||||
|
@ -187,10 +221,18 @@ func (m *mockStep) Run(ctx context.Context, log logging.Logger, obj *advisorv0al
|
|||
type mockClient struct {
|
||||
resource.Client
|
||||
|
||||
getFunc func(ctx context.Context, id resource.Identifier) (resource.Object, error)
|
||||
createFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error)
|
||||
updateFunc func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error)
|
||||
}
|
||||
|
||||
func (m *mockClient) Get(ctx context.Context, id resource.Identifier) (resource.Object, error) {
|
||||
if m.getFunc != nil {
|
||||
return m.getFunc(ctx, id)
|
||||
}
|
||||
return advisorv0alpha1.CheckTypeKind().ZeroValue(), nil
|
||||
}
|
||||
|
||||
func (m *mockClient) Create(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
|
||||
return m.createFunc(ctx, id, obj, opts)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
|
@ -31,7 +32,7 @@ func getCheck(obj resource.Object, checkMap map[string]checks.Check) (checks.Che
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func processCheck(ctx context.Context, log logging.Logger, client resource.Client, obj resource.Object, check checks.Check) error {
|
||||
func processCheck(ctx context.Context, log logging.Logger, client resource.Client, typesClient resource.Client, obj resource.Object, check checks.Check) error {
|
||||
status := checks.GetStatusAnnotation(obj)
|
||||
if status != "" {
|
||||
// Check already processed
|
||||
|
@ -54,8 +55,20 @@ func processCheck(ctx context.Context, log logging.Logger, client resource.Clien
|
|||
}
|
||||
return fmt.Errorf("error initializing check: %w", err)
|
||||
}
|
||||
// Get the check type
|
||||
var checkType resource.Object
|
||||
checkType, err = typesClient.Get(ctx, resource.Identifier{
|
||||
Namespace: obj.GetNamespace(),
|
||||
Name: check.ID(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Run the steps
|
||||
steps := check.Steps()
|
||||
steps, err := filterSteps(checkType, check.Steps())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
failures, err := runStepsInParallel(ctx, log, &c.Spec, steps, items)
|
||||
if err != nil {
|
||||
setErr := checks.SetStatusAnnotation(ctx, client, obj, checks.StatusAnnotationError)
|
||||
|
@ -69,20 +82,27 @@ func processCheck(ctx context.Context, log logging.Logger, client resource.Clien
|
|||
Failures: failures,
|
||||
Count: int64(len(items)),
|
||||
}
|
||||
err = checks.SetStatusAnnotation(ctx, client, obj, checks.StatusAnnotationProcessed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Set the status annotation to processed and annotate the steps ignored
|
||||
annotations := checks.AddAnnotations(ctx, obj, map[string]string{
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
checks.IgnoreStepsAnnotationList: checkType.GetAnnotations()[checks.IgnoreStepsAnnotationList],
|
||||
})
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{{
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/status/report",
|
||||
Value: *report,
|
||||
}},
|
||||
Operations: []resource.PatchOperation{
|
||||
{
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/status/report",
|
||||
Value: *report,
|
||||
}, {
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/metadata/annotations",
|
||||
Value: annotations,
|
||||
},
|
||||
},
|
||||
}, resource.PatchOptions{}, obj)
|
||||
}
|
||||
|
||||
func processCheckRetry(ctx context.Context, log logging.Logger, client resource.Client, obj resource.Object, check checks.Check) error {
|
||||
func processCheckRetry(ctx context.Context, log logging.Logger, client resource.Client, typesClient resource.Client, obj resource.Object, check checks.Check) error {
|
||||
status := checks.GetStatusAnnotation(obj)
|
||||
if status == "" || status == checks.StatusAnnotationError {
|
||||
// Check not processed yet or errored
|
||||
|
@ -111,8 +131,20 @@ func processCheckRetry(ctx context.Context, log logging.Logger, client resource.
|
|||
}
|
||||
return fmt.Errorf("error initializing check: %w", err)
|
||||
}
|
||||
// Get the check type
|
||||
var checkType resource.Object
|
||||
checkType, err = typesClient.Get(ctx, resource.Identifier{
|
||||
Namespace: obj.GetNamespace(),
|
||||
Name: check.ID(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Run the steps
|
||||
steps := check.Steps()
|
||||
steps, err := filterSteps(checkType, check.Steps())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
failures, err := runStepsInParallel(ctx, log, &c.Spec, steps, []any{item})
|
||||
if err != nil {
|
||||
setErr := checks.SetStatusAnnotation(ctx, client, obj, checks.StatusAnnotationError)
|
||||
|
@ -137,8 +169,7 @@ func processCheckRetry(ctx context.Context, log logging.Logger, client resource.
|
|||
return false
|
||||
})
|
||||
// Delete the retry annotation to mark the check as processed
|
||||
annotations := obj.GetAnnotations()
|
||||
delete(annotations, checks.RetryAnnotation)
|
||||
annotations := checks.DeleteAnnotations(ctx, obj, []string{checks.RetryAnnotation})
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{{
|
||||
Operation: resource.PatchOpAdd,
|
||||
|
@ -193,3 +224,18 @@ func runStepsInParallel(ctx context.Context, log logging.Logger, spec *advisorv0
|
|||
wg.Wait()
|
||||
return reportFailures, internalErr
|
||||
}
|
||||
|
||||
func filterSteps(checkType resource.Object, steps []checks.Step) ([]checks.Step, error) {
|
||||
ignoreStepsList := checkType.GetAnnotations()[checks.IgnoreStepsAnnotationList]
|
||||
if ignoreStepsList != "" {
|
||||
filteredSteps := []checks.Step{}
|
||||
ignoreStepsList := strings.Split(ignoreStepsList, ",")
|
||||
for _, step := range steps {
|
||||
if !slices.Contains(ignoreStepsList, step.ID()) {
|
||||
filteredSteps = append(filteredSteps, step)
|
||||
}
|
||||
}
|
||||
return filteredSteps, nil
|
||||
}
|
||||
return steps, nil
|
||||
}
|
||||
|
|
|
@ -58,14 +58,15 @@ func TestProcessCheck(t *testing.T) {
|
|||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
check := &mockCheck{
|
||||
items: []any{"item"},
|
||||
}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, obj, check)
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessMultipleCheckItems(t *testing.T) {
|
||||
|
@ -77,6 +78,7 @@ func TestProcessMultipleCheckItems(t *testing.T) {
|
|||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
items := make([]any, 100)
|
||||
for i := range items {
|
||||
|
@ -90,9 +92,9 @@ func TestProcessMultipleCheckItems(t *testing.T) {
|
|||
items: items,
|
||||
}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, obj, check)
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
r := client.lastValue.(advisorv0alpha1.CheckV0alpha1StatusReport)
|
||||
assert.Equal(t, r.Count, int64(100))
|
||||
assert.Len(t, r.Failures, 50)
|
||||
|
@ -100,12 +102,13 @@ func TestProcessMultipleCheckItems(t *testing.T) {
|
|||
|
||||
func TestProcessCheck_AlreadyProcessed(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{checks.StatusAnnotation: "processed"})
|
||||
obj.SetAnnotations(map[string]string{checks.StatusAnnotation: checks.StatusAnnotationProcessed})
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
check := &mockCheck{}
|
||||
|
||||
err := processCheck(ctx, logging.DefaultLogger, client, obj, check)
|
||||
err := processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
|
@ -118,6 +121,7 @@ func TestProcessCheck_RunError(t *testing.T) {
|
|||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
|
@ -125,9 +129,35 @@ func TestProcessCheck_RunError(t *testing.T) {
|
|||
err: errors.New("run error"),
|
||||
}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, obj, check)
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessCheck_IgnoreSteps(t *testing.T) {
|
||||
checkType := &advisorv0alpha1.CheckType{}
|
||||
checkType.SetAnnotations(map[string]string{checks.IgnoreStepsAnnotationList: "mock"})
|
||||
typesClient := &mockTypesClient{
|
||||
res: checkType,
|
||||
}
|
||||
ctx := context.TODO()
|
||||
check := &mockCheck{
|
||||
items: []any{"item"},
|
||||
err: errors.New("run error, should not be triggered"),
|
||||
}
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{})
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, "mock", obj.GetAnnotations()[checks.IgnoreStepsAnnotationList])
|
||||
}
|
||||
|
||||
func TestProcessCheck_RunRecoversFromPanic(t *testing.T) {
|
||||
|
@ -139,6 +169,7 @@ func TestProcessCheck_RunRecoversFromPanic(t *testing.T) {
|
|||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
|
@ -146,10 +177,10 @@ func TestProcessCheck_RunRecoversFromPanic(t *testing.T) {
|
|||
runPanics: true,
|
||||
}
|
||||
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, obj, check)
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "panic recovered in step")
|
||||
assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessCheckRetry_NoRetry(t *testing.T) {
|
||||
|
@ -161,11 +192,12 @@ func TestProcessCheckRetry_NoRetry(t *testing.T) {
|
|||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{}
|
||||
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, obj, check)
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
|
@ -173,7 +205,7 @@ func TestProcessCheckRetry_RetryError(t *testing.T) {
|
|||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{
|
||||
checks.RetryAnnotation: "item",
|
||||
checks.StatusAnnotation: "processed",
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
})
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
|
@ -181,6 +213,7 @@ func TestProcessCheckRetry_RetryError(t *testing.T) {
|
|||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
|
@ -188,16 +221,16 @@ func TestProcessCheckRetry_RetryError(t *testing.T) {
|
|||
err: errors.New("retry error"),
|
||||
}
|
||||
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, obj, check)
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessCheckRetry_Success(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{
|
||||
checks.RetryAnnotation: "item",
|
||||
checks.StatusAnnotation: "processed",
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
})
|
||||
obj.CheckStatus.Report.Failures = []advisorv0alpha1.CheckReportFailure{
|
||||
{
|
||||
|
@ -211,15 +244,16 @@ func TestProcessCheckRetry_Success(t *testing.T) {
|
|||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
items: []any{"item"},
|
||||
}
|
||||
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, obj, check)
|
||||
err = processCheckRetry(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "processed", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Empty(t, obj.GetAnnotations()[checks.RetryAnnotation])
|
||||
assert.Empty(t, obj.CheckStatus.Report.Failures)
|
||||
}
|
||||
|
@ -234,6 +268,18 @@ func (m *mockClient) PatchInto(ctx context.Context, id resource.Identifier, req
|
|||
return nil
|
||||
}
|
||||
|
||||
type mockTypesClient struct {
|
||||
resource.Client
|
||||
res resource.Object
|
||||
}
|
||||
|
||||
func (m *mockTypesClient) Get(ctx context.Context, id resource.Identifier) (resource.Object, error) {
|
||||
if m.res == nil {
|
||||
return advisorv0alpha1.CheckTypeKind().ZeroValue(), nil
|
||||
}
|
||||
return m.res, nil
|
||||
}
|
||||
|
||||
type mockCheck struct {
|
||||
err error
|
||||
items []any
|
||||
|
|
|
@ -97,6 +97,21 @@ const injectedRtkApi = api
|
|||
}),
|
||||
providesTags: ['CheckType'],
|
||||
}),
|
||||
updateCheckType: build.mutation<UpdateCheckTypeApiResponse, UpdateCheckTypeApiArg>({
|
||||
query: (queryArg) => ({
|
||||
url: `/checktypes/${queryArg.name}`,
|
||||
method: 'PATCH',
|
||||
body: queryArg.patch,
|
||||
params: {
|
||||
pretty: queryArg.pretty,
|
||||
dryRun: queryArg.dryRun,
|
||||
fieldManager: queryArg.fieldManager,
|
||||
fieldValidation: queryArg.fieldValidation,
|
||||
force: queryArg.force,
|
||||
},
|
||||
}),
|
||||
invalidatesTags: ['CheckType'],
|
||||
}),
|
||||
}),
|
||||
overrideExisting: false,
|
||||
});
|
||||
|
@ -246,6 +261,22 @@ export type ListCheckTypeApiArg = {
|
|||
/** Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion. */
|
||||
watch?: boolean;
|
||||
};
|
||||
export type UpdateCheckTypeApiResponse = /** status 200 OK */ CheckType | /** status 201 Created */ CheckType;
|
||||
export type UpdateCheckTypeApiArg = {
|
||||
/** name of the CheckType */
|
||||
name: string;
|
||||
/** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
|
||||
pretty?: string;
|
||||
/** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
|
||||
dryRun?: string;
|
||||
/** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */
|
||||
fieldManager?: string;
|
||||
/** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
|
||||
fieldValidation?: string;
|
||||
/** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */
|
||||
force?: boolean;
|
||||
patch: Patch;
|
||||
};
|
||||
export type Time = string;
|
||||
export type FieldsV1 = object;
|
||||
export type ManagedFieldsEntry = {
|
||||
|
|
|
@ -15,6 +15,18 @@ export const advisorAPI = generatedAPI.enhanceEndpoints({
|
|||
});
|
||||
}
|
||||
},
|
||||
updateCheckType: (endpointDefinition) => {
|
||||
const originalQuery = endpointDefinition.query;
|
||||
if (originalQuery) {
|
||||
endpointDefinition.query = (requestOptions) => ({
|
||||
...originalQuery(requestOptions),
|
||||
headers: {
|
||||
'Content-Type': 'application/json-patch+json',
|
||||
},
|
||||
body: JSON.stringify(requestOptions.patch),
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
export const {
|
||||
|
@ -24,5 +36,6 @@ export const {
|
|||
useDeleteCheckMutation,
|
||||
useUpdateCheckMutation,
|
||||
useListCheckTypeQuery,
|
||||
useUpdateCheckTypeMutation,
|
||||
} = advisorAPI;
|
||||
export { type Check, type CheckType } from './endpoints.gen'; // eslint-disable-line
|
||||
|
|
|
@ -63,7 +63,15 @@ const config: ConfigFile = {
|
|||
'../public/app/api/clients/advisor/endpoints.gen.ts': {
|
||||
apiFile: '../public/app/api/clients/advisor/baseAPI.ts',
|
||||
schemaFile: '../data/openapi/advisor.grafana.app-v0alpha1.json',
|
||||
filterEndpoints: ['createCheck', 'getCheck', 'listCheck', 'deleteCheck', 'updateCheck', 'listCheckType'],
|
||||
filterEndpoints: [
|
||||
'createCheck',
|
||||
'getCheck',
|
||||
'listCheck',
|
||||
'deleteCheck',
|
||||
'updateCheck',
|
||||
'listCheckType',
|
||||
'updateCheckType',
|
||||
],
|
||||
tag: true,
|
||||
},
|
||||
'../public/app/api/clients/playlist/endpoints.gen.ts': {
|
||||
|
|
Loading…
Reference in New Issue