From f28d149390fca43f5a23e603066aae37baf204e1 Mon Sep 17 00:00:00 2001 From: Kacper Rzetelski Date: Tue, 15 Oct 2024 10:36:48 +0200 Subject: [PATCH] Use clusters' OwnerUID labels to reconcile clusters registered with Scylla Manager --- pkg/controller/manager/status.go | 20 +- pkg/controller/manager/sync.go | 131 +-- pkg/controller/manager/sync_action.go | 230 ++--- pkg/controller/manager/sync_test.go | 1137 +++++++++++++++---------- 4 files changed, 873 insertions(+), 645 deletions(-) diff --git a/pkg/controller/manager/status.go b/pkg/controller/manager/status.go index 26d7f36a43..2be1b59ee9 100644 --- a/pkg/controller/manager/status.go +++ b/pkg/controller/manager/status.go @@ -6,14 +6,26 @@ import ( "context" scyllav1 "github.com/scylladb/scylla-operator/pkg/api/scylla/v1" + "github.com/scylladb/scylla-operator/pkg/naming" + "github.com/scylladb/scylla-operator/pkg/pointer" apiequality "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog/v2" ) -func (c *Controller) calculateStatus(sc *scyllav1.ScyllaCluster, managerState *state) *scyllav1.ScyllaClusterStatus { +func (c *Controller) calculateStatus(sc *scyllav1.ScyllaCluster, state *managerClusterState) *scyllav1.ScyllaClusterStatus { status := sc.Status.DeepCopy() + status.ManagerID = pointer.Ptr("") + status.Backups = []scyllav1.BackupTaskStatus{} + status.Repairs = []scyllav1.RepairTaskStatus{} + + if state.Cluster == nil || state.Cluster.Labels[naming.OwnerUIDLabel] != string(sc.UID) { + return status + } + + status.ManagerID = pointer.Ptr(state.Cluster.ID) + repairTaskClientErrorMap := map[string]string{} for _, rts := range status.Repairs { if rts.Error != nil { @@ -21,7 +33,6 @@ func (c *Controller) calculateStatus(sc *scyllav1.ScyllaCluster, managerState *s } } - status.Repairs = []scyllav1.RepairTaskStatus{} for _, rt := range sc.Spec.Repairs { repairTaskStatus := scyllav1.RepairTaskStatus{ TaskStatus: scyllav1.TaskStatus{ @@ -29,7 +40,7 @@ func (c *Controller) calculateStatus(sc *scyllav1.ScyllaCluster, managerState *s }, } - managerTaskStatus, isInManagerState := managerState.RepairTasks[rt.Name] + managerTaskStatus, isInManagerState := state.RepairTasks[rt.Name] if isInManagerState { repairTaskStatus = managerTaskStatus } else { @@ -52,7 +63,6 @@ func (c *Controller) calculateStatus(sc *scyllav1.ScyllaCluster, managerState *s } } - status.Backups = []scyllav1.BackupTaskStatus{} for _, bt := range sc.Spec.Backups { backupTaskStatus := scyllav1.BackupTaskStatus{ TaskStatus: scyllav1.TaskStatus{ @@ -60,7 +70,7 @@ func (c *Controller) calculateStatus(sc *scyllav1.ScyllaCluster, managerState *s }, } - managerTaskStatus, isInManagerState := managerState.BackupTasks[bt.Name] + managerTaskStatus, isInManagerState := state.BackupTasks[bt.Name] if isInManagerState { backupTaskStatus = managerTaskStatus } else { diff --git a/pkg/controller/manager/sync.go b/pkg/controller/manager/sync.go index ffefe7e5d1..a2027adbe9 100644 --- a/pkg/controller/manager/sync.go +++ b/pkg/controller/manager/sync.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/scylladb/scylla-manager/v3/pkg/managerclient" scyllav1 "github.com/scylladb/scylla-operator/pkg/api/scylla/v1" "github.com/scylladb/scylla-operator/pkg/helpers" "github.com/scylladb/scylla-operator/pkg/naming" @@ -24,59 +25,78 @@ func (c *Controller) getAuthToken(sc *scyllav1.ScyllaCluster) (string, error) { return helpers.GetAgentAuthTokenFromSecret(secret) } -func (c *Controller) getManagerState(ctx context.Context, clusterID string) (*state, error) { - clusters, err := c.managerClient.ListClusters(ctx) +func (c *Controller) getManagerClusterState(ctx context.Context, sc *scyllav1.ScyllaCluster) (*managerClusterState, error) { + managerClusters, err := c.managerClient.ListClusters(ctx) if err != nil { - return nil, err - } - var ( - repairTasks map[string]scyllav1.RepairTaskStatus - backupTasks map[string]scyllav1.BackupTaskStatus - ) - - if clusterID != "" { - clusterFound := false - for _, c := range clusters { - if c.ID == clusterID { - clusterFound = true - } + return nil, fmt.Errorf("can't list clusters registered with manager: %w", err) + } + + var managerCluster *managerclient.Cluster + clusterName := naming.ManagerClusterName(sc) + for _, mc := range managerClusters { + // Cluster names in manager state are unique, so it suffices to only find one with a matching name. + if mc.Name == clusterName { + managerCluster = mc + break } + } + + if managerCluster == nil { + return &managerClusterState{}, nil + } + + ownerUIDLabel := managerCluster.Labels[naming.OwnerUIDLabel] + if ownerUIDLabel != string(sc.UID) { + // Despite the label mismatch the cluster needs to be propagated to state so that we can delete it to avoid a name collision. + return &managerClusterState{ + Cluster: managerCluster, + }, nil + } + + // Sanity check. + if len(managerCluster.ID) == 0 { + return nil, fmt.Errorf("manager cluster is missing an ID") + } - if clusterFound { - managerRepairTasks, err := c.managerClient.ListTasks(ctx, clusterID, "repair", true, "", "") - if err != nil { - return nil, err - } - - repairTasks = make(map[string]scyllav1.RepairTaskStatus, len(managerRepairTasks.TaskListItemSlice)) - for _, managerRepairTask := range managerRepairTasks.TaskListItemSlice { - rts, err := NewRepairStatusFromManager(managerRepairTask) - if err != nil { - return nil, err - } - repairTasks[rts.Name] = *rts - } - - managerBackupTasks, err := c.managerClient.ListTasks(ctx, clusterID, "backup", true, "", "") - if err != nil { - return nil, err - } - - backupTasks = make(map[string]scyllav1.BackupTaskStatus, len(managerBackupTasks.TaskListItemSlice)) - for _, managerBackupTask := range managerBackupTasks.TaskListItemSlice { - bts, err := NewBackupStatusFromManager(managerBackupTask) - if err != nil { - return nil, err - } - backupTasks[bts.Name] = *bts - } + var repairTaskStatuses map[string]scyllav1.RepairTaskStatus + var backupTaskStatuses map[string]scyllav1.BackupTaskStatus + + var managerRepairTasks managerclient.TaskListItems + managerRepairTasks, err = c.managerClient.ListTasks(ctx, managerCluster.ID, "repair", true, "", "") + if err != nil { + return nil, fmt.Errorf("can't list repair tasks registered with manager: %w", err) + } + + repairTaskStatuses = make(map[string]scyllav1.RepairTaskStatus, len(managerRepairTasks.TaskListItemSlice)) + for _, managerRepairTask := range managerRepairTasks.TaskListItemSlice { + var repairTaskStatus *scyllav1.RepairTaskStatus + repairTaskStatus, err = NewRepairStatusFromManager(managerRepairTask) + if err != nil { + return nil, fmt.Errorf("can't get repair task status from manager task: %w", err) } + repairTaskStatuses[repairTaskStatus.Name] = *repairTaskStatus + } + + var managerBackupTasks managerclient.TaskListItems + managerBackupTasks, err = c.managerClient.ListTasks(ctx, managerCluster.ID, "backup", true, "", "") + if err != nil { + return nil, fmt.Errorf("can't list backup tasks registered with manager: %w", err) } - return &state{ - Clusters: clusters, - BackupTasks: backupTasks, - RepairTasks: repairTasks, + backupTaskStatuses = make(map[string]scyllav1.BackupTaskStatus, len(managerBackupTasks.TaskListItemSlice)) + for _, managerBackupTask := range managerBackupTasks.TaskListItemSlice { + var backupTaskStatus *scyllav1.BackupTaskStatus + backupTaskStatus, err = NewBackupStatusFromManager(managerBackupTask) + if err != nil { + return nil, fmt.Errorf("can't get backup task status from manager backup task: %w", err) + } + backupTaskStatuses[backupTaskStatus.Name] = *backupTaskStatus + } + + return &managerClusterState{ + Cluster: managerCluster, + BackupTasks: backupTaskStatuses, + RepairTasks: repairTaskStatuses, }, nil } @@ -102,17 +122,12 @@ func (c *Controller) sync(ctx context.Context, key string) error { return err } - clusterID := "" - if sc.Status.ManagerID != nil { - clusterID = *sc.Status.ManagerID - } - - managerState, err := c.getManagerState(ctx, clusterID) + state, err := c.getManagerClusterState(ctx, sc) if err != nil { - return fmt.Errorf("can't get manager state: %w", err) + return fmt.Errorf("can't get manager state for cluster %q: %w", naming.ObjRef(sc), err) } - status := c.calculateStatus(sc, managerState) + status := c.calculateStatus(sc, state) if sc.DeletionTimestamp != nil { return c.updateStatus(ctx, sc, status) @@ -120,12 +135,12 @@ func (c *Controller) sync(ctx context.Context, key string) error { authToken, err := c.getAuthToken(sc) if err != nil { - return fmt.Errorf("can't get auth token: %w", err) + return fmt.Errorf("can't get auth token for cluster %q: %w", naming.ObjRef(sc), err) } - actions, requeue, err := runSync(ctx, sc, authToken, managerState) + actions, requeue, err := runSync(ctx, sc, authToken, state) if err != nil { - return fmt.Errorf("can't run sync: %w", err) + return fmt.Errorf("can't run sync for cluster %q: %w", naming.ObjRef(sc), err) } var errs []error diff --git a/pkg/controller/manager/sync_action.go b/pkg/controller/manager/sync_action.go index 5c072a788a..4067a7cc40 100644 --- a/pkg/controller/manager/sync_action.go +++ b/pkg/controller/manager/sync_action.go @@ -14,83 +14,90 @@ import ( scyllav1 "github.com/scylladb/scylla-operator/pkg/api/scylla/v1" "github.com/scylladb/scylla-operator/pkg/helpers/slices" "github.com/scylladb/scylla-operator/pkg/naming" + hashutil "github.com/scylladb/scylla-operator/pkg/util/hash" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/sets" ) -type state struct { - Clusters []*managerclient.Cluster +type managerClusterState struct { + Cluster *managerclient.Cluster RepairTasks map[string]scyllav1.RepairTaskStatus BackupTasks map[string]scyllav1.BackupTaskStatus } -func runSync(ctx context.Context, cluster *scyllav1.ScyllaCluster, authToken string, state *state) ([]action, bool, error) { - var actions []action - requeue := false - clusterID := "" - clusterName := naming.ManagerClusterName(cluster) - if cluster.Status.ManagerID != nil { - clusterID = *cluster.Status.ManagerID - } - found := false - for _, c := range state.Clusters { - if c.Name == clusterName || c.ID == clusterID { - found = true - if c.ID == clusterID { - // TODO: can't detect changes for following params: - // * known hosts aren't returned by the API - // * username/password are not part of Cluster CRD - if c.AuthToken != authToken { - actions = append(actions, &updateClusterAction{ - cluster: &managerclient.Cluster{ - ID: c.ID, - Name: naming.ManagerClusterName(cluster), - Host: naming.CrossNamespaceServiceNameForCluster(cluster), - AuthToken: authToken, - // TODO: enable CQL over TLS when https://github.com/scylladb/scylla-operator/issues/1766 is completed - ForceNonSslSessionPort: true, - ForceTLSDisabled: true, - }, - }) - requeue = true - } - } else { - // Delete old to avoid name collision. - actions = append(actions, &deleteClusterAction{clusterID: c.ID}) - found = false - } - } +func runSync(ctx context.Context, sc *scyllav1.ScyllaCluster, authToken string, managerClusterState *managerClusterState) ([]action, bool, error) { + clusterAction, err := syncCluster(sc, authToken, managerClusterState.Cluster) + if err != nil { + return nil, false, fmt.Errorf("can't sync cluster %q: %w", naming.ObjRef(sc), err) } - if !found { - actions = append(actions, &addClusterAction{ - cluster: &managerclient.Cluster{ - Host: naming.CrossNamespaceServiceNameForCluster(cluster), - Name: naming.ManagerClusterName(cluster), - AuthToken: authToken, - // TODO: enable CQL over TLS when https://github.com/scylladb/scylla-operator/issues/1766 is completed - ForceNonSslSessionPort: true, - ForceTLSDisabled: true, - }, - }) - requeue = true + if clusterAction != nil { + // Execute the cluster action first and requeue to avoid potential errors in task execution in the same iteration. + return []action{clusterAction}, true, nil } - if found { - taskActions, err := syncTasks(clusterID, cluster, state) - if err != nil { - return nil, false, err - } - actions = append(actions, taskActions...) + var taskActions []action + taskActions, err = syncTasks(managerClusterState.Cluster.ID, sc, managerClusterState) + if err != nil { + return nil, false, fmt.Errorf("can't sync tasks for cluster %q: %w", naming.ObjRef(sc), err) + } + + return taskActions, false, nil +} + +func syncCluster(sc *scyllav1.ScyllaCluster, authToken string, existingManagerCluster *managerclient.Cluster) (action, error) { + clusterName := naming.ManagerClusterName(sc) + managerCluster := &managerclient.Cluster{ + Name: clusterName, + Host: naming.CrossNamespaceServiceNameForCluster(sc), + AuthToken: authToken, + // TODO: enable CQL over TLS when https://github.com/scylladb/scylla-operator/issues/1673 is completed + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Labels: map[string]string{ + naming.OwnerUIDLabel: string(sc.UID), + }, + } + + managedHash, err := hashutil.HashObjects(managerCluster) + if err != nil { + return nil, fmt.Errorf("can't calculate managed hash for cluster %q: %w", clusterName, err) + } + managerCluster.Labels[naming.ManagedHash] = managedHash + + if existingManagerCluster == nil { + return &addClusterAction{ + Cluster: managerCluster, + }, nil + } + + // Sanity check. + if len(existingManagerCluster.ID) == 0 { + return nil, fmt.Errorf("manager cluster is missing an id") } - return actions, requeue, nil + if existingManagerCluster.Labels == nil || existingManagerCluster.Labels[naming.OwnerUIDLabel] != string(sc.UID) { + // We're not certain the cluster is owned by us - we have to delete it to avoid a name collision. + return &deleteClusterAction{ + ClusterID: existingManagerCluster.ID, + }, nil + } + + if existingManagerCluster.Labels != nil && managedHash == existingManagerCluster.Labels[naming.ManagedHash] { + // Cluster matches the desired state, nothing to do. + return nil, nil + } + + managerCluster.ID = existingManagerCluster.ID + return &updateClusterAction{ + Cluster: managerCluster, + }, nil } -func syncTasks(clusterID string, cluster *scyllav1.ScyllaCluster, state *state) ([]action, error) { +func syncTasks(clusterID string, sc *scyllav1.ScyllaCluster, state *managerClusterState) ([]action, error) { var errs []error var actions []action - repairTaskSpecNames := sets.New(slices.ConvertSlice(cluster.Spec.Repairs, func(b scyllav1.RepairTaskSpec) string { + repairTaskSpecNames := sets.New(slices.ConvertSlice(sc.Spec.Repairs, func(b scyllav1.RepairTaskSpec) string { return b.Name })...) for taskName, task := range state.RepairTasks { @@ -99,13 +106,13 @@ func syncTasks(clusterID string, cluster *scyllav1.ScyllaCluster, state *state) } actions = append(actions, &deleteTaskAction{ - clusterID: clusterID, - taskType: managerclient.RepairTask, - taskID: *task.ID, + ClusterID: clusterID, + TaskType: managerclient.RepairTask, + TaskID: *task.ID, }) } - backupTaskSpecNames := sets.New(slices.ConvertSlice(cluster.Spec.Backups, func(b scyllav1.BackupTaskSpec) string { + backupTaskSpecNames := sets.New(slices.ConvertSlice(sc.Spec.Backups, func(b scyllav1.BackupTaskSpec) string { return b.Name })...) for taskName, task := range state.BackupTasks { @@ -114,19 +121,19 @@ func syncTasks(clusterID string, cluster *scyllav1.ScyllaCluster, state *state) } actions = append(actions, &deleteTaskAction{ - clusterID: clusterID, - taskType: managerclient.BackupTask, - taskID: *task.ID, + ClusterID: clusterID, + TaskType: managerclient.BackupTask, + TaskID: *task.ID, }) } - repairActions, err := syncRepairTasks(clusterID, cluster, state) + repairActions, err := syncRepairTasks(clusterID, sc, state) if err != nil { errs = append(errs, fmt.Errorf("can't sync repair tasks: %w", err)) } actions = append(actions, repairActions...) - backupActions, err := syncBackupTasks(clusterID, cluster, state) + backupActions, err := syncBackupTasks(clusterID, sc, state) if err != nil { errs = append(errs, fmt.Errorf("can't sync backup tasks: %w", err)) } @@ -140,13 +147,13 @@ func syncTasks(clusterID string, cluster *scyllav1.ScyllaCluster, state *state) return actions, nil } -func syncBackupTasks(clusterID string, cluster *scyllav1.ScyllaCluster, managerState *state) ([]action, error) { +func syncBackupTasks(clusterID string, cluster *scyllav1.ScyllaCluster, state *managerClusterState) ([]action, error) { var errs []error var actions []action for _, bt := range cluster.Spec.Backups { taskStatusFunc := func() (*scyllav1.TaskStatus, bool) { - s, ok := managerState.BackupTasks[bt.Name] + s, ok := state.BackupTasks[bt.Name] if !ok { return nil, false } @@ -176,13 +183,13 @@ func syncBackupTasks(clusterID string, cluster *scyllav1.ScyllaCluster, managerS return actions, nil } -func syncRepairTasks(clusterID string, cluster *scyllav1.ScyllaCluster, managerState *state) ([]action, error) { +func syncRepairTasks(clusterID string, cluster *scyllav1.ScyllaCluster, state *managerClusterState) ([]action, error) { var errs []error var actions []action for _, rt := range cluster.Spec.Repairs { taskStatusFunc := func() (*scyllav1.TaskStatus, bool) { - s, ok := managerState.RepairTasks[rt.Name] + s, ok := state.RepairTasks[rt.Name] if !ok { return nil, false } @@ -228,8 +235,8 @@ func syncTask(clusterID string, spec taskSpecInterface, statusFunc func() (*scyl setManagerClientTaskManagedHashLabel(managerClientTask, managedHash) return &addTaskAction{ - clusterID: clusterID, - task: managerClientTask, + ClusterID: clusterID, + Task: managerClientTask, }, nil } @@ -252,8 +259,8 @@ func syncTask(clusterID string, spec taskSpecInterface, statusFunc func() (*scyl managerClientTask.ID = *status.ID return &updateTaskAction{ - clusterID: clusterID, - task: managerClientTask, + ClusterID: clusterID, + Task: managerClientTask, }, nil } @@ -279,14 +286,13 @@ type action interface { } type addClusterAction struct { - cluster *managerclient.Cluster - clusterID string + Cluster *managerclient.Cluster } func (a *addClusterAction) Execute(ctx context.Context, client *managerclient.Client, status *scyllav1.ScyllaClusterStatus) error { - id, err := client.CreateCluster(ctx, a.cluster) + id, err := client.CreateCluster(ctx, a.Cluster) if err != nil { - return fmt.Errorf("can't create cluster %q: %w", a.cluster.Name, err) + return fmt.Errorf("can't create cluster %q: %w", a.Cluster.Name, err) } else { status.ManagerID = &id } @@ -295,55 +301,55 @@ func (a *addClusterAction) Execute(ctx context.Context, client *managerclient.Cl } func (a *addClusterAction) String() string { - return fmt.Sprintf("add cluster %q", a.clusterID) + return fmt.Sprintf("add cluster %q", a.Cluster.Name) } type updateClusterAction struct { - cluster *managerclient.Cluster + Cluster *managerclient.Cluster } func (a *updateClusterAction) Execute(ctx context.Context, client *managerclient.Client, _ *scyllav1.ScyllaClusterStatus) error { - err := client.UpdateCluster(ctx, a.cluster) + err := client.UpdateCluster(ctx, a.Cluster) if err != nil { - return fmt.Errorf("can't update cluster %q: %w", a.cluster.ID, err) + return fmt.Errorf("can't update cluster %q: %w", a.Cluster.ID, err) } return nil } func (a *updateClusterAction) String() string { - return fmt.Sprintf("update cluster %q", a.cluster.ID) + return fmt.Sprintf("update cluster %q", a.Cluster.ID) } type deleteClusterAction struct { - clusterID string + ClusterID string } func (a *deleteClusterAction) Execute(ctx context.Context, client *managerclient.Client, status *scyllav1.ScyllaClusterStatus) error { - err := client.DeleteCluster(ctx, a.clusterID) + err := client.DeleteCluster(ctx, a.ClusterID) if err != nil { - return fmt.Errorf("can't delete cluster %q: %w", a.clusterID, err) + return fmt.Errorf("can't delete cluster %q: %w", a.ClusterID, err) } return nil } func (a *deleteClusterAction) String() string { - return fmt.Sprintf("delete cluster %q", a.clusterID) + return fmt.Sprintf("delete cluster %q", a.ClusterID) } type deleteTaskAction struct { - clusterID string - taskType string - taskID string - taskName string + ClusterID string + TaskType string + TaskID string + TaskName string } func (a *deleteTaskAction) Execute(ctx context.Context, client *managerclient.Client, status *scyllav1.ScyllaClusterStatus) error { err := a.stopAndDeleteTask(ctx, client) if err != nil { - setTaskStatusError(a.taskType, a.taskName, messageOf(err), status) - return fmt.Errorf("can't stop and delete task %q: %w", a.taskName, err) + setTaskStatusError(a.TaskType, a.TaskName, messageOf(err), status) + return fmt.Errorf("can't stop and delete task %q: %w", a.TaskName, err) } return nil @@ -351,56 +357,56 @@ func (a *deleteTaskAction) Execute(ctx context.Context, client *managerclient.Cl func (a *deleteTaskAction) stopAndDeleteTask(ctx context.Context, client *managerclient.Client) error { // StopTask is idempotent - err := client.StopTask(ctx, a.clusterID, a.taskType, uuid.MustParse(a.taskID), false) + err := client.StopTask(ctx, a.ClusterID, a.TaskType, uuid.MustParse(a.TaskID), false) if err != nil { - return fmt.Errorf("can't stop task %q: %w", a.taskID, err) + return fmt.Errorf("can't stop task %q: %w", a.TaskID, err) } - err = client.DeleteTask(ctx, a.clusterID, a.taskType, uuid.MustParse(a.taskID)) + err = client.DeleteTask(ctx, a.ClusterID, a.TaskType, uuid.MustParse(a.TaskID)) if err != nil { - return fmt.Errorf("can't delete task %q: %w", a.taskID, err) + return fmt.Errorf("can't delete task %q: %w", a.TaskID, err) } return nil } func (a *deleteTaskAction) String() string { - return fmt.Sprintf("delete task %q", a.taskID) + return fmt.Sprintf("delete task %q", a.TaskID) } type addTaskAction struct { - clusterID string - task *managerclient.Task + ClusterID string + Task *managerclient.Task } func (a *addTaskAction) String() string { - return fmt.Sprintf("add task %+v", a.task) + return fmt.Sprintf("add task %+v", a.Task) } func (a *addTaskAction) Execute(ctx context.Context, client *managerclient.Client, status *scyllav1.ScyllaClusterStatus) error { - _, err := client.CreateTask(ctx, a.clusterID, a.task) + _, err := client.CreateTask(ctx, a.ClusterID, a.Task) if err != nil { - setTaskStatusError(a.task.Type, a.task.Name, messageOf(err), status) - return fmt.Errorf("can't create task %q: %w", a.task.Name, err) + setTaskStatusError(a.Task.Type, a.Task.Name, messageOf(err), status) + return fmt.Errorf("can't create task %q: %w", a.Task.Name, err) } return nil } type updateTaskAction struct { - clusterID string - task *managerclient.Task + ClusterID string + Task *managerclient.Task } func (a *updateTaskAction) String() string { - return fmt.Sprintf("update task %+v", a.task) + return fmt.Sprintf("update task %+v", a.Task) } func (a *updateTaskAction) Execute(ctx context.Context, client *managerclient.Client, status *scyllav1.ScyllaClusterStatus) error { - err := client.UpdateTask(ctx, a.clusterID, a.task) + err := client.UpdateTask(ctx, a.ClusterID, a.Task) if err != nil { - setTaskStatusError(a.task.Type, a.task.Name, messageOf(err), status) - return fmt.Errorf("can't update task %q: %w", a.task.Name, err) + setTaskStatusError(a.Task.Type, a.Task.Name, messageOf(err), status) + return fmt.Errorf("can't update task %q: %w", a.Task.Name, err) } return nil diff --git a/pkg/controller/manager/sync_test.go b/pkg/controller/manager/sync_test.go index 7acb09634d..a38a1d8258 100644 --- a/pkg/controller/manager/sync_test.go +++ b/pkg/controller/manager/sync_test.go @@ -4,675 +4,829 @@ package manager import ( "context" + "fmt" "reflect" "testing" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/scylladb/scylla-manager/v3/pkg/managerclient" scyllav1 "github.com/scylladb/scylla-operator/pkg/api/scylla/v1" "github.com/scylladb/scylla-operator/pkg/pointer" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestManagerSynchronization(t *testing.T) { - const ( - clusterAuthToken = "token" - namespace = "test" - name = "cluster" - clusterName = "test/cluster" - clusterID = "cluster-id" - ) +func Test_runSync(t *testing.T) { + t.Parallel() - tcs := []struct { - Name string - Spec scyllav1.ScyllaClusterSpec - Status scyllav1.ScyllaClusterStatus - State state - - Actions []action - Requeue bool + tt := []struct { + name string + sc *scyllav1.ScyllaCluster + authToken string + state *managerClusterState + expectedActions []action + expectedRequeue bool + expectedErr error }{ { - Name: "Empty manager, empty spec, add cluster and requeue", - Spec: scyllav1.ScyllaClusterSpec{}, - Status: scyllav1.ScyllaClusterStatus{}, - State: state{}, - - Requeue: true, - Actions: []action{&addClusterAction{cluster: &managerclient.Cluster{Name: clusterName}}}, - }, - { - Name: "Empty manager, task in spec, add cluster and requeue", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{}, + name: "no cluster in state, no tasks in state, return add cluster action and requeue", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: nil, + }, + expectedActions: []action{ + &addClusterAction{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + }, + }, }, - Status: scyllav1.ScyllaClusterStatus{}, - State: state{}, - - Requeue: true, - Actions: []action{&addClusterAction{cluster: &managerclient.Cluster{Name: clusterName}}}, + expectedRequeue: true, + expectedErr: nil, }, { - Name: "Cluster registered in manager do nothing", - Spec: scyllav1.ScyllaClusterSpec{}, - Status: scyllav1.ScyllaClusterStatus{}, - State: state{ - Clusters: []*managerclient.Cluster{{ - Name: clusterName, - AuthToken: clusterAuthToken, - }}, + name: "cluster in state, missing owner UID label, no tasks in state, return delete cluster action and requeue", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, }, - - Requeue: false, - Actions: nil, + expectedActions: []action{ + &deleteClusterAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + }, + expectedRequeue: true, + expectedErr: nil, }, { - Name: "Cluster registered in manager but auth token is different, update and requeue", - Spec: scyllav1.ScyllaClusterSpec{}, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: "different-auth-token", - }}, + name: "cluster in state, empty owner UID label, no tasks in state, return delete cluster action and requeue", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, }, - - Requeue: true, - Actions: []action{&updateClusterAction{cluster: &managerclient.Cluster{ID: clusterID}}}, + expectedActions: []action{ + &deleteClusterAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + }, + expectedRequeue: true, + expectedErr: nil, }, { - Name: "Name collision, delete old one, add new and requeue", - Spec: scyllav1.ScyllaClusterSpec{}, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: "different-id", - Name: clusterName, - }}, + name: "cluster in state, mismatching owner UID label, no tasks in state, return delete cluster action and requeue", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "other-uid", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, }, - - Requeue: true, - Actions: []action{ - &deleteClusterAction{clusterID: "different-id"}, - &addClusterAction{cluster: &managerclient.Cluster{Name: clusterName}}, + expectedActions: []action{ + &deleteClusterAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, }, + expectedRequeue: true, + expectedErr: nil, }, { - Name: "Schedule repair task", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ - Name: "my-repair", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2006-01-02T15:04:05Z"), - Interval: pointer.Ptr("0"), - }, + name: "cluster in state, matching owner UID label, missing managed hash label, no tasks in state, return update cluster action and requeue", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + }, + expectedActions: []action{ + &updateClusterAction{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", }, - SmallTableThreshold: "1GiB", - DC: []string{"dc1"}, - FailFast: false, - Intensity: "0.5", - Keyspace: []string{"keyspace1"}, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", }, }, }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, - }, - - Actions: []action{&addTaskAction{clusterID: clusterID, task: &managerclient.Task{Name: "my-repair"}}}, + expectedRequeue: true, + expectedErr: nil, }, { - Name: "Schedule backup task", - Spec: scyllav1.ScyllaClusterSpec{ - Backups: []scyllav1.BackupTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ - Name: "my-backup", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2006-01-02T15:04:05Z"), - Interval: pointer.Ptr("0"), - }, + name: "cluster in state, matching owner UID label, empty managed hash label, no tasks in state, return update cluster action and requeue", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + }, + expectedActions: []action{ + &updateClusterAction{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", }, - DC: []string{"dc1"}, - Keyspace: []string{"keyspace1"}, - Location: []string{"s3:abc"}, - RateLimit: []string{"dc1:1"}, - Retention: 3, - SnapshotParallel: []string{"dc1:1"}, - UploadParallel: []string{"dc1:1"}, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", }, }, }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), + expectedRequeue: true, + expectedErr: nil, + }, + { + name: "cluster in state, matching owner UID label, mismatching managed hash label, no tasks in state, return update cluster action and requeue", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "other-managed-hash", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + }, + expectedActions: []action{ + &updateClusterAction{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + }, }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, + expectedRequeue: true, + expectedErr: nil, + }, + { + name: "cluster in state, missing clusterID, return err", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "", + }, }, - - Actions: []action{&addTaskAction{clusterID: clusterID, task: &managerclient.Task{Name: "my-backup"}}}, + expectedActions: nil, + expectedRequeue: false, + expectedErr: fmt.Errorf(`can't sync cluster "test/test": %w`, fmt.Errorf(`manager cluster is missing an id`)), }, { - Name: "Update repair if it's already registered in Manager", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ - Name: "repair", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2006-01-02T15:04:05Z"), - Interval: pointer.Ptr("0"), - }, - }, - SmallTableThreshold: "1GiB", - Intensity: "0", + name: "matching cluster in state, no tasks in state, return add task actions", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", }, }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - Repairs: []scyllav1.RepairTaskStatus{ - { - TaskStatus: scyllav1.TaskStatus{ - SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2006-01-02T15:04:05Z"), - Interval: pointer.Ptr("0"), - }, - Name: "repair", - ID: pointer.Ptr("repair-id"), - Error: nil, + expectedActions: []action{ + &addTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + Task: &managerclient.Task{ + Enabled: true, + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "e6nOWps39EWcHS4BmiEti8MfyH6PL5Yt3kpM4Vej1Rd0SAsCM39CXe/XH1Yjhsi3b611nx7yfK9paShMxaNqGw==", }, - Intensity: pointer.Ptr("666"), - SmallTableThreshold: pointer.Ptr("1GiB"), + Name: "repair", + Properties: map[string]any{ + "intensity": float64(0.5), + "parallel": int64(0), + "small_table_threshold": int64(1073741824), + }, + Schedule: &managerclient.Schedule{ + StartDate: validDateTime, + }, + Type: "repair", }, }, - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, - RepairTasks: map[string]scyllav1.RepairTaskStatus{ - "repair": { - TaskStatus: scyllav1.TaskStatus{ - SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2006-01-02T15:04:05Z"), - Interval: pointer.Ptr("0"), - }, - Name: "repair", - ID: pointer.Ptr("repair-id"), + &addTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + Task: &managerclient.Task{ + Enabled: true, + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "c+YqtlphqYcYveZmyspNBzt77JuW9zNWKMdUPv8AGbP+9j3Gi4KvQqJyBrq5DPFDV6E8rWxcyXHF2mNYFzwIaA==", }, - Intensity: pointer.Ptr("123"), - SmallTableThreshold: pointer.Ptr("1GiB"), + Name: "backup", + Properties: map[string]any{ + "location": []string{"gcs:location"}, + "retention": int64(0), + }, + Schedule: &managerclient.Schedule{ + StartDate: validDateTime, + }, + Type: "backup", }, }, }, - - Actions: []action{&updateTaskAction{clusterID: clusterID, task: &managerclient.Task{ID: "repair-id"}}}, + expectedRequeue: false, + expectedErr: nil, }, { - Name: "Do not update task when it didn't change", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ - Name: "repair", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), + name: "matching cluster in state, tasks in state, missing managed hash labels, return update task actions", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + BackupTasks: map[string]scyllav1.BackupTaskStatus{ + "backup": { + TaskStatus: scyllav1.TaskStatus{ + SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ + StartDate: pointer.Ptr(validDate), }, + Name: "backup", + ID: pointer.Ptr("backup-id"), + Labels: map[string]string{}, }, - SmallTableThreshold: "1GiB", - Intensity: "666", - Parallel: 0, + Location: []string{"gcs:location"}, }, }, - }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, RepairTasks: map[string]scyllav1.RepairTaskStatus{ "repair": { TaskStatus: scyllav1.TaskStatus{ SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), - }, - Name: "repair", - ID: pointer.Ptr("repair-id"), - Labels: map[string]string{ - "scylla-operator.scylladb.com/managed-hash": "9gAqa0Ngh483/n6qTDn3FMKGvyMXrUKqPS3Jp5RDp8RJ1/58h8p5oYrtP7r6rYmNoRY1neKQHvV1IIzmMr6hBg==", + StartDate: pointer.Ptr(validDate), }, + Name: "repair", + ID: pointer.Ptr("repair-id"), + Labels: map[string]string{}, }, - FailFast: pointer.Ptr(false), - Intensity: pointer.Ptr("666"), + Intensity: pointer.Ptr("0.5"), Parallel: pointer.Ptr[int64](0), SmallTableThreshold: pointer.Ptr("1GiB"), }, }, }, - Actions: nil, - }, - { - Name: "Delete tasks from Manager unknown to spec", - Spec: scyllav1.ScyllaClusterSpec{}, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, - RepairTasks: map[string]scyllav1.RepairTaskStatus{ - "other-repair": { - TaskStatus: scyllav1.TaskStatus{ - Name: "other-repair", - SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2006-01-02T15:04:05Z"), - Interval: pointer.Ptr("0"), - }, - ID: pointer.Ptr("other-repair-id"), + expectedActions: []action{ + &updateTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + Task: &managerclient.Task{ + ID: "repair-id", + Enabled: true, + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "e6nOWps39EWcHS4BmiEti8MfyH6PL5Yt3kpM4Vej1Rd0SAsCM39CXe/XH1Yjhsi3b611nx7yfK9paShMxaNqGw==", + }, + Name: "repair", + Properties: map[string]any{ + "intensity": float64(0.5), + "parallel": int64(0), + "small_table_threshold": int64(1073741824), + }, + Schedule: &managerclient.Schedule{ + StartDate: validDateTime, }, + Type: "repair", + }, + }, + &updateTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + Task: &managerclient.Task{ + ID: "backup-id", + Enabled: true, + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "c+YqtlphqYcYveZmyspNBzt77JuW9zNWKMdUPv8AGbP+9j3Gi4KvQqJyBrq5DPFDV6E8rWxcyXHF2mNYFzwIaA==", + }, + Name: "backup", + Properties: map[string]any{ + "location": []string{"gcs:location"}, + "retention": int64(0), + }, + Schedule: &managerclient.Schedule{ + StartDate: validDateTime, + }, + Type: "backup", }, }, }, - - Actions: []action{&deleteTaskAction{clusterID: clusterID, taskID: "other-repair-id"}}, + expectedRequeue: false, + expectedErr: nil, }, { - Name: "Special 'now' startDate is not compared during update decision", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ - Name: "repair", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("now"), - Interval: pointer.Ptr("0"), + name: "matching cluster in state, tasks in state, empty managed hash labels, return update task actions", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + BackupTasks: map[string]scyllav1.BackupTaskStatus{ + "backup": { + TaskStatus: scyllav1.TaskStatus{ + SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ + StartDate: pointer.Ptr(validDate), + }, + Name: "backup", + ID: pointer.Ptr("backup-id"), + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "", }, }, - Intensity: "666", - SmallTableThreshold: "1GiB", - Parallel: 0, + Location: []string{"gcs:location"}, }, }, - }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, RepairTasks: map[string]scyllav1.RepairTaskStatus{ "repair": { TaskStatus: scyllav1.TaskStatus{ SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), + StartDate: pointer.Ptr(validDate), }, Name: "repair", ID: pointer.Ptr("repair-id"), Labels: map[string]string{ - "scylla-operator.scylladb.com/managed-hash": "QAYSzOPRCIVGqS04NfnFslWujYbjmjwNjH//peQawBxry6I6M/scdCaHR2qgNOL9YJQsGjnD846eO0oULMyJ8A==", + "scylla-operator.scylladb.com/managed-hash": "", }, }, - FailFast: pointer.Ptr(false), - Intensity: pointer.Ptr("666"), + Intensity: pointer.Ptr("0.5"), Parallel: pointer.Ptr[int64](0), SmallTableThreshold: pointer.Ptr("1GiB"), }, }, }, - - Actions: nil, + expectedActions: []action{ + &updateTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + Task: &managerclient.Task{ + ID: "repair-id", + Enabled: true, + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "e6nOWps39EWcHS4BmiEti8MfyH6PL5Yt3kpM4Vej1Rd0SAsCM39CXe/XH1Yjhsi3b611nx7yfK9paShMxaNqGw==", + }, + Name: "repair", + Properties: map[string]any{ + "intensity": float64(0.5), + "parallel": int64(0), + "small_table_threshold": int64(1073741824), + }, + Schedule: &managerclient.Schedule{ + StartDate: validDateTime, + }, + Type: "repair", + }, + }, + &updateTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + Task: &managerclient.Task{ + ID: "backup-id", + Enabled: true, + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "c+YqtlphqYcYveZmyspNBzt77JuW9zNWKMdUPv8AGbP+9j3Gi4KvQqJyBrq5DPFDV6E8rWxcyXHF2mNYFzwIaA==", + }, + Name: "backup", + Properties: map[string]any{ + "location": []string{"gcs:location"}, + "retention": int64(0), + }, + Schedule: &managerclient.Schedule{ + StartDate: validDateTime, + }, + Type: "backup", + }, + }, + }, + expectedRequeue: false, + expectedErr: nil, }, { - Name: "Task gets updated when startDate is changed", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ - Name: "repair", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2006-01-02T15:04:05Z"), - Interval: pointer.Ptr("0"), + name: "matching cluster in state, tasks in state, mismatching managed hash labels, return update task actions", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + BackupTasks: map[string]scyllav1.BackupTaskStatus{ + "backup": { + TaskStatus: scyllav1.TaskStatus{ + SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ + StartDate: pointer.Ptr(validDate), + }, + Name: "backup", + ID: pointer.Ptr("backup-id"), + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "other-managed-hash", }, }, - Intensity: "666", - SmallTableThreshold: "1GiB", - Parallel: 0, + Location: []string{"gcs:location"}, }, }, - }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, RepairTasks: map[string]scyllav1.RepairTaskStatus{ "repair": { TaskStatus: scyllav1.TaskStatus{ SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), + StartDate: pointer.Ptr(validDate), }, Name: "repair", ID: pointer.Ptr("repair-id"), + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "other-managed-hash", + }, }, - FailFast: pointer.Ptr(false), - Intensity: pointer.Ptr("666"), + Intensity: pointer.Ptr("0.5"), Parallel: pointer.Ptr[int64](0), SmallTableThreshold: pointer.Ptr("1GiB"), }, }, }, - - Actions: []action{&updateTaskAction{clusterID: clusterID, task: &managerclient.Task{ID: "repair-id"}}}, - }, - { - Name: "Task gets updated when managed hash is missing", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ - Name: "repair", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), - }, + expectedActions: []action{ + &updateTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + Task: &managerclient.Task{ + ID: "repair-id", + Enabled: true, + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "e6nOWps39EWcHS4BmiEti8MfyH6PL5Yt3kpM4Vej1Rd0SAsCM39CXe/XH1Yjhsi3b611nx7yfK9paShMxaNqGw==", }, - SmallTableThreshold: "1GiB", - Intensity: "666", - Parallel: 0, + Name: "repair", + Properties: map[string]any{ + "intensity": float64(0.5), + "parallel": int64(0), + "small_table_threshold": int64(1073741824), + }, + Schedule: &managerclient.Schedule{ + StartDate: validDateTime, + }, + Type: "repair", }, }, - }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, - RepairTasks: map[string]scyllav1.RepairTaskStatus{ - "repair": { - TaskStatus: scyllav1.TaskStatus{ - SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), - }, - Name: "repair", - ID: pointer.Ptr("repair-id"), - Labels: map[string]string{}, + &updateTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + Task: &managerclient.Task{ + ID: "backup-id", + Enabled: true, + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "c+YqtlphqYcYveZmyspNBzt77JuW9zNWKMdUPv8AGbP+9j3Gi4KvQqJyBrq5DPFDV6E8rWxcyXHF2mNYFzwIaA==", }, - FailFast: pointer.Ptr(false), - Intensity: pointer.Ptr("666"), - Parallel: pointer.Ptr[int64](0), - SmallTableThreshold: pointer.Ptr("1GiB"), + Name: "backup", + Properties: map[string]any{ + "location": []string{"gcs:location"}, + "retention": int64(0), + }, + Schedule: &managerclient.Schedule{ + StartDate: validDateTime, + }, + Type: "backup", }, }, }, - Actions: []action{&updateTaskAction{clusterID: clusterID, task: &managerclient.Task{ID: "repair-id"}}}, + expectedRequeue: false, + expectedErr: nil, }, { - Name: "Task gets updated when managed hash is empty", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ - Name: "repair", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), + name: "matching cluster in state, tasks in state, matching managed hash labels, return no actions", + sc: newBasicScyllaClusterWithBackupAndRepairTasks(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + BackupTasks: map[string]scyllav1.BackupTaskStatus{ + "backup": { + TaskStatus: scyllav1.TaskStatus{ + SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ + StartDate: pointer.Ptr(validDate), + }, + Name: "backup", + ID: pointer.Ptr("backup-id"), + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "c+YqtlphqYcYveZmyspNBzt77JuW9zNWKMdUPv8AGbP+9j3Gi4KvQqJyBrq5DPFDV6E8rWxcyXHF2mNYFzwIaA==", }, }, - SmallTableThreshold: "1GiB", - Intensity: "666", - Parallel: 0, + Location: []string{"gcs:location"}, }, }, - }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, RepairTasks: map[string]scyllav1.RepairTaskStatus{ "repair": { TaskStatus: scyllav1.TaskStatus{ SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), + StartDate: pointer.Ptr(validDate), }, Name: "repair", ID: pointer.Ptr("repair-id"), Labels: map[string]string{ - "scylla-operator.scylladb.com/managed-hash": "", + "scylla-operator.scylladb.com/managed-hash": "e6nOWps39EWcHS4BmiEti8MfyH6PL5Yt3kpM4Vej1Rd0SAsCM39CXe/XH1Yjhsi3b611nx7yfK9paShMxaNqGw==", }, }, - FailFast: pointer.Ptr(false), - Intensity: pointer.Ptr("666"), + Intensity: pointer.Ptr("0.5"), Parallel: pointer.Ptr[int64](0), SmallTableThreshold: pointer.Ptr("1GiB"), }, }, }, - Actions: []action{&updateTaskAction{clusterID: clusterID, task: &managerclient.Task{ID: "repair-id"}}}, + expectedActions: nil, + expectedRequeue: false, + expectedErr: nil, }, { - Name: "Task gets updated when managed hash does not match", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{ + name: "matching cluster in state, tasks in state, tasks with 'now' startDate and matching managed hash labels, return no actions", + sc: func() *scyllav1.ScyllaCluster { + sc := newBasicScyllaCluster() + + sc.Spec.Backups = []scyllav1.BackupTaskSpec{ + { + TaskSpec: scyllav1.TaskSpec{ + Name: "backup", + SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ + StartDate: pointer.Ptr("now"), + }, + }, + Location: []string{"gcs:location"}, + }, + } + + sc.Spec.Repairs = []scyllav1.RepairTaskSpec{ { TaskSpec: scyllav1.TaskSpec{ Name: "repair", SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), + StartDate: pointer.Ptr("now"), }, }, SmallTableThreshold: "1GiB", - Intensity: "666", - Parallel: 0, + Intensity: "0.5", + }, + } + + return sc + }(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", + }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + }, + BackupTasks: map[string]scyllav1.BackupTaskStatus{ + "backup": { + TaskStatus: scyllav1.TaskStatus{ + SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ + StartDate: pointer.Ptr(validDate), + }, + Name: "backup", + ID: pointer.Ptr("backup-id"), + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "0aeio9mEaSKenZ7/GRW4l0TFm7f9kY2w7A3wpO3du5+EfjM9zIqJunon9vT+VvGNcwYxQQqF4gmZW1GSLpZU6g==", + }, + }, + Location: []string{"gcs:location"}, }, }, - }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, RepairTasks: map[string]scyllav1.RepairTaskStatus{ "repair": { TaskStatus: scyllav1.TaskStatus{ SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - Interval: pointer.Ptr("0"), + StartDate: pointer.Ptr(validDate), }, Name: "repair", ID: pointer.Ptr("repair-id"), Labels: map[string]string{ - "scylla-operator.scylladb.com/managed-hash": "non-matching-hash", + "scylla-operator.scylladb.com/managed-hash": "Il1bCvnHQAs7fjjuE4OHn6Au6tvMxNsVrfvdbwg3uYeNZPzYR+qgvDyAEjVrwakzfOTu/e+viMTWzaCRLqYetA==", }, }, - FailFast: pointer.Ptr(false), - Intensity: pointer.Ptr("666"), + Intensity: pointer.Ptr("0.5"), Parallel: pointer.Ptr[int64](0), SmallTableThreshold: pointer.Ptr("1GiB"), }, }, }, - Actions: []action{&updateTaskAction{clusterID: clusterID, task: &managerclient.Task{ID: "repair-id"}}}, + expectedActions: nil, + expectedRequeue: false, + expectedErr: nil, }, { - Name: "Tasks do not get updated when in-manager state differs but managed hashes match specs", - Spec: scyllav1.ScyllaClusterSpec{ - Repairs: []scyllav1.RepairTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ - Name: "repair", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), - }, - }, - SmallTableThreshold: "10GiB", - Intensity: "666", - Parallel: 3, + name: "matching cluster in state, superfluous tasks in state, return delete task actions", + sc: newBasicScyllaCluster(), + authToken: "token", + state: &managerClusterState{ + Cluster: &managerclient.Cluster{ + AuthToken: "token", + ForceNonSslSessionPort: true, + ForceTLSDisabled: true, + Host: "test-client.test.svc", + Labels: map[string]string{ + "scylla-operator.scylladb.com/owner-uid": "1bbeb48b-101f-4d49-8ba4-67adc9878721", + "scylla-operator.scylladb.com/managed-hash": "UfHEc1kxt3UHl1r2ETXinXfhAtyYrha5RRJn544zkvY6bLDyzl1Q7wSskU355iHlIuwFHyeePE80I0ZOSmoChA==", }, + Name: "test/test", + ID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", }, - Backups: []scyllav1.BackupTaskSpec{ - { - TaskSpec: scyllav1.TaskSpec{ + BackupTasks: map[string]scyllav1.BackupTaskStatus{ + "backup": { + TaskStatus: scyllav1.TaskStatus{ + SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ + StartDate: pointer.Ptr(validDate), + }, Name: "backup", - SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ - StartDate: pointer.Ptr("2021-01-01T11:11:11Z"), + ID: pointer.Ptr("backup-id"), + Labels: map[string]string{ + "scylla-operator.scylladb.com/managed-hash": "0aeio9mEaSKenZ7/GRW4l0TFm7f9kY2w7A3wpO3du5+EfjM9zIqJunon9vT+VvGNcwYxQQqF4gmZW1GSLpZU6g==", }, }, - Location: []string{"s3:backup"}, - Retention: 3, + Location: []string{"gcs:location"}, }, }, - }, - Status: scyllav1.ScyllaClusterStatus{ - ManagerID: pointer.Ptr(clusterID), - }, - State: state{ - Clusters: []*managerclient.Cluster{{ - ID: clusterID, - Name: clusterName, - AuthToken: clusterAuthToken, - }}, RepairTasks: map[string]scyllav1.RepairTaskStatus{ "repair": { TaskStatus: scyllav1.TaskStatus{ SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2006-01-02T15:04:05Z"), + StartDate: pointer.Ptr(validDate), }, Name: "repair", ID: pointer.Ptr("repair-id"), Labels: map[string]string{ - "scylla-operator.scylladb.com/managed-hash": "JcrUPldfq9/FT/tAaXzdY2aclZFsjTlRsYDh7LEjM3K5XRbl8w+jUGvZdBHRRSZ28TdWu2dsa/L5LBxxWjIpHw==", + "scylla-operator.scylladb.com/managed-hash": "Il1bCvnHQAs7fjjuE4OHn6Au6tvMxNsVrfvdbwg3uYeNZPzYR+qgvDyAEjVrwakzfOTu/e+viMTWzaCRLqYetA==", }, }, + Intensity: pointer.Ptr("0.5"), + Parallel: pointer.Ptr[int64](0), SmallTableThreshold: pointer.Ptr("1GiB"), - Intensity: pointer.Ptr("1"), - Parallel: pointer.Ptr[int64](1), }, }, - BackupTasks: map[string]scyllav1.BackupTaskStatus{ - "backup": { - TaskStatus: scyllav1.TaskStatus{ - SchedulerTaskStatus: scyllav1.SchedulerTaskStatus{ - StartDate: pointer.Ptr("2006-01-02T15:04:05Z"), - }, - Name: "backup", - ID: pointer.Ptr("backup-id"), - Labels: map[string]string{ - "scylla-operator.scylladb.com/managed-hash": "NpboZYiDmzZfS84omU0kgZxxDzg5p3IEhKCWU0sS6G0QpSh2KZFPHB7AlJohyqo+RjBG2aEIqbBW8GiDo18uxw==", - }, - }, - Location: []string{"s3:other-backup"}, - Retention: pointer.Ptr[int64](1), - }, + }, + expectedActions: []action{ + &deleteTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + TaskType: "repair", + TaskID: "repair-id", + }, + &deleteTaskAction{ + ClusterID: "bead6247-d9e4-401c-84b4-ad0bffe36eac", + TaskType: "backup", + TaskID: "backup-id", }, }, - Actions: nil, + expectedRequeue: false, + expectedErr: nil, }, } - for _, test := range tcs { - t.Run(test.Name, func(t *testing.T) { + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + ctx := context.Background() - cluster := &scyllav1.ScyllaCluster{ - ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, - Spec: test.Spec, - Status: test.Status, + actions, requeue, err := runSync(ctx, tc.sc, tc.authToken, tc.state) + if !reflect.DeepEqual(err, tc.expectedErr) { + t.Fatalf("expected and got errors differ:\n%s", cmp.Diff(tc.expectedErr, err, cmpopts.EquateErrors())) } - actions, requeue, err := runSync(ctx, cluster, clusterAuthToken, &test.State) - if err != nil { - t.Error(err) + if requeue != tc.expectedRequeue { + t.Errorf("expected requeue %t, got %t", tc.expectedRequeue, requeue) } - if requeue != test.Requeue { - t.Error(err, "Unexpected requeue") - } - if !cmp.Equal(actions, test.Actions, cmp.Comparer(actionComparer)) { - t.Error(err, "Unexpected actions", cmp.Diff(actions, test.Actions, cmp.Comparer(actionComparer))) + if !reflect.DeepEqual(actions, tc.expectedActions) { + t.Errorf("expected and got actions differ:\n%s", cmp.Diff(tc.expectedActions, actions)) } }) } } -func actionComparer(a action, b action) bool { - switch va := a.(type) { - case *addClusterAction: - vb := b.(*addClusterAction) - return va.cluster.Name == vb.cluster.Name - case *updateClusterAction: - vb := b.(*updateClusterAction) - return va.cluster.ID == vb.cluster.ID - case *deleteClusterAction: - vb := b.(*deleteClusterAction) - return va.clusterID == vb.clusterID - case *updateTaskAction: - vb := b.(*updateTaskAction) - return va.clusterID == vb.clusterID && va.task.ID == vb.task.ID - case *addTaskAction: - vb := b.(*addTaskAction) - return va.clusterID == vb.clusterID && va.task.Name == vb.task.Name - case *deleteTaskAction: - vb := b.(*deleteTaskAction) - return va.clusterID == vb.clusterID && va.taskID == vb.taskID - default: - } - return false -} +func Test_evaluateDates(t *testing.T) { + t.Parallel() -func TestEvaluateDates(t *testing.T) { - ts := []struct { + tt := []struct { name string spec *scyllav1.TaskSpec status *scyllav1.TaskStatus @@ -750,15 +904,58 @@ func TestEvaluateDates(t *testing.T) { }, } - for _, tc := range ts { + for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { t.Parallel() evaluateDates(tc.spec, tc.status) got := taskSpecToStatus(tc.spec) if !reflect.DeepEqual(got, tc.expected) { - t.Errorf("expected and got repair task statuses differ: %s", cmp.Diff(tc.expected, got)) + t.Errorf("expected and got repair task statuses differ:\n%s", cmp.Diff(tc.expected, got)) } }) } } + +func newBasicScyllaClusterWithBackupAndRepairTasks() *scyllav1.ScyllaCluster { + sc := newBasicScyllaCluster() + + sc.Spec.Backups = []scyllav1.BackupTaskSpec{ + { + TaskSpec: scyllav1.TaskSpec{ + Name: "backup", + SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ + StartDate: pointer.Ptr(validDate), + }, + }, + Location: []string{"gcs:location"}, + }, + } + + sc.Spec.Repairs = []scyllav1.RepairTaskSpec{ + { + TaskSpec: scyllav1.TaskSpec{ + Name: "repair", + SchedulerTaskSpec: scyllav1.SchedulerTaskSpec{ + StartDate: pointer.Ptr(validDate), + }, + }, + SmallTableThreshold: "1GiB", + Intensity: "0.5", + }, + } + + return sc +} + +func newBasicScyllaCluster() *scyllav1.ScyllaCluster { + return &scyllav1.ScyllaCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + UID: "1bbeb48b-101f-4d49-8ba4-67adc9878721", + }, + Spec: scyllav1.ScyllaClusterSpec{}, + Status: scyllav1.ScyllaClusterStatus{}, + } +}