diff --git a/config/crds/v1/schemas.schemahero.io_tables.yaml b/config/crds/v1/schemas.schemahero.io_tables.yaml index e90825fec..2ed692863 100644 --- a/config/crds/v1/schemas.schemahero.io_tables.yaml +++ b/config/crds/v1/schemas.schemahero.io_tables.yaml @@ -272,6 +272,14 @@ spec: type: object status: description: TableStatus defines the observed state of Table + properties: + lastPlannedTableSpecSHA: + description: We store the SHA of the table spec from the last time + we executed a plan to make startup less noisy by skipping re-planning + objects that have been planned we cannot use the resourceVersion + or generation fields because updating them would cause the object + to be modified again + type: string type: object type: object served: true diff --git a/config/crds/v1beta1/schemas.schemahero.io_tables.yaml b/config/crds/v1beta1/schemas.schemahero.io_tables.yaml index 248241907..b829e36de 100644 --- a/config/crds/v1beta1/schemas.schemahero.io_tables.yaml +++ b/config/crds/v1beta1/schemas.schemahero.io_tables.yaml @@ -270,6 +270,14 @@ spec: type: object status: description: TableStatus defines the observed state of Table + properties: + lastPlannedTableSpecSHA: + description: We store the SHA of the table spec from the last time we + executed a plan to make startup less noisy by skipping re-planning + objects that have been planned we cannot use the resourceVersion or + generation fields because updating them would cause the object to + be modified again + type: string type: object type: object version: v1alpha4 diff --git a/pkg/apis/schemas/v1alpha4/table_types.go b/pkg/apis/schemas/v1alpha4/table_types.go index af4af8828..cd4c6a458 100644 --- a/pkg/apis/schemas/v1alpha4/table_types.go +++ b/pkg/apis/schemas/v1alpha4/table_types.go @@ -42,6 +42,11 @@ type TableSpec struct { // TableStatus defines the observed state of Table type TableStatus struct { + // We store the SHA of the table spec from the last time we executed a plan to + // make startup less noisy by skipping re-planning objects that have been planned + // we cannot use the resourceVersion or generation fields because updating them + // would cause the object to be modified again + LastPlannedTableSpecSHA string `json:"lastPlannedTableSpecSHA,omitempty" yaml:"lastPlannedTableSpecSHA,omitempty"` } // +genclient @@ -71,7 +76,7 @@ func (t Table) GetSHA() (string, error) { } sum := sha256.Sum256(b) - return fmt.Sprintf("%x", sum)[:7], nil + return fmt.Sprintf("%x", sum), nil } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/controller/table/reconcile_pod.go b/pkg/controller/table/reconcile_pod.go index cf95a19ad..96baefe46 100644 --- a/pkg/controller/table/reconcile_pod.go +++ b/pkg/controller/table/reconcile_pod.go @@ -111,6 +111,8 @@ func (r *ReconcileTable) reconcilePod(ctx context.Context, pod *corev1.Pod) (rec return reconcile.Result{}, errors.Wrap(err, "failed to get sha of table") } + tableSHA = tableSHA[:7] + desiredMigration := schemasv1alpha4.Migration{ TypeMeta: metav1.TypeMeta{ APIVersion: "schemas.schemahero.io/v1alpha4", diff --git a/pkg/controller/table/reconile_table.go b/pkg/controller/table/reconile_table.go index c144b2567..7c281672e 100644 --- a/pkg/controller/table/reconile_table.go +++ b/pkg/controller/table/reconile_table.go @@ -23,7 +23,17 @@ func (r *ReconcileTable) reconcileTable(ctx context.Context, instance *schemasv1 logger.Debug("reconciling table", zap.String("kind", instance.Kind), zap.String("name", instance.Name), - zap.String("database", instance.Spec.Database)) + zap.String("database", instance.Spec.Database), + zap.String("lastPlannedTableSpecSHA", instance.Status.LastPlannedTableSpecSHA)) + + // early exit if the sha of the spec hasn't changed + currentTableSpecSHA, err := instance.GetSHA() + if err != nil { + return reconcile.Result{}, errors.Wrap(err, "failed to get instance sha") + } + if instance.Status.LastPlannedTableSpecSHA == currentTableSpecSHA { + return reconcile.Result{}, nil + } // get the full database spec from the api database, err := r.getDatabaseSpec(ctx, instance.Namespace, instance.Spec.Database) @@ -57,6 +67,8 @@ func (r *ReconcileTable) reconcileTable(ctx context.Context, instance *schemasv1 return reconcile.Result{}, errors.Wrap(err, "failed to get table sha") } + tableSHA = tableSHA[:7] + migration, err := r.getMigrationSpec(instance.Namespace, tableSHA) if err != nil { return reconcile.Result{}, errors.Wrap(err, "failed to get migration spec") @@ -254,5 +266,16 @@ func (r *ReconcileTable) deployMigrationPlanPhase(ctx context.Context, database return reconcile.Result{}, errors.Wrap(err, "failed to check if pod exists") } + // update the status with this plan so we don't reconcile it again + newTableSpecSHA, err := table.GetSHA() + if err != nil { + return reconcile.Result{}, errors.Wrap(err, "failed to get sha") + } + table.Status.LastPlannedTableSpecSHA = newTableSpecSHA + + if err := r.Update(ctx, table); err != nil { + return reconcile.Result{}, errors.Wrap(err, "failed to update status") + } + return reconcile.Result{}, nil } diff --git a/pkg/installer/table_objects.go b/pkg/installer/table_objects.go index 0cfd8f21c..539047555 100644 --- a/pkg/installer/table_objects.go +++ b/pkg/installer/table_objects.go @@ -272,6 +272,14 @@ spec: type: object status: description: TableStatus defines the observed state of Table + properties: + lastPlannedTableSpecSHA: + description: We store the SHA of the table spec from the last time we + executed a plan to make startup less noisy by skipping re-planning + objects that have been planned we cannot use the resourceVersion or + generation fields because updating them would cause the object to + be modified again + type: string type: object type: object version: v1alpha4 @@ -560,6 +568,14 @@ spec: type: object status: description: TableStatus defines the observed state of Table + properties: + lastPlannedTableSpecSHA: + description: We store the SHA of the table spec from the last time + we executed a plan to make startup less noisy by skipping re-planning + objects that have been planned we cannot use the resourceVersion + or generation fields because updating them would cause the object + to be modified again + type: string type: object type: object served: true