From 134aaba0cb5ee398fe05f658f1890d42195bf714 Mon Sep 17 00:00:00 2001 From: David Eads Date: Wed, 6 May 2026 11:35:17 -0400 Subject: [PATCH] read new cluster content --- .../server/handlers/hcp/serialconsole_test.go | 6 ++ .../create_billing_doc_test.go | 3 + .../orphaned_billing_cleanup_test.go | 3 + .../cluster_properties_sync_test.go | 3 + ...aestro_readonly_bundles_controller_test.go | 18 ++++++ .../datadumpcontrollers/cs_state_dump_test.go | 3 + ...aestro_readonly_bundles_controller_test.go | 54 ++++++++++++++++ .../maestro_readonly_bundle_helpers_test.go | 6 ++ .../resource_metrics_controller_test.go | 3 + .../operationcontrollers/test_helpers_test.go | 9 +++ ...eadonly_bundles_content_controller_test.go | 12 ++++ ...ol_plane_active_version_controller_test.go | 3 + ...l_plane_desired_version_controller_test.go | 6 ++ .../nodepool_version_controller_test.go | 3 + backend/pkg/informers/informers_test.go | 9 +++ .../pkg/listertesting/slice_listers_test.go | 3 + frontend/pkg/frontend/cluster.go | 8 ++- frontend/pkg/frontend/frontend_test.go | 6 ++ frontend/pkg/frontend/helpers_test.go | 6 ++ internal/api/types_cluster.go | 19 +----- .../v20240610preview/conversion_fuzz_test.go | 9 ++- .../hcpopenshiftclusters_methods.go | 5 +- .../v20251223preview/conversion_fuzz_test.go | 3 - .../hcpopenshiftclusters_methods.go | 5 +- .../zero_value_roundtrip_test.go | 3 + internal/api/zz_generated.deepcopy.go | 1 + internal/conversion/readonly_cluster.go | 5 +- internal/database/convert_cluster.go | 64 +++++++------------ internal/database/convert_cluster_test.go | 15 ++++- .../convert_defaults_consistency_test.go | 42 ++++++------ internal/database/types_hcpcluster.go | 12 ++-- .../databasetesting/mock_dbclient_test.go | 15 +++++ internal/serverutils/dump_billing_test.go | 15 +++++ .../backend/launch/metrics_test.go | 3 + test-integration/frontend/informer_test.go | 6 ++ 35 files changed, 282 insertions(+), 104 deletions(-) diff --git a/admin/server/handlers/hcp/serialconsole_test.go b/admin/server/handlers/hcp/serialconsole_test.go index 08909d85c01..fc3ecd91042 100644 --- a/admin/server/handlers/hcp/serialconsole_test.go +++ b/admin/server/handlers/hcp/serialconsole_test.go @@ -94,6 +94,9 @@ func TestSerialConsoleHandler(t *testing.T) { internalID, err := api.NewInternalID("/api/clusters_mgmt/v1/clusters/test-cluster-id") require.NoError(t, err) hcp := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: resourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ID: resourceID}, }, @@ -117,6 +120,9 @@ func TestSerialConsoleHandler(t *testing.T) { internalID, err := api.NewInternalID("/api/clusters_mgmt/v1/clusters/test-cluster-id") require.NoError(t, err) hcp := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: resourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ID: resourceID}, }, diff --git a/backend/pkg/controllers/billingcontrollers/create_billing_doc_test.go b/backend/pkg/controllers/billingcontrollers/create_billing_doc_test.go index c0f77a560d9..beebb489e1d 100644 --- a/backend/pkg/controllers/billingcontrollers/create_billing_doc_test.go +++ b/backend/pkg/controllers/billingcontrollers/create_billing_doc_test.go @@ -83,6 +83,9 @@ func newTestSubscription() *arm.Subscription { func newTestCluster(t *testing.T, clusterUID string, provisioningState arm.ProvisioningState, createdAt *time.Time) *api.HCPOpenShiftCluster { t.Helper() return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: newTestClusterResourceID(t), + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: newTestClusterResourceID(t), diff --git a/backend/pkg/controllers/billingcontrollers/orphaned_billing_cleanup_test.go b/backend/pkg/controllers/billingcontrollers/orphaned_billing_cleanup_test.go index 6806ee67b25..3531e971f84 100644 --- a/backend/pkg/controllers/billingcontrollers/orphaned_billing_cleanup_test.go +++ b/backend/pkg/controllers/billingcontrollers/orphaned_billing_cleanup_test.go @@ -126,6 +126,9 @@ func TestOrphanedBillingCleanup_SyncOnce(t *testing.T) { clusters: []*api.HCPOpenShiftCluster{ // Only cluster-2 exists { + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: api.Must(azcorearm.ParseResourceID("/subscriptions/" + testSubscriptionID + "/resourceGroups/" + testResourceGroupName + "/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster-2")), + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: api.Must(azcorearm.ParseResourceID("/subscriptions/" + testSubscriptionID + "/resourceGroups/" + testResourceGroupName + "/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster-2")), diff --git a/backend/pkg/controllers/clusterpropertiescontroller/cluster_properties_sync_test.go b/backend/pkg/controllers/clusterpropertiescontroller/cluster_properties_sync_test.go index d43bf23c66f..142c7a8ad4a 100644 --- a/backend/pkg/controllers/clusterpropertiescontroller/cluster_properties_sync_test.go +++ b/backend/pkg/controllers/clusterpropertiescontroller/cluster_properties_sync_test.go @@ -330,6 +330,9 @@ func newTestCluster(opts ...func(*api.HCPOpenShiftCluster)) *api.HCPOpenShiftClu )) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: resourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: resourceID, diff --git a/backend/pkg/controllers/create_cluster_scoped_maestro_readonly_bundles_controller_test.go b/backend/pkg/controllers/create_cluster_scoped_maestro_readonly_bundles_controller_test.go index 7432b1396b9..d6b6eae3c27 100644 --- a/backend/pkg/controllers/create_cluster_scoped_maestro_readonly_bundles_controller_test.go +++ b/backend/pkg/controllers/create_cluster_scoped_maestro_readonly_bundles_controller_test.go @@ -129,6 +129,9 @@ func TestBuildInitialReadonlyMaestroBundleForHostedCluster(t *testing.T) { clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -444,6 +447,9 @@ func TestCreateClusterScopedMaestroReadonlyBundlesSyncer_syncMaestroBundle(t *te } ctx := context.Background() cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ID: clusterResourceID}, }, @@ -584,6 +590,9 @@ func TestCreateClusterScopedMaestroReadonlyBundlesSyncer_SyncOnce_GetServiceProv clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/11111111111111111111111111111111"))), @@ -634,6 +643,9 @@ func TestCreateClusterScopedMaestroReadonlyBundlesSyncer_SyncOnce_AllBundlesAlre clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ID: clusterResourceID}, }, @@ -703,6 +715,9 @@ func TestCreateClusterScopedMaestroReadonlyBundlesSyncer_SyncOnce_SyncLoopExecut // Setup cluster clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -816,6 +831,9 @@ func TestCreateClusterScopedMaestroReadonlyBundlesSyncer_SyncOnce_ProcessesParti clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ID: clusterResourceID}, }, diff --git a/backend/pkg/controllers/datadumpcontrollers/cs_state_dump_test.go b/backend/pkg/controllers/datadumpcontrollers/cs_state_dump_test.go index 8cf271831b1..61a82e059c9 100644 --- a/backend/pkg/controllers/datadumpcontrollers/cs_state_dump_test.go +++ b/backend/pkg/controllers/datadumpcontrollers/cs_state_dump_test.go @@ -173,6 +173,9 @@ func TestCSStateDump_SyncOnce(t *testing.T) { if tt.createCluster { clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ID: clusterResourceID}, }, diff --git a/backend/pkg/controllers/delete_orphaned_maestro_readonly_bundles_controller_test.go b/backend/pkg/controllers/delete_orphaned_maestro_readonly_bundles_controller_test.go index 4b926a04108..7acedcb7911 100644 --- a/backend/pkg/controllers/delete_orphaned_maestro_readonly_bundles_controller_test.go +++ b/backend/pkg/controllers/delete_orphaned_maestro_readonly_bundles_controller_test.go @@ -802,6 +802,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_mapServiceProviderClustersByProvis mockResourcesDBClient := databasetesting.NewMockResourcesDBClient() mockCS := ocm.NewMockClusterServiceClientSpec(ctrl) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -826,6 +829,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_mapServiceProviderClustersByProvis mockResourcesDBClient := databasetesting.NewMockResourcesDBClient() mockCS := ocm.NewMockClusterServiceClientSpec(ctrl) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -863,6 +869,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_mapServiceProviderClustersByProvis mockResourcesDBClient := databasetesting.NewMockResourcesDBClient() mockCS := ocm.NewMockClusterServiceClientSpec(ctrl) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -895,12 +904,18 @@ func TestDeleteOrphanedMaestroReadonlyBundles_mapServiceProviderClustersByProvis cluster1ResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster1")) cluster2ResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub2/resourceGroups/rg2/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster2")) cluster1 := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: cluster1ResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: cluster1ResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid1"))), }, } cluster2 := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: cluster2ResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: cluster2ResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid2"))), @@ -1106,6 +1121,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_mapServiceProviderNodePoolsByProvi mockResourcesDBClient := databasetesting.NewMockResourcesDBClient() mockCS := ocm.NewMockClusterServiceClientSpec(ctrl) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1130,6 +1148,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_mapServiceProviderNodePoolsByProvi mockResourcesDBClient := databasetesting.NewMockResourcesDBClient() mockCS := ocm.NewMockClusterServiceClientSpec(ctrl) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1167,6 +1188,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_mapServiceProviderNodePoolsByProvi mockResourcesDBClient := databasetesting.NewMockResourcesDBClient() mockCS := ocm.NewMockClusterServiceClientSpec(ctrl) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1199,12 +1223,18 @@ func TestDeleteOrphanedMaestroReadonlyBundles_mapServiceProviderNodePoolsByProvi cluster1ResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster1")) cluster2ResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub2/resourceGroups/rg2/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster2")) cluster1 := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: cluster1ResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: cluster1ResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid1"))), }, } cluster2 := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: cluster2ResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: cluster2ResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid2"))), @@ -1346,6 +1376,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_ensureClusterScopedOrphanedMaestro setupMock: func(t *testing.T, m *maestro.MockClient, mockResourcesDBClient *databasetesting.MockResourcesDBClient, mockCS *ocm.MockClusterServiceClientSpec, shard *arohcpv1alpha1.ProvisionShard) map[string]*shardMaestroClient { clusterRID := spcResourceID.Parent cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterRID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterRID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1386,6 +1419,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_ensureClusterScopedOrphanedMaestro setupMock: func(t *testing.T, m *maestro.MockClient, mockResourcesDBClient *databasetesting.MockResourcesDBClient, mockCS *ocm.MockClusterServiceClientSpec, shard *arohcpv1alpha1.ProvisionShard) map[string]*shardMaestroClient { clusterRID := spcResourceID.Parent cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterRID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterRID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1567,6 +1603,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_ensureOrphanedNodePoolScopedMaestr setupMock: func(t *testing.T, m *maestro.MockClient, mockResourcesDBClient *databasetesting.MockResourcesDBClient, mockCS *ocm.MockClusterServiceClientSpec, shard *arohcpv1alpha1.ProvisionShard) map[string]*shardMaestroClient { clusterRID := spnpResourceID.Parent.Parent cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterRID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterRID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1607,6 +1646,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_ensureOrphanedNodePoolScopedMaestr setupMock: func(t *testing.T, m *maestro.MockClient, mockResourcesDBClient *databasetesting.MockResourcesDBClient, mockCS *ocm.MockClusterServiceClientSpec, shard *arohcpv1alpha1.ProvisionShard) map[string]*shardMaestroClient { clusterRID := spnpResourceID.Parent.Parent cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterRID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterRID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1695,6 +1737,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_ensureClusterScopedOrphanedMaestro spcOnShard1ResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub/resourceGroups/rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster1/serviceProviderClusters/default")) clusterRID := spcOnShard1ResourceID.Parent cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterRID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterRID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1777,6 +1822,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_ensureClusterScopedOrphanedMaestro spcResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub/resourceGroups/rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster-a/serviceProviderClusters/default")) clusterRID := spcResourceID.Parent cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterRID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterRID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1834,6 +1882,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_ensureClusterScopedOrphanedMaestro spcResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub/resourceGroups/rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster/serviceProviderClusters/default")) clusterRID := spcResourceID.Parent cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterRID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterRID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), @@ -1891,6 +1942,9 @@ func TestDeleteOrphanedMaestroReadonlyBundles_SyncOnce_FullFlow_DeletesOrphanedB clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub/resourceGroups/rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/csid"))), diff --git a/backend/pkg/controllers/maestro_readonly_bundle_helpers_test.go b/backend/pkg/controllers/maestro_readonly_bundle_helpers_test.go index ba09da267a3..c8a8cfd09c9 100644 --- a/backend/pkg/controllers/maestro_readonly_bundle_helpers_test.go +++ b/backend/pkg/controllers/maestro_readonly_bundle_helpers_test.go @@ -350,6 +350,9 @@ var _ database.ServiceProviderClusterCRUD = &errorInjectingSPCCRUD{} func TestMaestroReadonlyBundleHelpers_calculateManagementClusterContentFromMaestroBundle(t *testing.T) { clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub/resourceGroups/rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, } ref := &api.MaestroBundleReference{ @@ -439,6 +442,9 @@ func TestMaestroReadonlyBundleHelpers_readAndPersistMaestroReadonlyBundleContent ctx := context.Background() clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/sub/resourceGroups/rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, } ref := &api.MaestroBundleReference{ diff --git a/backend/pkg/controllers/metricscontrollers/resource_metrics_controller_test.go b/backend/pkg/controllers/metricscontrollers/resource_metrics_controller_test.go index 349af2f0bc6..5413d205eb4 100644 --- a/backend/pkg/controllers/metricscontrollers/resource_metrics_controller_test.go +++ b/backend/pkg/controllers/metricscontrollers/resource_metrics_controller_test.go @@ -42,6 +42,9 @@ func newTestCluster(t *testing.T, name string, state arm.ProvisioningState, crea } return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: api.Must(azcorearm.ParseResourceID("/subscriptions/sub-1/resourceGroups/rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/" + name)), + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: api.Must(azcorearm.ParseResourceID("/subscriptions/sub-1/resourceGroups/rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/" + name)), diff --git a/backend/pkg/controllers/operationcontrollers/test_helpers_test.go b/backend/pkg/controllers/operationcontrollers/test_helpers_test.go index f0e14923d03..35f104fcb2b 100644 --- a/backend/pkg/controllers/operationcontrollers/test_helpers_test.go +++ b/backend/pkg/controllers/operationcontrollers/test_helpers_test.go @@ -82,6 +82,9 @@ func newClusterTestFixture() *clusterTestFixture { func (f *clusterTestFixture) newCluster(createdAt *time.Time) *api.HCPOpenShiftCluster { return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: f.clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: f.clusterResourceID, @@ -162,6 +165,9 @@ func newNodePoolTestFixture() *nodePoolTestFixture { func (f *nodePoolTestFixture) newCluster() *api.HCPOpenShiftCluster { return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: f.clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: f.clusterResourceID, @@ -257,6 +263,9 @@ func newExternalAuthTestFixture() *externalAuthTestFixture { func (f *externalAuthTestFixture) newCluster() *api.HCPOpenShiftCluster { return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: f.clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: f.clusterResourceID, diff --git a/backend/pkg/controllers/read_and_persist_cluster_scoped_maestro_readonly_bundles_content_controller_test.go b/backend/pkg/controllers/read_and_persist_cluster_scoped_maestro_readonly_bundles_content_controller_test.go index b727c3215f5..22adb796f96 100644 --- a/backend/pkg/controllers/read_and_persist_cluster_scoped_maestro_readonly_bundles_content_controller_test.go +++ b/backend/pkg/controllers/read_and_persist_cluster_scoped_maestro_readonly_bundles_content_controller_test.go @@ -66,6 +66,9 @@ func TestReadAndPersistClusterScopedMaestroReadonlyBundlesContentSyncer_SyncOnce clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/11111111111111111111111111111111"))), @@ -112,6 +115,9 @@ func TestReadAndPersistClusterScopedMaestroReadonlyBundlesContentSyncer_SyncOnce clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/11111111111111111111111111111111"))), @@ -154,6 +160,9 @@ func TestReadAndPersistClusterScopedMaestroReadonlyBundlesContentSyncer_SyncOnce clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/11111111111111111111111111111111"))), @@ -210,6 +219,9 @@ func TestReadAndPersistClusterScopedMaestroReadonlyBundlesContentSyncer_SyncOnce clusterResourceID := api.Must(azcorearm.ParseResourceID("/subscriptions/test-sub/resourceGroups/test-rg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{Resource: arm.Resource{ID: clusterResourceID}}, ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ ClusterServiceID: api.Ptr(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/11111111111111111111111111111111"))), diff --git a/backend/pkg/controllers/upgradecontrollers/control_plane_active_version_controller_test.go b/backend/pkg/controllers/upgradecontrollers/control_plane_active_version_controller_test.go index e0df48532eb..431b9e2b11f 100644 --- a/backend/pkg/controllers/upgradecontrollers/control_plane_active_version_controller_test.go +++ b/backend/pkg/controllers/upgradecontrollers/control_plane_active_version_controller_test.go @@ -286,6 +286,9 @@ func createTestHCPCluster(t *testing.T, ctx context.Context, mockResourcesDBClie require.NoError(t, err) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, diff --git a/backend/pkg/controllers/upgradecontrollers/control_plane_desired_version_controller_test.go b/backend/pkg/controllers/upgradecontrollers/control_plane_desired_version_controller_test.go index ca092f4d15c..588f4e82e5c 100644 --- a/backend/pkg/controllers/upgradecontrollers/control_plane_desired_version_controller_test.go +++ b/backend/pkg/controllers/upgradecontrollers/control_plane_desired_version_controller_test.go @@ -578,6 +578,9 @@ func TestDesiredControlPlaneZVersion_NextYStreamUpgrade(t *testing.T) { func testCosmosClusterWithWorkersNodePoolAtVersion(nodePoolVersionId string) []any { clusterResourceId := api.Must(api.ToClusterResourceID("6b690bec-0c16-4ecb-8f67-781caf40bba7", "test-rg", "test-cluster")) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceId, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceId, @@ -974,6 +977,9 @@ func createTestHCPClusterWithCustomerVersion(t *testing.T, ctx context.Context, clusterInternalID, err := api.NewInternalID(testCSClusterIDStr) require.NoError(t, err) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, diff --git a/backend/pkg/controllers/upgradecontrollers/nodepool_version_controller_test.go b/backend/pkg/controllers/upgradecontrollers/nodepool_version_controller_test.go index e123014afb4..971703e52a3 100644 --- a/backend/pkg/controllers/upgradecontrollers/nodepool_version_controller_test.go +++ b/backend/pkg/controllers/upgradecontrollers/nodepool_version_controller_test.go @@ -101,6 +101,9 @@ func createTestNodePoolWithVersion(t *testing.T, ctx context.Context, mockResour require.NoError(t, err) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, diff --git a/backend/pkg/informers/informers_test.go b/backend/pkg/informers/informers_test.go index dc3ecbc8aa0..405097d3d8a 100644 --- a/backend/pkg/informers/informers_test.go +++ b/backend/pkg/informers/informers_test.go @@ -344,6 +344,9 @@ func clusterInformerTestCase() informerTestCase { internalID, err := api.NewInternalID("/api/clusters_mgmt/v1/clusters/" + name) require.NoError(t, err) return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -479,6 +482,9 @@ func nodePoolInformerTestCase() informerTestCase { internalID, err := api.NewInternalID("/api/clusters_mgmt/v1/clusters/" + clusterName) require.NoError(t, err) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -710,6 +716,9 @@ func controllerInformerTestCase() informerTestCase { internalID, err := api.NewInternalID("/api/clusters_mgmt/v1/clusters/" + clusterName) require.NoError(t, err) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, diff --git a/backend/pkg/listertesting/slice_listers_test.go b/backend/pkg/listertesting/slice_listers_test.go index c8528c540e9..2545aff0594 100644 --- a/backend/pkg/listertesting/slice_listers_test.go +++ b/backend/pkg/listertesting/slice_listers_test.go @@ -323,6 +323,9 @@ func newTestCluster(subscriptionID, resourceGroupName, clusterName string) *api. "/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/" + clusterName, )) return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: resourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: resourceID, diff --git a/frontend/pkg/frontend/cluster.go b/frontend/pkg/frontend/cluster.go index 37b9690e617..efb80a46498 100644 --- a/frontend/pkg/frontend/cluster.go +++ b/frontend/pkg/frontend/cluster.go @@ -232,6 +232,7 @@ func decodeDesiredClusterCreate(ctx context.Context, azureLocation string, reque } // TrackedResource info doesn't appear to come from the external resource information conversion.CopyReadOnlyTrackedResourceValues(&newInternalCluster.TrackedResource, ptr.To(arm.NewTrackedResource(resourceID, azureLocation))) + newInternalCluster.SetResourceID(resourceID) // set fields that were not included during the conversion, because the user does not provide them or because the // data is determined live on read. @@ -839,8 +840,11 @@ func (f *Frontend) getInternalClusterFromStorage(ctx context.Context, resourceID // normalize or return a toupper or tolower form of the resource // group or resource name. The resource group name and resource // name must come from the URL and not the request body. - if !strings.EqualFold(internalCluster.ID.String(), resourceID.String()) { - return nil, fmt.Errorf("unexpected resourceID: %s", internalCluster.ID.String()) + if internalCluster.ResourceID == nil { + return nil, fmt.Errorf("stored cluster document is missing cosmosMetadata.resourceID") + } + if !strings.EqualFold(internalCluster.ResourceID.String(), resourceID.String()) { + return nil, fmt.Errorf("unexpected resourceID: %s", internalCluster.ResourceID.String()) } internalCluster.ID = resourceID diff --git a/frontend/pkg/frontend/frontend_test.go b/frontend/pkg/frontend/frontend_test.go index 813c768878d..e8fa7179a6d 100644 --- a/frontend/pkg/frontend/frontend_test.go +++ b/frontend/pkg/frontend/frontend_test.go @@ -603,6 +603,9 @@ func TestRequestAdminCredential(t *testing.T) { ctx := utils.ContextWithLogger(t.Context(), testr.New(t)) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -713,6 +716,9 @@ func TestRevokeCredentials(t *testing.T) { ctx := utils.ContextWithLogger(t.Context(), testr.New(t)) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, diff --git a/frontend/pkg/frontend/helpers_test.go b/frontend/pkg/frontend/helpers_test.go index 0bf08d55a97..f6abfa46c6a 100644 --- a/frontend/pkg/frontend/helpers_test.go +++ b/frontend/pkg/frontend/helpers_test.go @@ -123,6 +123,9 @@ func TestCheckForProvisioningStateConflict(t *testing.T) { parentResourceID := resourceID.Parent clusterInternalID := api.Must(api.NewInternalID(ocm.GenerateOCMCommercialClusterHREF("testCluster"))) parentCluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: parentResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: parentResourceID, @@ -170,6 +173,9 @@ func TestCheckForProvisioningStateConflict(t *testing.T) { // Pre-populate the parent cluster with the test provisioning state clusterInternalID := api.Must(api.NewInternalID(ocm.GenerateOCMCommercialClusterHREF("testCluster"))) parentCluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: parentResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: parentResourceID, diff --git a/internal/api/types_cluster.go b/internal/api/types_cluster.go index ba7c919e4cb..c764fd2c8fd 100644 --- a/internal/api/types_cluster.go +++ b/internal/api/types_cluster.go @@ -15,7 +15,6 @@ package api import ( - "github.com/Azure/azure-sdk-for-go/sdk/azcore" azcorearm "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" "github.com/Azure/ARO-HCP/internal/api/arm" @@ -24,30 +23,17 @@ import ( // HCPOpenShiftCluster represents an ARO HCP OpenShift cluster resource. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type HCPOpenShiftCluster struct { + CosmosMetadata `json:"cosmosMetadata"` + arm.TrackedResource CustomerProperties HCPOpenShiftClusterCustomerProperties `json:"customerProperties,omitempty"` ServiceProviderProperties HCPOpenShiftClusterServiceProviderProperties `json:"serviceProviderProperties,omitempty"` Identity *arm.ManagedServiceIdentity `json:"identity,omitempty"` - // CosmosETag is an in-memory copy of the _etag field read from the Cosmos DB document (BaseDocument) and - // populated on DB read via the CosmosToInternalCluster() conversion function. - // We carry it across the API boundary between HCPCluster (the direct cosmos db type) and HCPOpenShiftCluster (this) - // so we can populate the CosmosETag in GetCosmosData() so that we can do conditional replaces in cosmos. - // This can be removed once we have inlined and serialized CosmosMetadata in - // HCPOpenShiftCluster. - CosmosETag azcore.ETag `json:"-"` } var _ arm.CosmosPersistable = &HCPOpenShiftCluster{} -func (o *HCPOpenShiftCluster) GetCosmosData() *arm.CosmosMetadata { - return &arm.CosmosMetadata{ - CosmosETag: o.CosmosETag, - ResourceID: o.ID, - ExistingCosmosUID: o.ServiceProviderProperties.ExistingCosmosUID, - } -} - // HCPOpenShiftClusterCustomerProperties represents the property bag of a HCPOpenShiftCluster resource. type HCPOpenShiftClusterCustomerProperties struct { Version VersionProfile `json:"version,omitempty"` @@ -64,7 +50,6 @@ type HCPOpenShiftClusterCustomerProperties struct { // HCPOpenShiftClusterCustomerProperties represents the property bag of a HCPOpenShiftCluster resource. type HCPOpenShiftClusterServiceProviderProperties struct { - ExistingCosmosUID string `json:"-"` ProvisioningState arm.ProvisioningState `json:"provisioningState,omitempty"` ClusterServiceID *InternalID `json:"clusterServiceID,omitempty"` ActiveOperationID string `json:"activeOperationId,omitempty"` diff --git a/internal/api/v20240610preview/conversion_fuzz_test.go b/internal/api/v20240610preview/conversion_fuzz_test.go index cf2cae217f5..d8208471629 100644 --- a/internal/api/v20240610preview/conversion_fuzz_test.go +++ b/internal/api/v20240610preview/conversion_fuzz_test.go @@ -62,7 +62,6 @@ func TestRoundTripInternalExternalInternal(t *testing.T) { j.RevokeCredentialsOperationID = "" // ClusterServiceID does not roundtrip through the external type because it is purely an internal detail j.ClusterServiceID = nil - j.ExistingCosmosUID = "" // ExperimentalFeatures does not roundtrip through the external type because it is purely an internal detail j.ExperimentalFeatures = api.ExperimentalFeatures{} // ManagedIdentitiesDataPlaneIdentityURL does not roundtrip through the external type because @@ -106,6 +105,12 @@ func TestRoundTripInternalExternalInternal(t *testing.T) { // Visibility was added in v2025_12_23_preview and does not exist in v2024_06_10_preview j.Visibility = "" }, + func(j *api.CosmosMetadata, c randfill.Continue) { + c.FillNoCustom(j) + j.ResourceID = api.Must(azcorearm.ParseResourceID("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg")) + j.ExistingCosmosUID = "" + j.CosmosETag = "" + }, func(j *api.CustomerManagedEncryptionProfile, c randfill.Continue) { c.FillNoCustom(j) // A zero-value KmsEncryptionProfile cannot roundtrip because @@ -123,8 +128,6 @@ func TestRoundTripInternalExternalInternal(t *testing.T) { for i := 0; i < 200; i++ { original := &api.HCPOpenShiftCluster{} fuzzer.Fill(original) - // CosmosETag does not roundtrip through the external type because it is purely a database concern - original.CosmosETag = "" roundTripHCPCluster(t, original) } diff --git a/internal/api/v20240610preview/hcpopenshiftclusters_methods.go b/internal/api/v20240610preview/hcpopenshiftclusters_methods.go index b1546e3878b..62f658d422e 100644 --- a/internal/api/v20240610preview/hcpopenshiftclusters_methods.go +++ b/internal/api/v20240610preview/hcpopenshiftclusters_methods.go @@ -307,8 +307,8 @@ func (v version) NewHCPOpenShiftCluster(from *api.HCPOpenShiftCluster) api.Versi } idString := "" - if from.ID != nil { - idString = from.ID.String() + if from.ResourceID != nil { + idString = from.ResourceID.String() } out := &HcpOpenShiftCluster{ @@ -349,6 +349,7 @@ func (c *HcpOpenShiftCluster) ConvertToInternal(existing *api.HCPOpenShiftCluste if c.ID != nil { out.ID = api.Must(azcorearm.ParseResourceID(strings.ToLower(*c.ID))) + out.ResourceID = api.Must(azcorearm.ParseResourceID(strings.ToLower(*c.ID))) } if c.Name != nil { out.Name = *c.Name diff --git a/internal/api/v20251223preview/conversion_fuzz_test.go b/internal/api/v20251223preview/conversion_fuzz_test.go index fdec57204ff..c78d53222dc 100644 --- a/internal/api/v20251223preview/conversion_fuzz_test.go +++ b/internal/api/v20251223preview/conversion_fuzz_test.go @@ -60,7 +60,6 @@ func TestRoundTripInternalExternalInternal(t *testing.T) { j.RevokeCredentialsOperationID = "" // ClusterServiceID does not roundtrip through the external type because it is purely an internal detail j.ClusterServiceID = nil - j.ExistingCosmosUID = "" // ExperimentalFeatures does not roundtrip through the external type because it is purely an internal detail j.ExperimentalFeatures = api.ExperimentalFeatures{} // ManagedIdentitiesDataPlaneIdentityURL does not roundtrip through the external type because @@ -107,8 +106,6 @@ func TestRoundTripInternalExternalInternal(t *testing.T) { for i := 0; i < 200; i++ { original := &api.HCPOpenShiftCluster{} fuzzer.Fill(original) - // CosmosETag does not roundtrip through the external type because it is purely a database concern - original.CosmosETag = "" roundTripHCPCluster(t, original) } diff --git a/internal/api/v20251223preview/hcpopenshiftclusters_methods.go b/internal/api/v20251223preview/hcpopenshiftclusters_methods.go index 9598b3631ae..b26144dee86 100644 --- a/internal/api/v20251223preview/hcpopenshiftclusters_methods.go +++ b/internal/api/v20251223preview/hcpopenshiftclusters_methods.go @@ -326,8 +326,8 @@ func (v version) NewHCPOpenShiftCluster(from *api.HCPOpenShiftCluster) api.Versi } idString := "" - if from.ID != nil { - idString = from.ID.String() + if from.ResourceID != nil { + idString = from.ResourceID.String() } out := &HcpOpenShiftCluster{ @@ -406,6 +406,7 @@ func (c *HcpOpenShiftCluster) ConvertToInternal(existing *api.HCPOpenShiftCluste if c.ID != nil { out.ID = api.Must(azcorearm.ParseResourceID(strings.ToLower(*c.ID))) + out.ResourceID = api.Must(azcorearm.ParseResourceID(strings.ToLower(*c.ID))) } if c.Name != nil { out.Name = *c.Name diff --git a/internal/api/v20251223preview/zero_value_roundtrip_test.go b/internal/api/v20251223preview/zero_value_roundtrip_test.go index 44d79a737fb..e812b3059d5 100644 --- a/internal/api/v20251223preview/zero_value_roundtrip_test.go +++ b/internal/api/v20251223preview/zero_value_roundtrip_test.go @@ -296,6 +296,9 @@ func newBaselineInternalNodePool() *api.HCPOpenShiftClusterNodePool { // to zero before round-tripping. func newBaselineInternalCluster() *api.HCPOpenShiftCluster { return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: api.Must(azcorearm.ParseResourceID(strings.ToLower("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/myCluster"))), + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: api.Must(azcorearm.ParseResourceID(strings.ToLower("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/myCluster"))), diff --git a/internal/api/zz_generated.deepcopy.go b/internal/api/zz_generated.deepcopy.go index 9f2cd0b6b60..a7a53bb36a5 100644 --- a/internal/api/zz_generated.deepcopy.go +++ b/internal/api/zz_generated.deepcopy.go @@ -426,6 +426,7 @@ func (in *HCPNodePoolActiveVersion) DeepCopy() *HCPNodePoolActiveVersion { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HCPOpenShiftCluster) DeepCopyInto(out *HCPOpenShiftCluster) { *out = *in + in.CosmosMetadata.DeepCopyInto(&out.CosmosMetadata) in.TrackedResource.DeepCopyInto(&out.TrackedResource) in.CustomerProperties.DeepCopyInto(&out.CustomerProperties) in.ServiceProviderProperties.DeepCopyInto(&out.ServiceProviderProperties) diff --git a/internal/conversion/readonly_cluster.go b/internal/conversion/readonly_cluster.go index 60e1b2f4d5f..ac61c0d8a02 100644 --- a/internal/conversion/readonly_cluster.go +++ b/internal/conversion/readonly_cluster.go @@ -30,6 +30,10 @@ func CopyReadOnlyTrackedResourceValues(dest, src *arm.TrackedResource) { func CopyReadOnlyClusterValues(dest, src *api.HCPOpenShiftCluster) { CopyReadOnlyTrackedResourceValues(&dest.TrackedResource, &src.TrackedResource) + // CosmosMetadata is read-only on the API surface; carry over so the + // case-preserving ResourceID and CosmosETag survive the replace round-trip. + dest.CosmosMetadata = *src.CosmosMetadata.DeepCopy() + switch { case hasClusterIdentityToSet(src.Identity) && dest.Identity == nil: dest.Identity = &arm.ManagedServiceIdentity{} @@ -41,7 +45,6 @@ func CopyReadOnlyClusterValues(dest, src *api.HCPOpenShiftCluster) { } dest.ServiceProviderProperties = *src.ServiceProviderProperties.DeepCopy() - dest.CosmosETag = src.CosmosETag } func copyReadOnlyManagedServiceIdentityValues(dest, src *arm.ManagedServiceIdentity) { diff --git a/internal/database/convert_cluster.go b/internal/database/convert_cluster.go index 28c6c2a9397..90c17b14584 100644 --- a/internal/database/convert_cluster.go +++ b/internal/database/convert_cluster.go @@ -21,7 +21,6 @@ import ( "k8s.io/utils/ptr" "github.com/Azure/ARO-HCP/internal/api" - "github.com/Azure/ARO-HCP/internal/api/arm" ) func InternalToCosmosCluster(internalObj *api.HCPOpenShiftCluster) (*HCPCluster, error) { @@ -29,22 +28,26 @@ func InternalToCosmosCluster(internalObj *api.HCPOpenShiftCluster) (*HCPCluster, return nil, nil } + // CosmosMetadata.ResourceID is the canonical identifier for cosmos-side + // concerns (partitioning, document UID, resource-type indexing). Use it + // instead of the TrackedResource.ID, which is an ARM-surface concern. + cosmosResourceID := internalObj.GetCosmosData().ResourceID + if cosmosResourceID == nil { + return nil, fmt.Errorf("internalObj is missing CosmosMetadata.ResourceID") + } cosmosObj := &HCPCluster{ TypedDocument: TypedDocument{ BaseDocument: BaseDocument{ ID: internalObj.GetCosmosData().GetCosmosUID(), }, - PartitionKey: strings.ToLower(internalObj.ID.SubscriptionID), - ResourceID: internalObj.ID, - ResourceType: internalObj.ID.ResourceType.String(), + PartitionKey: strings.ToLower(cosmosResourceID.SubscriptionID), + ResourceID: cosmosResourceID, + ResourceType: cosmosResourceID.ResourceType.String(), }, HCPClusterProperties: HCPClusterProperties{ HCPOpenShiftCluster: *internalObj, - CosmosMetadata: api.CosmosMetadata{ - ResourceID: internalObj.ID, - }, IntermediateResourceDoc: &ResourceDocument{ - ResourceID: internalObj.ID, + ResourceID: cosmosResourceID, InternalID: ptr.Deref(internalObj.ServiceProviderProperties.ClusterServiceID, api.InternalID{}), ActiveOperationID: internalObj.ServiceProviderProperties.ActiveOperationID, ProvisioningState: internalObj.ServiceProviderProperties.ProvisioningState, @@ -58,6 +61,8 @@ func InternalToCosmosCluster(internalObj *api.HCPOpenShiftCluster) (*HCPCluster, }, } + cosmosObj.InternalState.InternalAPI.CosmosMetadata = api.CosmosMetadata{} + return cosmosObj, nil } @@ -77,42 +82,17 @@ func CosmosToInternalCluster(cosmosObj *HCPCluster) (*api.HCPOpenShiftCluster, e if cosmosObj == nil { return nil, nil } - resourceDoc := cosmosObj.IntermediateResourceDoc - if resourceDoc == nil { - return nil, fmt.Errorf("resource document cannot be nil") - } - - tempInternalAPI := cosmosObj.InternalState.InternalAPI - internalObj := &tempInternalAPI - - // some pieces of data are stored on the ResourceDocument, so we need to restore that data - internalObj.TrackedResource = arm.TrackedResource{ - Resource: arm.Resource{ - ID: resourceDoc.ResourceID, - Name: resourceDoc.ResourceID.Name, - Type: resourceDoc.ResourceID.ResourceType.String(), - SystemData: resourceDoc.SystemData, - }, - Location: cosmosObj.InternalState.InternalAPI.Location, - Tags: resourceDoc.Tags, - } - // we carry over the CosmosETag from the cosmos object to the internal object into a - // temporary field until we have inlined and serialized CosmosMetadata in - // HCPOpenShiftCluster. - internalObj.CosmosETag = cosmosObj.BaseDocument.CosmosETag - internalObj.Identity = resourceDoc.Identity.DeepCopy() - internalObj.SystemData = resourceDoc.SystemData - internalObj.Tags = copyTags(resourceDoc.Tags) - internalObj.ServiceProviderProperties.ExistingCosmosUID = cosmosObj.ID - internalObj.ServiceProviderProperties.ProvisioningState = resourceDoc.ProvisioningState - if len(resourceDoc.InternalID.String()) == 0 { - // preserve the nil on read - internalObj.ServiceProviderProperties.ClusterServiceID = nil - } else { - internalObj.ServiceProviderProperties.ClusterServiceID = &resourceDoc.InternalID + internalObj := cosmosObj.DeepCopy() + internalObj.ExistingCosmosUID = cosmosObj.ID + internalObj.SetEtag(cosmosObj.CosmosETag) + if internalObj.GetResourceID() == nil { + if cosmosObj.ResourceID != nil { + internalObj.SetResourceID(cosmosObj.ResourceID) + } else { + return nil, fmt.Errorf("internalObj is missing a resourceID: %T: %q", cosmosObj, cosmosObj.ID) + } } - internalObj.ServiceProviderProperties.ActiveOperationID = resourceDoc.ActiveOperationID internalObj.EnsureDefaults() diff --git a/internal/database/convert_cluster_test.go b/internal/database/convert_cluster_test.go index 82205ef2594..6161aee4d15 100644 --- a/internal/database/convert_cluster_test.go +++ b/internal/database/convert_cluster_test.go @@ -68,13 +68,17 @@ func TestRoundTripClusterInternalCosmosInternal(t *testing.T) { foo := api.Must(api.NewInternalID("/api/clusters_mgmt/v1/clusters/r" + strings.ReplaceAll(c.String(10), "/", "-"))) j.ClusterServiceID = &foo }, + func(j *api.CosmosMetadata, c randfill.Continue) { + c.FillNoCustom(j) + j.ResourceID = api.Must(azcorearm.ParseResourceID("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myRg")) + j.ExistingCosmosUID = "" + j.CosmosETag = "" + }, func(j *api.HCPOpenShiftCluster, c randfill.Continue) { c.FillNoCustom(j) if j == nil { return } - j.ServiceProviderProperties.ExistingCosmosUID = "" - j.CosmosETag = "" // Canonical defaults are applied on Cosmos read, so ensure // defaulted fields are never zero during round-trip testing. if len(j.CustomerProperties.Network.NetworkType) == 0 { @@ -136,7 +140,7 @@ func roundTripInternalToCosmosToInternal[InternalAPIType, CosmosAPIType any](t * // this value is set during conversion, so we need clear for comparison switch cast := any(final).(type) { case *api.HCPOpenShiftCluster: - cast.ServiceProviderProperties.ExistingCosmosUID = "" + cast.ExistingCosmosUID = "" case *api.HCPOpenShiftClusterNodePool: cast.ExistingCosmosUID = "" case *api.HCPOpenShiftClusterExternalAuth: @@ -180,6 +184,11 @@ func TestCosmosToInternalClusterPreservesETag(t *testing.T) { }, }, HCPClusterProperties: HCPClusterProperties{ + HCPOpenShiftCluster: api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: resourceID, + }, + }, IntermediateResourceDoc: &ResourceDocument{ ResourceID: resourceID, }, diff --git a/internal/database/convert_defaults_consistency_test.go b/internal/database/convert_defaults_consistency_test.go index 05b60d07874..0953007fda1 100644 --- a/internal/database/convert_defaults_consistency_test.go +++ b/internal/database/convert_defaults_consistency_test.go @@ -269,33 +269,33 @@ func TestKMSVisibilityDefaultsToPublic(t *testing.T) { ResourceID: resourceID, }, HCPClusterProperties: HCPClusterProperties{ - IntermediateResourceDoc: &ResourceDocument{ - ResourceID: resourceID, - InternalID: api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/test-cluster")), - ProvisioningState: arm.ProvisioningStateSucceeded, - }, - InternalState: ClusterInternalState{ - InternalAPI: api.HCPOpenShiftCluster{ - CustomerProperties: api.HCPOpenShiftClusterCustomerProperties{ - Etcd: api.EtcdProfile{ - DataEncryption: api.EtcdDataEncryptionProfile{ - KeyManagementMode: api.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged, - CustomerManaged: &api.CustomerManagedEncryptionProfile{ - EncryptionType: api.CustomerManagedEncryptionTypeKMS, - Kms: &api.KmsEncryptionProfile{ - ActiveKey: api.KmsKey{ - Name: "test-key", - VaultName: "test-vault", - Version: "v1", - }, - // Visibility intentionally not set (empty string) - Visibility: "", + HCPOpenShiftCluster: api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: resourceID, + }, + CustomerProperties: api.HCPOpenShiftClusterCustomerProperties{ + Etcd: api.EtcdProfile{ + DataEncryption: api.EtcdDataEncryptionProfile{ + KeyManagementMode: api.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged, + CustomerManaged: &api.CustomerManagedEncryptionProfile{ + EncryptionType: api.CustomerManagedEncryptionTypeKMS, + Kms: &api.KmsEncryptionProfile{ + ActiveKey: api.KmsKey{ + Name: "test-key", + VaultName: "test-vault", + Version: "v1", }, + // Visibility intentionally not set (empty string) + Visibility: "", }, }, }, }, }, + ServiceProviderProperties: api.HCPOpenShiftClusterServiceProviderProperties{ + ClusterServiceID: ptr.To(api.Must(api.NewInternalID("/api/aro_hcp/v1alpha1/clusters/test-cluster"))), + ProvisioningState: arm.ProvisioningStateSucceeded, + }, }, }, } diff --git a/internal/database/types_hcpcluster.go b/internal/database/types_hcpcluster.go index 7570a5d4f21..5b569708865 100644 --- a/internal/database/types_hcpcluster.go +++ b/internal/database/types_hcpcluster.go @@ -25,16 +25,12 @@ type HCPCluster struct { } type HCPClusterProperties struct { - // HCPOpenShiftCluster is where we're migrating to. It is compatible with a GenericDocument[api.HCPOpenShiftCluster] - // which is where we want to end up. - // * to be compatible with prior versions, we must continue writing all previous fields and this new field - // * to be compatible with prior versions, we must continue reading only from previous fields + // HCPOpenShiftCluster is the inline serialization that mirrors GenericDocument[api.HCPOpenShiftCluster] + // (the destination shape for cluster documents). The reading path now uses these inline fields as the + // source of truth; the IntermediateResourceDoc/InternalState siblings are still written for compatibility + // with old readers, but will be removed once all readers have migrated. api.HCPOpenShiftCluster `json:",inline"` - // when we switch to inlining the internalObj, this will be in the right spot. We add it now so that we can switch our - // queries to select on cosmosMetadata.ResourceID instead of resourceId - CosmosMetadata api.CosmosMetadata `json:"cosmosMetadata"` - // IntermediateResourceDoc exists so that we can stop inlining the resource document so that we can directly // embed the InternalAPIType which has colliding serialization fields. IntermediateResourceDoc *ResourceDocument `json:"intermediateResourceDoc"` diff --git a/internal/databasetesting/mock_dbclient_test.go b/internal/databasetesting/mock_dbclient_test.go index b9513c617bf..31e840c871d 100644 --- a/internal/databasetesting/mock_dbclient_test.go +++ b/internal/databasetesting/mock_dbclient_test.go @@ -149,6 +149,9 @@ func TestMockResourcesDBClient_CRUD_Cluster(t *testing.T) { } cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -386,6 +389,9 @@ func TestMockResourcesDBClient_Transaction(t *testing.T) { } cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -444,6 +450,9 @@ func TestMockResourcesDBClient_UntypedCRUD(t *testing.T) { } cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -797,6 +806,9 @@ func TestMockResourcesDBClient_addResource(t *testing.T) { } cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -878,6 +890,9 @@ func TestNewMockResourcesDBClientWithResources(t *testing.T) { } cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, diff --git a/internal/serverutils/dump_billing_test.go b/internal/serverutils/dump_billing_test.go index 2914425951b..8d4688fcea6 100644 --- a/internal/serverutils/dump_billing_test.go +++ b/internal/serverutils/dump_billing_test.go @@ -43,6 +43,9 @@ func TestDumpBillingToLogger(t *testing.T) { // Create HCP clusters cluster1 := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: cluster1ResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: cluster1ResourceID, @@ -57,6 +60,9 @@ func TestDumpBillingToLogger(t *testing.T) { } cluster2 := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: cluster2ResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: cluster2ResourceID, @@ -120,6 +126,9 @@ func TestDumpBillingToLogger_PartitionScoping(t *testing.T) { // Create HCP clusters with ClusterUIDs cluster1 := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: cluster1ResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: cluster1ResourceID, @@ -134,6 +143,9 @@ func TestDumpBillingToLogger_PartitionScoping(t *testing.T) { } cluster2 := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: cluster2ResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: cluster2ResourceID, @@ -148,6 +160,9 @@ func TestDumpBillingToLogger_PartitionScoping(t *testing.T) { } cluster3 := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: cluster3ResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: cluster3ResourceID, diff --git a/test-integration/backend/launch/metrics_test.go b/test-integration/backend/launch/metrics_test.go index 3b63b9ff6ce..4e963a1103f 100644 --- a/test-integration/backend/launch/metrics_test.go +++ b/test-integration/backend/launch/metrics_test.go @@ -151,6 +151,9 @@ func TestBackendExposesMetrics(t *testing.T) { func newMetricsTestCluster(resourceID *azcorearm.ResourceID, provisioningState arm.ProvisioningState, createdAt *time.Time) *api.HCPOpenShiftCluster { return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: resourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: resourceID, diff --git a/test-integration/frontend/informer_test.go b/test-integration/frontend/informer_test.go index f0d10cad79d..dee2b0902a8 100644 --- a/test-integration/frontend/informer_test.go +++ b/test-integration/frontend/informer_test.go @@ -359,6 +359,9 @@ func clusterInformerIntegrationTestCase() informerIntegrationTestCase { internalID, err := api.NewInternalID("/api/clusters_mgmt/v1/clusters/" + name) require.NoError(t, err) return &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID, @@ -487,6 +490,9 @@ func nodePoolInformerIntegrationTestCase() informerIntegrationTestCase { internalID, err := api.NewInternalID("/api/clusters_mgmt/v1/clusters/" + clusterName) require.NoError(t, err) cluster := &api.HCPOpenShiftCluster{ + CosmosMetadata: arm.CosmosMetadata{ + ResourceID: clusterResourceID, + }, TrackedResource: arm.TrackedResource{ Resource: arm.Resource{ ID: clusterResourceID,