From c8c8249a4c3b11e3e0ff78ebf5c03c9e0d7292cb Mon Sep 17 00:00:00 2001 From: casey-coreweave Date: Mon, 9 Mar 2026 13:14:56 -0700 Subject: [PATCH] fix(mysql): Gate mysql init on actual cluster online status --- internal/controller/infra/mysql/mysql/read.go | 53 ++++++++++---- .../controller/infra/mysql/mysql/read_test.go | 73 +++++++++++++++++++ 2 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 internal/controller/infra/mysql/mysql/read_test.go diff --git a/internal/controller/infra/mysql/mysql/read.go b/internal/controller/infra/mysql/mysql/read.go index 1b20b46e..64d9195e 100644 --- a/internal/controller/infra/mysql/mysql/read.go +++ b/internal/controller/infra/mysql/mysql/read.go @@ -2,8 +2,10 @@ package mysql import ( "context" + "encoding/json" "fmt" "strconv" + "strings" ctrlcommon "github.com/wandb/operator/internal/controller/common" "github.com/wandb/operator/internal/controller/translator" @@ -126,21 +128,21 @@ func computeMySQLReportedReadyCondition(_ context.Context, clusterCR *v2.InnoDBC return []metav1.Condition{} } - status := metav1.ConditionUnknown - reason := "Unknown" - - // Map InnoDBCluster status to conditions. - // InnoDBClusterStatus.Status is a RawExtension, but we can check if it's ready - // based on the documented behavior or by looking at the actual status if we had more info. - // For now, we'll assume it's ready if we can find it, or try to infer from common patterns. - // Actually, looking at innodbcluster_types.go, Status is runtime.RawExtension. - // We might need to unmarshal it or find another way to check readiness. - - // If we don't have a clear way to check readiness from the Go types yet, - // we might just mark it as True if found for now, or look for standard conditions. - - status = metav1.ConditionTrue - reason = "Online" + status := metav1.ConditionFalse + reason := ctrlcommon.NoResourceReason + + clusterStatus := extractInnoDBClusterStatus(clusterCR) + switch { + case strings.EqualFold(clusterStatus, "ONLINE"): + status = metav1.ConditionTrue + reason = "Online" + case clusterStatus != "": + status = metav1.ConditionFalse + reason = clusterStatus + default: + status = metav1.ConditionFalse + reason = ctrlcommon.NoResourceReason + } return []metav1.Condition{ { @@ -150,3 +152,24 @@ func computeMySQLReportedReadyCondition(_ context.Context, clusterCR *v2.InnoDBC }, } } + +func extractInnoDBClusterStatus(clusterCR *v2.InnoDBCluster) string { + if clusterCR == nil || len(clusterCR.Status.Raw) == 0 { + return "" + } + + var raw map[string]any + if err := json.Unmarshal(clusterCR.Status.Raw, &raw); err != nil { + return "" + } + + cluster, ok := raw["cluster"].(map[string]any) + if !ok { + return "" + } + value, ok := cluster["status"].(string) + if !ok { + return "" + } + return value +} diff --git a/internal/controller/infra/mysql/mysql/read_test.go b/internal/controller/infra/mysql/mysql/read_test.go new file mode 100644 index 00000000..7448ee5c --- /dev/null +++ b/internal/controller/infra/mysql/mysql/read_test.go @@ -0,0 +1,73 @@ +package mysql + +import ( + "context" + "testing" + + "github.com/wandb/operator/internal/controller/common" + "github.com/wandb/operator/pkg/vendored/mysql-operator/v2" + "k8s.io/apimachinery/pkg/runtime" +) + +func TestComputeMySQLReportedReadyConditionOnline(t *testing.T) { + cluster := &v2.InnoDBCluster{ + Status: v2.InnoDBClusterStatus{ + RawExtension: runtime.RawExtension{ + Raw: []byte(`{"cluster":{"status":"ONLINE"}}`), + }, + }, + } + + conditions := computeMySQLReportedReadyCondition(context.Background(), cluster) + if len(conditions) != 1 { + t.Fatalf("expected one condition, got %d", len(conditions)) + } + if conditions[0].Status != "True" { + t.Fatalf("expected ready condition true, got %s", conditions[0].Status) + } + if conditions[0].Reason != "Online" { + t.Fatalf("expected reason Online, got %s", conditions[0].Reason) + } +} + +func TestComputeMySQLReportedReadyConditionPending(t *testing.T) { + cluster := &v2.InnoDBCluster{ + Status: v2.InnoDBClusterStatus{ + RawExtension: runtime.RawExtension{ + Raw: []byte(`{"cluster":{"status":"PENDING"}}`), + }, + }, + } + + conditions := computeMySQLReportedReadyCondition(context.Background(), cluster) + if len(conditions) != 1 { + t.Fatalf("expected one condition, got %d", len(conditions)) + } + if conditions[0].Status != "False" { + t.Fatalf("expected ready condition false, got %s", conditions[0].Status) + } + if conditions[0].Reason != "PENDING" { + t.Fatalf("expected reason PENDING, got %s", conditions[0].Reason) + } +} + +func TestComputeMySQLReportedReadyConditionMissingStatus(t *testing.T) { + cluster := &v2.InnoDBCluster{ + Status: v2.InnoDBClusterStatus{ + RawExtension: runtime.RawExtension{ + Raw: []byte(`{"kopf":{"progress":{}}}`), + }, + }, + } + + conditions := computeMySQLReportedReadyCondition(context.Background(), cluster) + if len(conditions) != 1 { + t.Fatalf("expected one condition, got %d", len(conditions)) + } + if conditions[0].Status != "False" { + t.Fatalf("expected ready condition false, got %s", conditions[0].Status) + } + if conditions[0].Reason != common.NoResourceReason { + t.Fatalf("expected reason %s, got %s", common.NoResourceReason, conditions[0].Reason) + } +}