diff --git a/lib/auth/apiserver_test.go b/lib/auth/apiserver_test.go index 6a89bcf783db4..85a50df1d7eea 100644 --- a/lib/auth/apiserver_test.go +++ b/lib/auth/apiserver_test.go @@ -135,7 +135,7 @@ func TestUpsertServer(t *testing.T) { addServers(s.GetAuthServers()) addServers(s.GetNodes(ctx, apidefaults.Namespace)) addServers(s.GetProxies()) - require.Empty(t, cmp.Diff(allServers, []types.Server{tt.wantServer}, cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + require.Empty(t, cmp.Diff(allServers, []types.Server{tt.wantServer}, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) }) } } diff --git a/lib/auth/auth_with_roles_test.go b/lib/auth/auth_with_roles_test.go index 4c812d35797e3..0039db02d2871 100644 --- a/lib/auth/auth_with_roles_test.go +++ b/lib/auth/auth_with_roles_test.go @@ -37,6 +37,7 @@ import ( "github.com/pquerna/otp/totp" "github.com/sirupsen/logrus" "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api" @@ -1782,33 +1783,33 @@ func TestDatabasesCRUDRBAC(t *testing.T) { db, err := devClt.GetDatabase(ctx, devDatabase.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(devDatabase, db, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Admin can get both databases. db, err = adminClt.GetDatabase(ctx, adminDatabase.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(adminDatabase, db, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) db, err = adminClt.GetDatabase(ctx, devDatabase.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(devDatabase, db, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // When listing databases, dev should only see one. dbs, err := devClt.GetDatabases(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Database{devDatabase}, dbs, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Admin should see both. dbs, err = adminClt.GetDatabases(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Database{adminDatabase, devDatabase}, dbs, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Dev shouldn't be able to delete dev database... @@ -1902,7 +1903,7 @@ func mustGetDatabases(t *testing.T, client *Client, wantDatabases []types.Databa require.NoError(t, err) require.Empty(t, cmp.Diff(wantDatabases, actualDatabases, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), cmpopts.EquateEmpty(), )) } @@ -1957,7 +1958,7 @@ func TestKubernetesClusterCRUD_DiscoveryService(t *testing.T) { t.Run("Read", func(t *testing.T) { clusters, err := discoveryClt.GetKubernetesClusters(ctx) require.NoError(t, err) - require.Empty(t, cmp.Diff([]types.KubeCluster{eksCluster}, clusters)) + require.Empty(t, cmp.Diff([]types.KubeCluster{eksCluster}, clusters, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) }) t.Run("Update", func(t *testing.T) { require.NoError(t, discoveryClt.UpdateKubernetesCluster(ctx, eksCluster)) @@ -2480,33 +2481,33 @@ func TestApps(t *testing.T) { app, err := devClt.GetApp(ctx, devApp.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(devApp, app, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Admin can get both apps. app, err = adminClt.GetApp(ctx, adminApp.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(adminApp, app, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) app, err = adminClt.GetApp(ctx, devApp.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(devApp, app, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // When listing apps, dev should only see one. apps, err := devClt.GetApps(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Application{devApp}, apps, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Admin should see both. apps, err = adminClt.GetApps(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Application{adminApp, devApp}, apps, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Dev shouldn't be able to delete dev app... @@ -2531,7 +2532,7 @@ func TestApps(t *testing.T) { apps, err = adminClt.GetApps(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Application{adminApp}, apps, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Admin should be able to delete all. @@ -3609,7 +3610,10 @@ func TestListResources_KindUserGroup(t *testing.T) { userGroups, err := types.ResourcesWithLabels(res.Resources).AsUserGroups() require.NoError(t, err) - require.ElementsMatch(t, []types.UserGroup{testUg1, testUg2, testUg3}, userGroups) + slices.SortFunc(userGroups, func(a, b types.UserGroup) int { + return strings.Compare(a.GetName(), b.GetName()) + }) + require.Empty(t, cmp.Diff([]types.UserGroup{testUg2, testUg3, testUg1}, userGroups, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) }) t.Run("start keys", func(t *testing.T) { @@ -6378,7 +6382,7 @@ func TestCreateAccessRequest(t *testing.T) { // We have to ignore the name here, as it's auto-generated by the underlying access request // logic. require.Empty(t, cmp.Diff(test.expected, accessRequests[0], - cmpopts.IgnoreFields(types.Metadata{}, "Name", "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "Name", "ID", "Revision"), cmpopts.IgnoreFields(types.AccessRequestSpecV3{}), )) }) diff --git a/lib/auth/grpcserver_test.go b/lib/auth/grpcserver_test.go index 462822c73f08c..b9d70a3d4af60 100644 --- a/lib/auth/grpcserver_test.go +++ b/lib/auth/grpcserver_test.go @@ -2454,7 +2454,7 @@ func TestNodesCRUD(t *testing.T) { require.NoError(t, err) require.Len(t, nodes, 2) require.Empty(t, cmp.Diff([]types.Server{node1, node2}, nodes, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // GetNodes should not fail if namespace is empty _, err = clt.GetNodes(ctx, "") @@ -2466,7 +2466,7 @@ func TestNodesCRUD(t *testing.T) { node, err := clt.GetNode(ctx, apidefaults.Namespace, "node1") require.NoError(t, err) require.Empty(t, cmp.Diff(node1, node, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // GetNode should fail if node name isn't provided _, err = clt.GetNode(ctx, apidefaults.Namespace, "") @@ -2565,7 +2565,7 @@ func TestLocksCRUD(t *testing.T) { require.NoError(t, err) require.Len(t, locks, 2) require.Empty(t, cmp.Diff([]types.Lock{lock1, lock2}, locks, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) }) t.Run("GetLocks with targets", func(t *testing.T) { t.Parallel() @@ -2574,7 +2574,7 @@ func TestLocksCRUD(t *testing.T) { require.NoError(t, err) require.Len(t, locks, 2) require.Empty(t, cmp.Diff([]types.Lock{lock1, lock2}, locks, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Match only one of the locks. roleTarget := types.LockTarget{Role: "role-A"} @@ -2582,7 +2582,7 @@ func TestLocksCRUD(t *testing.T) { require.NoError(t, err) require.Len(t, locks, 1) require.Empty(t, cmp.Diff([]types.Lock{lock1}, locks, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Match none of the locks. locks, err = clt.GetLocks(ctx, false, roleTarget) @@ -2595,7 +2595,7 @@ func TestLocksCRUD(t *testing.T) { lock, err := clt.GetLock(ctx, lock1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(lock1, lock, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Attempt to get a nonexistent lock. _, err = clt.GetLock(ctx, "lock3") @@ -2675,7 +2675,7 @@ func TestApplicationServersCRUD(t *testing.T) { out, err = clt.GetApplicationServers(ctx, apidefaults.Namespace) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.AppServer{server1, server2, server3}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Update an app server. @@ -2685,7 +2685,7 @@ func TestApplicationServersCRUD(t *testing.T) { out, err = clt.GetApplicationServers(ctx, apidefaults.Namespace) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.AppServer{server1, server2, server3}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete an app server. @@ -2694,7 +2694,7 @@ func TestApplicationServersCRUD(t *testing.T) { out, err = clt.GetApplicationServers(ctx, apidefaults.Namespace) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.AppServer{server2, server3}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete all app servers. @@ -2745,14 +2745,14 @@ func TestAppsCRUD(t *testing.T) { out, err = clt.GetApps(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Application{app1, app2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific app. app, err := clt.GetApp(ctx, app2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(app2, app, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch an app that doesn't exist. @@ -2770,7 +2770,7 @@ func TestAppsCRUD(t *testing.T) { app, err = clt.GetApp(ctx, app1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(app1, app, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete an app. @@ -2779,7 +2779,7 @@ func TestAppsCRUD(t *testing.T) { out, err = clt.GetApps(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Application{app2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete an app that doesn't exist. @@ -2826,7 +2826,7 @@ func TestAppServersCRUD(t *testing.T) { appServer := resources.Resources[0].(types.AppServer) require.Empty(t, cmp.Diff(appServer, appServer1, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) require.NoError(t, clt.DeleteApplicationServer(ctx, apidefaults.Namespace, "hostID", appServer1.GetName())) @@ -2879,7 +2879,7 @@ func TestAppServersCRUD(t *testing.T) { app2.SetOrigin(types.OriginOkta) appServer = resources.Resources[0].(types.AppServer) require.Empty(t, cmp.Diff(appServer, appServer2, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) require.NoError(t, clt.DeleteApplicationServer(ctx, apidefaults.Namespace, "hostID", appServer2.GetName())) @@ -2934,14 +2934,14 @@ func TestDatabasesCRUD(t *testing.T) { out, err = clt.GetDatabases(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Database{db1, db2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific database. db, err := clt.GetDatabase(ctx, db2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(db2, db, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch a database that doesn't exist. @@ -2959,7 +2959,7 @@ func TestDatabasesCRUD(t *testing.T) { db, err = clt.GetDatabase(ctx, db1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(db1, db, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete a database. @@ -2968,7 +2968,7 @@ func TestDatabasesCRUD(t *testing.T) { out, err = clt.GetDatabases(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Database{db2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete a database that doesn't exist. @@ -3050,7 +3050,7 @@ func TestDatabaseServicesCRUD(t *testing.T) { out, err = types.ResourcesWithLabels(listServicesResp.Resources).AsDatabaseServices() require.NoError(t, err) require.Empty(t, cmp.Diff([]types.DatabaseService{db1, db2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Update a DatabaseService. @@ -3072,7 +3072,7 @@ func TestDatabaseServicesCRUD(t *testing.T) { out, err = types.ResourcesWithLabels(listServicesResp.Resources).AsDatabaseServices() require.NoError(t, err) require.Empty(t, cmp.Diff([]types.DatabaseService{db1, db2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete a DatabaseService. @@ -3088,7 +3088,7 @@ func TestDatabaseServicesCRUD(t *testing.T) { out, err = types.ResourcesWithLabels(listServicesResp.Resources).AsDatabaseServices() require.NoError(t, err) require.Empty(t, cmp.Diff([]types.DatabaseService{db2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete a DatabaseService that doesn't exist. @@ -3151,7 +3151,7 @@ func TestServerInfoCRUD(t *testing.T) { } requireResourcesEqual := func(t *testing.T, expected, actual interface{}) { - require.Empty(t, cmp.Diff(expected, actual, cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + require.Empty(t, cmp.Diff(expected, actual, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } t.Run("ServerInfoGetters", func(t *testing.T) { @@ -3253,7 +3253,7 @@ func TestSAMLIdPServiceProvidersCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextKey) require.Empty(t, cmp.Diff([]types.SAMLIdPServiceProvider{sp1, sp2}, listResp, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Update a service provider. @@ -3266,7 +3266,7 @@ func TestSAMLIdPServiceProvidersCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextKey) require.Empty(t, cmp.Diff([]types.SAMLIdPServiceProvider{sp1, sp2}, listResp, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete a service provider. @@ -3276,7 +3276,7 @@ func TestSAMLIdPServiceProvidersCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextKey) require.Empty(t, cmp.Diff([]types.SAMLIdPServiceProvider{sp2}, listResp, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete a service provider that doesn't exist. @@ -4174,7 +4174,7 @@ func TestRoleVersions(t *testing.T) { return } require.Empty(t, cmp.Diff(tc.expectedRole, gotRole, - cmpopts.IgnoreFields(types.RoleV6{}, "Metadata.ID", "Metadata.Labels"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision", "Labels"))) // The downgraded label value won't match exactly because it // includes the client version, so just check it's not empty // and ignore it in the role diff. diff --git a/lib/auth/init_test.go b/lib/auth/init_test.go index d6a7f32cf24e7..4119d020ce5d4 100644 --- a/lib/auth/init_test.go +++ b/lib/auth/init_test.go @@ -1339,7 +1339,7 @@ func resourceFromYAML(t *testing.T, value string) types.Resource { func resourceDiff(res1, res2 types.Resource) string { return cmp.Diff(res1, res2, - cmpopts.IgnoreFields(types.Metadata{}, "ID", "Namespace"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision", "Namespace"), cmpopts.EquateEmpty()) } diff --git a/lib/auth/okta/service_test.go b/lib/auth/okta/service_test.go index ad1aba003453d..65c0871336ad9 100644 --- a/lib/auth/okta/service_test.go +++ b/lib/auth/okta/service_test.go @@ -61,7 +61,7 @@ func TestOktaImportRules(t *testing.T) { require.NoError(t, err) require.Empty(t, listResp.NextPageToken) require.Empty(t, cmp.Diff([]*types.OktaImportRuleV1{r1, r2, r3}, listResp.ImportRules, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) r1.SetExpiry(time.Now().Add(30 * time.Minute)) updateResp, err := svc.UpdateOktaImportRule(ctx, &oktapb.UpdateOktaImportRuleRequest{ImportRule: r1}) @@ -71,7 +71,7 @@ func TestOktaImportRules(t *testing.T) { r, err := svc.GetOktaImportRule(ctx, &oktapb.GetOktaImportRuleRequest{Name: r1.GetName()}) require.NoError(t, err) require.Empty(t, cmp.Diff(r1, r, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) _, err = svc.DeleteOktaImportRule(ctx, &oktapb.DeleteOktaImportRuleRequest{Name: r1.GetName()}) require.NoError(t, err) @@ -80,7 +80,7 @@ func TestOktaImportRules(t *testing.T) { require.NoError(t, err) require.Empty(t, listResp.NextPageToken) require.Empty(t, cmp.Diff([]*types.OktaImportRuleV1{r2, r3}, listResp.ImportRules, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) _, err = svc.DeleteAllOktaImportRules(ctx, &oktapb.DeleteAllOktaImportRulesRequest{}) require.NoError(t, err) @@ -119,7 +119,7 @@ func TestOktaAssignments(t *testing.T) { require.NoError(t, err) require.Empty(t, listResp.NextPageToken) require.Empty(t, cmp.Diff([]*types.OktaAssignmentV1{a1, a2, a3}, listResp.Assignments, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) a1.SetExpiry(time.Now().Add(30 * time.Minute)) updateResp, err := svc.UpdateOktaAssignment(ctx, &oktapb.UpdateOktaAssignmentRequest{Assignment: a1}) @@ -129,7 +129,7 @@ func TestOktaAssignments(t *testing.T) { a, err := svc.GetOktaAssignment(ctx, &oktapb.GetOktaAssignmentRequest{Name: a1.GetName()}) require.NoError(t, err) require.Empty(t, cmp.Diff(a1, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) _, err = svc.UpdateOktaAssignmentStatus(ctx, &oktapb.UpdateOktaAssignmentStatusRequest{Name: a1.GetName(), Status: types.OktaAssignmentSpecV1_PROCESSING}) @@ -140,7 +140,7 @@ func TestOktaAssignments(t *testing.T) { require.NoError(t, err) a1.SetLastTransition(a.GetLastTransition()) require.Empty(t, cmp.Diff(a1, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) _, err = svc.DeleteOktaAssignment(ctx, &oktapb.DeleteOktaAssignmentRequest{Name: a1.GetName()}) require.NoError(t, err) @@ -149,7 +149,7 @@ func TestOktaAssignments(t *testing.T) { require.NoError(t, err) require.Empty(t, listResp.NextPageToken) require.Empty(t, cmp.Diff([]*types.OktaAssignmentV1{a2, a3}, listResp.Assignments, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) _, err = svc.DeleteAllOktaAssignments(ctx, &oktapb.DeleteAllOktaAssignmentsRequest{}) require.NoError(t, err) diff --git a/lib/auth/tls_test.go b/lib/auth/tls_test.go index 27da4add7a509..c1f3a1fdfe227 100644 --- a/lib/auth/tls_test.go +++ b/lib/auth/tls_test.go @@ -1118,7 +1118,7 @@ func TestGetCurrentUser(t *testing.T) { currentUser, err := client1.GetCurrentUser(ctx) require.NoError(t, err) - require.Equal(t, &types.UserV2{ + require.Empty(t, cmp.Diff(&types.UserV2{ Kind: "user", SubKind: "", Version: "v2", @@ -1128,12 +1128,11 @@ func TestGetCurrentUser(t *testing.T) { Description: "", Labels: nil, Expires: nil, - ID: currentUser.GetMetadata().ID, }, Spec: types.UserSpecV2{ Roles: []string{"user:user1"}, }, - }, currentUser) + }, currentUser, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } func TestGetCurrentUserRoles(t *testing.T) { @@ -1148,7 +1147,7 @@ func TestGetCurrentUserRoles(t *testing.T) { roles, err := client1.GetCurrentUserRoles(ctx) require.NoError(t, err) - require.Empty(t, cmp.Diff(roles, []types.Role{user1Role}, cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + require.Empty(t, cmp.Diff(roles, []types.Role{user1Role}, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } func TestAuthPreferenceSettings(t *testing.T) { @@ -4089,7 +4088,7 @@ func TestGRPCServer_CreateTokenV2(t *testing.T) { require.Empty(t, cmp.Diff( tt.token, token, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) } }) @@ -4256,7 +4255,7 @@ func TestGRPCServer_UpsertTokenV2(t *testing.T) { require.Empty(t, cmp.Diff( tt.token, token, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) } }) @@ -4346,7 +4345,7 @@ func TestGRPCServer_GetTokens(t *testing.T) { require.Empty(t, cmp.Diff( expectTokens, tokens, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) } else { require.Empty(t, tokens) @@ -4416,7 +4415,7 @@ func TestGRPCServer_GetToken(t *testing.T) { require.Empty(t, cmp.Diff( token, pt, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) } else { require.Nil(t, token) diff --git a/lib/auth/trust/trustv1/service_test.go b/lib/auth/trust/trustv1/service_test.go index 702374f7fff34..18c274c9f3797 100644 --- a/lib/auth/trust/trustv1/service_test.go +++ b/lib/auth/trust/trustv1/service_test.go @@ -451,6 +451,7 @@ func TestGetCertAuthority(t *testing.T) { assertion: func(t *testing.T, authority types.CertAuthority, err error) { require.NoError(t, err) require.Empty(t, cmp.Diff(authority, ca, + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), cmpopts.IgnoreFields(types.SSHKeyPair{}, "PrivateKey"), cmpopts.IgnoreFields(types.TLSKeyPair{}, "Key"), cmpopts.IgnoreFields(types.JWTKeyPair{}, "PrivateKey"), @@ -470,7 +471,7 @@ func TestGetCertAuthority(t *testing.T) { }, assertion: func(t *testing.T, authority types.CertAuthority, err error) { require.NoError(t, err) - require.Empty(t, cmp.Diff(authority, ca)) + require.Empty(t, cmp.Diff(authority, ca, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) }, }, } @@ -542,6 +543,7 @@ func TestGetCertAuthorities(t *testing.T) { assertion: func(t *testing.T, resp *trustpb.GetCertAuthoritiesResponse, err error) { require.NoError(t, err) require.Empty(t, cmp.Diff(expectedCAs, resp.CertAuthoritiesV2, + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), cmpopts.IgnoreFields(types.SSHKeyPair{}, "PrivateKey"), cmpopts.IgnoreFields(types.TLSKeyPair{}, "Key"), cmpopts.IgnoreFields(types.JWTKeyPair{}, "PrivateKey"), @@ -563,7 +565,7 @@ func TestGetCertAuthorities(t *testing.T) { }, assertion: func(t *testing.T, resp *trustpb.GetCertAuthoritiesResponse, err error) { require.NoError(t, err) - require.Empty(t, cmp.Diff(expectedCAs, resp.CertAuthoritiesV2)) + require.Empty(t, cmp.Diff(expectedCAs, resp.CertAuthoritiesV2, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) }, }, } diff --git a/lib/auth/trustedcluster_test.go b/lib/auth/trustedcluster_test.go index e6bc49a459db1..8eddfaab017cb 100644 --- a/lib/auth/trustedcluster_test.go +++ b/lib/auth/trustedcluster_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/gravitational/trace" "github.com/stretchr/testify/require" @@ -53,9 +54,8 @@ func TestRemoteClusterStatus(t *testing.T) { // Initially, no tunnels exist and status should be "offline". wantRC.SetConnectionStatus(teleport.RemoteClusterStatusOffline) gotRC, err := a.GetRemoteCluster(rc.GetName()) - gotRC.SetResourceID(0) require.NoError(t, err) - require.Empty(t, cmp.Diff(rc, gotRC)) + require.Empty(t, cmp.Diff(rc, gotRC, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Create several tunnel connections. lastHeartbeat := a.clock.Now().UTC() @@ -86,8 +86,7 @@ func TestRemoteClusterStatus(t *testing.T) { wantRC.SetLastHeartbeat(tc2.GetLastHeartbeat()) gotRC, err = a.GetRemoteCluster(rc.GetName()) require.NoError(t, err) - gotRC.SetResourceID(0) - require.Empty(t, cmp.Diff(rc, gotRC)) + require.Empty(t, cmp.Diff(rc, gotRC, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Delete the latest connection. require.NoError(t, a.DeleteTunnelConnection(tc2.GetClusterName(), tc2.GetName())) @@ -99,9 +98,8 @@ func TestRemoteClusterStatus(t *testing.T) { // heartbeat. wantRC.SetConnectionStatus(teleport.RemoteClusterStatusOnline) gotRC, err = a.GetRemoteCluster(rc.GetName()) - gotRC.SetResourceID(0) require.NoError(t, err) - require.Empty(t, cmp.Diff(rc, gotRC)) + require.Empty(t, cmp.Diff(rc, gotRC, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Delete the remaining connection require.NoError(t, a.DeleteTunnelConnection(tc1.GetClusterName(), tc1.GetName())) @@ -112,9 +110,8 @@ func TestRemoteClusterStatus(t *testing.T) { // The last_heartbeat should remain the same. wantRC.SetConnectionStatus(teleport.RemoteClusterStatusOffline) gotRC, err = a.GetRemoteCluster(rc.GetName()) - gotRC.SetResourceID(0) require.NoError(t, err) - require.Empty(t, cmp.Diff(rc, gotRC)) + require.Empty(t, cmp.Diff(rc, gotRC, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } func TestRefreshRemoteClusters(t *testing.T) { @@ -191,7 +188,7 @@ func TestRefreshRemoteClusters(t *testing.T) { var updated int for _, cluster := range clusters { old := allClusters[cluster.GetName()] - if cmp.Diff(old, cluster) != "" { + if cmp.Diff(old, cluster, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision")) != "" { updated++ } } diff --git a/lib/auth/userloginstate/service_test.go b/lib/auth/userloginstate/service_test.go index 208c7b4923c5c..31059314198b1 100644 --- a/lib/auth/userloginstate/service_test.go +++ b/lib/auth/userloginstate/service_test.go @@ -45,7 +45,7 @@ const ( var ( // cmpOpts are general cmpOpts for all comparisons across the service tests. cmpOpts = []cmp.Option{ - cmpopts.IgnoreFields(header.Metadata{}, "ID"), + cmpopts.IgnoreFields(header.Metadata{}, "ID", "Revision"), cmpopts.SortSlices(func(a, b *userloginstate.UserLoginState) bool { return a.GetName() < b.GetName() }), diff --git a/lib/authz/permissions_test.go b/lib/authz/permissions_test.go index 71eaf2ba8453b..a562f7d5fdf24 100644 --- a/lib/authz/permissions_test.go +++ b/lib/authz/permissions_test.go @@ -910,6 +910,6 @@ func createUserAndRole(client *testClient, username string, allowedLogins []stri func resourceDiff(res1, res2 types.Resource) string { return cmp.Diff(res1, res2, - cmpopts.IgnoreFields(types.Metadata{}, "ID", "Namespace"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision", "Namespace"), cmpopts.EquateEmpty()) } diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index 44fb7e917b2a7..12262e7be8b0f 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -336,8 +336,7 @@ func TestCA(t *testing.T) { out, err := p.cache.GetCertAuthority(ctx, ca.GetID(), true) require.NoError(t, err) - ca.SetResourceID(out.GetResourceID()) - require.Empty(t, cmp.Diff(ca, out)) + require.Empty(t, cmp.Diff(ca, out, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = p.trustS.DeleteCertAuthority(ctx, ca.GetID()) require.NoError(t, err) @@ -1048,7 +1047,6 @@ func initStrategy(t *testing.T) { normalizeCA := func(ca types.CertAuthority) types.CertAuthority { ca = ca.Clone() - ca.SetResourceID(0) ca.SetExpiry(time.Time{}) types.RemoveCASecrets(ca) return ca @@ -1057,7 +1055,7 @@ func initStrategy(t *testing.T) { out, err := p.cache.GetCertAuthority(ctx, ca.GetID(), false) require.NoError(t, err) - require.Empty(t, cmp.Diff(normalizeCA(ca), normalizeCA(out))) + require.Empty(t, cmp.Diff(normalizeCA(ca), normalizeCA(out), cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // fail again, make sure last recent data is still served // on errors @@ -1072,7 +1070,7 @@ func initStrategy(t *testing.T) { out2, err := p.cache.GetCertAuthority(ctx, ca.GetID(), false) require.NoError(t, err) require.Equal(t, out.GetResourceID(), out2.GetResourceID()) - require.Empty(t, cmp.Diff(normalizeCA(ca), normalizeCA(out))) + require.Empty(t, cmp.Diff(normalizeCA(ca), normalizeCA(out), cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // add modification and expect the resource to recover ca.SetRoleMap(types.RoleMap{types.RoleMapping{Remote: "test", Local: []string{"local-test"}}}) @@ -1089,7 +1087,7 @@ func initStrategy(t *testing.T) { // new value is available now out, err = p.cache.GetCertAuthority(ctx, ca.GetID(), false) require.NoError(t, err) - require.Empty(t, cmp.Diff(normalizeCA(ca), normalizeCA(out))) + require.Empty(t, cmp.Diff(normalizeCA(ca), normalizeCA(out), cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } // TestRecovery tests error recovery scenario @@ -1132,9 +1130,8 @@ func TestRecovery(t *testing.T) { out, err := p.cache.GetCertAuthority(context.Background(), ca2.GetID(), false) require.NoError(t, err) - ca2.SetResourceID(out.GetResourceID()) types.RemoveCASecrets(ca2) - require.Empty(t, cmp.Diff(ca2, out)) + require.Empty(t, cmp.Diff(ca2, out, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } // TestTokens tests static and dynamic tokens @@ -1168,8 +1165,7 @@ func TestTokens(t *testing.T) { out, err := p.cache.GetStaticTokens() require.NoError(t, err) - staticTokens.SetResourceID(out.GetResourceID()) - require.Empty(t, cmp.Diff(staticTokens, out)) + require.Empty(t, cmp.Diff(staticTokens, out, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) expires := time.Now().Add(10 * time.Hour).Truncate(time.Second).UTC() token, err := types.NewProvisionToken("token", types.SystemRoles{types.RoleAuth, types.RoleNode}, expires) @@ -1187,8 +1183,7 @@ func TestTokens(t *testing.T) { tout, err := p.cache.GetToken(ctx, token.GetName()) require.NoError(t, err) - token.SetResourceID(tout.GetResourceID()) - require.Empty(t, cmp.Diff(token, tout)) + require.Empty(t, cmp.Diff(token, tout, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = p.provisionerS.DeleteToken(ctx, token.GetName()) require.NoError(t, err) @@ -1230,8 +1225,7 @@ func TestAuthPreference(t *testing.T) { outAuthPref, err := p.cache.GetAuthPreference(ctx) require.NoError(t, err) - authPref.SetResourceID(outAuthPref.GetResourceID()) - require.Empty(t, cmp.Diff(outAuthPref, authPref)) + require.Empty(t, cmp.Diff(outAuthPref, authPref, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } func TestClusterNetworkingConfig(t *testing.T) { @@ -1260,8 +1254,7 @@ func TestClusterNetworkingConfig(t *testing.T) { outNetConfig, err := p.cache.GetClusterNetworkingConfig(ctx) require.NoError(t, err) - netConfig.SetResourceID(outNetConfig.GetResourceID()) - require.Empty(t, cmp.Diff(outNetConfig, netConfig)) + require.Empty(t, cmp.Diff(outNetConfig, netConfig, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } func TestSessionRecordingConfig(t *testing.T) { @@ -1290,8 +1283,7 @@ func TestSessionRecordingConfig(t *testing.T) { outRecConfig, err := p.cache.GetSessionRecordingConfig(ctx) require.NoError(t, err) - recConfig.SetResourceID(outRecConfig.GetResourceID()) - require.Empty(t, cmp.Diff(outRecConfig, recConfig)) + require.Empty(t, cmp.Diff(outRecConfig, recConfig, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } func TestClusterAuditConfig(t *testing.T) { @@ -1319,8 +1311,7 @@ func TestClusterAuditConfig(t *testing.T) { outAuditConfig, err := p.cache.GetClusterAuditConfig(ctx) require.NoError(t, err) - auditConfig.SetResourceID(outAuditConfig.GetResourceID()) - require.Empty(t, cmp.Diff(outAuditConfig, auditConfig)) + require.Empty(t, cmp.Diff(outAuditConfig, auditConfig, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } func TestClusterName(t *testing.T) { @@ -1347,8 +1338,7 @@ func TestClusterName(t *testing.T) { outName, err := p.cache.GetClusterName() require.NoError(t, err) - clusterName.SetResourceID(outName.GetResourceID()) - require.Empty(t, cmp.Diff(outName, clusterName)) + require.Empty(t, cmp.Diff(outName, clusterName, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } // TestNamespaces tests caching of namespaces @@ -1376,8 +1366,7 @@ func TestNamespaces(t *testing.T) { out, err := p.cache.GetNamespace(ns.GetName()) require.NoError(t, err) - ns.SetResourceID(out.GetResourceID()) - require.Empty(t, cmp.Diff(ns, out)) + require.Empty(t, cmp.Diff(ns, out, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // update namespace metadata ns.Metadata.Labels = map[string]string{"a": "b"} @@ -1397,8 +1386,7 @@ func TestNamespaces(t *testing.T) { out, err = p.cache.GetNamespace(ns.GetName()) require.NoError(t, err) - ns.SetResourceID(out.GetResourceID()) - require.Empty(t, cmp.Diff(ns, out)) + require.Empty(t, cmp.Diff(ns, out, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = p.presenceS.DeleteNamespace(ns.GetName()) require.NoError(t, err) @@ -2204,8 +2192,8 @@ func testResources[T types.Resource](t *testing.T, p *testPack, funcs testFuncs[ require.NoError(t, err) cmpOpts := []cmp.Option{ - cmpopts.IgnoreFields(types.Metadata{}, "ID"), - cmpopts.IgnoreFields(header.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), + cmpopts.IgnoreFields(header.Metadata{}, "ID", "Revision"), } // Check that the resource is now in the backend. diff --git a/lib/kube/proxy/watcher_test.go b/lib/kube/proxy/watcher_test.go index ef8f10738abd1..49e52fba753cb 100644 --- a/lib/kube/proxy/watcher_test.go +++ b/lib/kube/proxy/watcher_test.go @@ -70,7 +70,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.KubeClusters{kube0}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -87,7 +87,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.KubeClusters{kube0, kube1}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -104,7 +104,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.KubeClusters{kube0, kube1}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -121,7 +121,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.KubeClusters{kube0, kube1}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -137,7 +137,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.KubeClusters{kube0, kube1, kube2}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -154,7 +154,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.KubeClusters{kube0, kube1, kube2}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // make sure credentials were updated as well. require.Equal(t, strings.TrimPrefix(kubeMock.URL, "https://"), testCtx.KubeServer.fwd.clusterDetails["kube2"].kubeCreds.getTargetAddr()) @@ -172,7 +172,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.KubeClusters{kube0, kube2}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -186,7 +186,7 @@ func TestWatcher(t *testing.T) { select { case a := <-reconcileCh: require.Empty(t, cmp.Diff(types.KubeClusters{kube0}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") diff --git a/lib/services/access_list.go b/lib/services/access_list.go index e063d42733857..3642364cd79f5 100644 --- a/lib/services/access_list.go +++ b/lib/services/access_list.go @@ -72,6 +72,7 @@ func MarshalAccessList(accessList *accesslist.AccessList, opts ...MarshalOption) if !cfg.PreserveResourceID { copy := *accessList copy.SetResourceID(0) + copy.SetRevision("") accessList = © } return utils.FastMarshal(accessList) @@ -96,6 +97,9 @@ func UnmarshalAccessList(data []byte, opts ...MarshalOption) (*accesslist.Access if cfg.ID != 0 { accessList.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + accessList.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { accessList.SetExpiry(cfg.Expires) } @@ -138,6 +142,7 @@ func MarshalAccessListMember(member *accesslist.AccessListMember, opts ...Marsha if !cfg.PreserveResourceID { copy := *member copy.SetResourceID(0) + copy.SetRevision("") member = © } return utils.FastMarshal(member) @@ -162,6 +167,9 @@ func UnmarshalAccessListMember(data []byte, opts ...MarshalOption) (*accesslist. if cfg.ID != 0 { member.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + member.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { member.SetExpiry(cfg.Expires) } diff --git a/lib/services/access_request.go b/lib/services/access_request.go index 3b9e837c7dded..dc1404768bc94 100644 --- a/lib/services/access_request.go +++ b/lib/services/access_request.go @@ -1568,6 +1568,9 @@ func UnmarshalAccessRequest(data []byte, opts ...MarshalOption) (types.AccessReq if cfg.ID != 0 { req.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + req.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { req.SetExpiry(cfg.Expires) } @@ -1592,6 +1595,7 @@ func MarshalAccessRequest(accessRequest types.AccessRequest, opts ...MarshalOpti // to prevent unexpected data races copy := *accessRequest copy.SetResourceID(0) + copy.SetRevision("") accessRequest = © } return utils.FastMarshal(accessRequest) diff --git a/lib/services/app.go b/lib/services/app.go index dc7b1a0ef6eb0..27b21a2fdd19f 100644 --- a/lib/services/app.go +++ b/lib/services/app.go @@ -67,6 +67,7 @@ func MarshalApp(app types.Application, opts ...MarshalOption) ([]byte, error) { if !cfg.PreserveResourceID { copy := *app copy.SetResourceID(0) + copy.SetRevision("") app = © } return utils.FastMarshal(app) @@ -100,6 +101,9 @@ func UnmarshalApp(data []byte, opts ...MarshalOption) (types.Application, error) if cfg.ID != 0 { app.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + app.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { app.SetExpiry(cfg.Expires) } @@ -124,6 +128,7 @@ func MarshalAppServer(appServer types.AppServer, opts ...MarshalOption) ([]byte, if !cfg.PreserveResourceID { copy := *appServer copy.SetResourceID(0) + copy.SetRevision("") appServer = © } return utils.FastMarshal(appServer) @@ -157,6 +162,9 @@ func UnmarshalAppServer(data []byte, opts ...MarshalOption) (types.AppServer, er if cfg.ID != 0 { s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } diff --git a/lib/services/audit.go b/lib/services/audit.go index 5549b1c82ee6b..e84afea75eeec 100644 --- a/lib/services/audit.go +++ b/lib/services/audit.go @@ -59,6 +59,9 @@ func UnmarshalClusterAuditConfig(bytes []byte, opts ...MarshalOption) (types.Clu if cfg.ID != 0 { auditConfig.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + auditConfig.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { auditConfig.SetExpiry(cfg.Expires) } @@ -83,6 +86,7 @@ func MarshalClusterAuditConfig(auditConfig types.ClusterAuditConfig, opts ...Mar // to prevent unexpected data races copy := *auditConfig copy.SetResourceID(0) + copy.SetRevision("") auditConfig = © } return utils.FastMarshal(auditConfig) diff --git a/lib/services/authentication.go b/lib/services/authentication.go index fdb007e6bcea9..0ccabecabf65a 100644 --- a/lib/services/authentication.go +++ b/lib/services/authentication.go @@ -117,6 +117,9 @@ func UnmarshalAuthPreference(bytes []byte, opts ...MarshalOption) (types.AuthPre if cfg.ID != 0 { authPreference.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + authPreference.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { authPreference.SetExpiry(cfg.Expires) } diff --git a/lib/services/authority.go b/lib/services/authority.go index 0167895dbecb5..a99de420e03d3 100644 --- a/lib/services/authority.go +++ b/lib/services/authority.go @@ -47,7 +47,7 @@ import ( func CertAuthoritiesEquivalent(lhs, rhs types.CertAuthority) bool { return cmp.Equal(lhs, rhs, ignoreProtoXXXFields(), - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), // Optimize types.CAKeySet comparison. cmp.Comparer(func(a, b types.CAKeySet) bool { // Note that Clone drops XXX_ fields. And it's benchmarked that cloning @@ -473,6 +473,9 @@ func UnmarshalCertAuthority(bytes []byte, opts ...MarshalOption) (types.CertAuth if cfg.ID != 0 { ca.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + ca.SetRevision(cfg.Revision) + } // Correct problems with existing CAs that contain non-UTC times, which // causes panics when doing a gogoproto Clone; should only ever be // possible with LastRotated, but we enforce it on all the times anyway. @@ -509,6 +512,7 @@ func MarshalCertAuthority(certAuthority types.CertAuthority, opts ...MarshalOpti // to prevent unexpected data races copy := *certAuthority copy.SetResourceID(0) + copy.SetRevision("") certAuthority = © } return utils.FastMarshal(certAuthority) diff --git a/lib/services/clustername.go b/lib/services/clustername.go index bf773fad1f7f2..2dbda8ad5d0d7 100644 --- a/lib/services/clustername.go +++ b/lib/services/clustername.go @@ -58,6 +58,9 @@ func UnmarshalClusterName(bytes []byte, opts ...MarshalOption) (types.ClusterNam if cfg.ID != 0 { clusterName.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + clusterName.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { clusterName.SetExpiry(cfg.Expires) } @@ -83,6 +86,7 @@ func MarshalClusterName(clusterName types.ClusterName, opts ...MarshalOption) ([ // to prevent unexpected data races copy := *clusterName copy.SetResourceID(0) + copy.SetRevision("") clusterName = © } return utils.FastMarshal(clusterName) diff --git a/lib/services/compare.go b/lib/services/compare.go index e26020c00509e..54d1e1073db81 100644 --- a/lib/services/compare.go +++ b/lib/services/compare.go @@ -29,7 +29,7 @@ import ( func CompareResources(resA, resB types.Resource) int { equal := cmp.Equal(resA, resB, ignoreProtoXXXFields(), - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), cmpopts.IgnoreFields(types.DatabaseV3{}, "Status"), cmpopts.EquateEmpty(), ) diff --git a/lib/services/connection_diagnostic.go b/lib/services/connection_diagnostic.go index a8e15ba4492d8..6b185818a91df 100644 --- a/lib/services/connection_diagnostic.go +++ b/lib/services/connection_diagnostic.go @@ -66,6 +66,7 @@ func MarshalConnectionDiagnostic(s types.ConnectionDiagnostic, opts ...MarshalOp // to prevent unexpected data races copy := *s copy.SetResourceID(0) + copy.SetRevision("") s = © } @@ -106,6 +107,10 @@ func UnmarshalConnectionDiagnostic(data []byte, opts ...MarshalOption) (types.Co s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } + if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } diff --git a/lib/services/database.go b/lib/services/database.go index 47bcca47e2c47..8ba97583e507d 100644 --- a/lib/services/database.go +++ b/lib/services/database.go @@ -97,6 +97,7 @@ func MarshalDatabase(database types.Database, opts ...MarshalOption) ([]byte, er // to prevent unexpected data races copy := *database copy.SetResourceID(0) + copy.SetRevision("") database = © } return utils.FastMarshal(database) @@ -130,6 +131,9 @@ func UnmarshalDatabase(data []byte, opts ...MarshalOption) (types.Database, erro if cfg.ID != 0 { database.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + database.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { database.SetExpiry(cfg.Expires) } diff --git a/lib/services/databaseserver.go b/lib/services/databaseserver.go index d39a314bd3dcf..2a4fe31dceaa5 100644 --- a/lib/services/databaseserver.go +++ b/lib/services/databaseserver.go @@ -41,6 +41,7 @@ func MarshalDatabaseServer(databaseServer types.DatabaseServer, opts ...MarshalO // to prevent unexpected data races copy := *databaseServer copy.SetResourceID(0) + copy.SetRevision("") databaseServer = © } return utils.FastMarshal(databaseServer) @@ -74,6 +75,9 @@ func UnmarshalDatabaseServer(data []byte, opts ...MarshalOption) (types.Database if cfg.ID != 0 { s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } diff --git a/lib/services/databaseservice.go b/lib/services/databaseservice.go index f81b0809b9940..f6e37ca1abef1 100644 --- a/lib/services/databaseservice.go +++ b/lib/services/databaseservice.go @@ -55,6 +55,7 @@ func MarshalDatabaseService(databaseService types.DatabaseService, opts ...Marsh // to prevent unexpected data races copy := *databaseService copy.SetResourceID(0) + copy.SetRevision("") databaseService = © } return utils.FastMarshal(databaseService) @@ -88,6 +89,9 @@ func UnmarshalDatabaseService(data []byte, opts ...MarshalOption) (types.Databas if cfg.ID != 0 { s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } diff --git a/lib/services/desktop.go b/lib/services/desktop.go index b4d927412a815..8f289ad12195f 100644 --- a/lib/services/desktop.go +++ b/lib/services/desktop.go @@ -60,6 +60,7 @@ func MarshalWindowsDesktop(s types.WindowsDesktop, opts ...MarshalOption) ([]byt // to prevent unexpected data races copy := *s copy.SetResourceID(0) + copy.SetRevision("") s = © } return utils.FastMarshal(s) @@ -93,6 +94,9 @@ func UnmarshalWindowsDesktop(data []byte, opts ...MarshalOption) (types.WindowsD if cfg.ID != 0 { s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } @@ -119,6 +123,7 @@ func MarshalWindowsDesktopService(s types.WindowsDesktopService, opts ...Marshal // to prevent unexpected data races copy := *s copy.SetResourceID(0) + copy.SetRevision("") s = © } return utils.FastMarshal(s) @@ -152,6 +157,9 @@ func UnmarshalWindowsDesktopService(data []byte, opts ...MarshalOption) (types.W if cfg.ID != 0 { s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } diff --git a/lib/services/github.go b/lib/services/github.go index 8cacbea854737..c05b37f1db9fa 100644 --- a/lib/services/github.go +++ b/lib/services/github.go @@ -167,6 +167,7 @@ func marshalGithubConnector(githubConnector types.GithubConnector, opts ...Marsh // to prevent unexpected data races copy := *githubConnector copy.SetResourceID(0) + copy.SetRevision("") githubConnector = © } return utils.FastMarshal(githubConnector) diff --git a/lib/services/installer.go b/lib/services/installer.go index adc165056e872..24492972ca9ea 100644 --- a/lib/services/installer.go +++ b/lib/services/installer.go @@ -46,6 +46,9 @@ func UnmarshalInstaller(data []byte, opts ...MarshalOption) (types.Installer, er if cfg.ID != 0 { installer.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + installer.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { installer.SetExpiry(cfg.Expires) } @@ -68,6 +71,7 @@ func MarshalInstaller(installer types.Installer, opts ...MarshalOption) ([]byte, // to prevent unexpected data races copy := *installer copy.SetResourceID(0) + copy.SetRevision("") installer = © } return utils.FastMarshal(installer) diff --git a/lib/services/integration.go b/lib/services/integration.go index 4e025c7faa683..f65ae7e510a06 100644 --- a/lib/services/integration.go +++ b/lib/services/integration.go @@ -62,6 +62,7 @@ func MarshalIntegration(ig types.Integration, opts ...MarshalOption) ([]byte, er if !cfg.PreserveResourceID { copy := *g copy.SetResourceID(0) + copy.SetRevision("") g = © } return utils.FastMarshal(g) @@ -91,6 +92,9 @@ func UnmarshalIntegration(data []byte, opts ...MarshalOption) (types.Integration if cfg.ID != 0 { ig.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + ig.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { ig.SetExpiry(cfg.Expires) } diff --git a/lib/services/kubernetes.go b/lib/services/kubernetes.go index ae4cce596ac5e..cff01d058c5d3 100644 --- a/lib/services/kubernetes.go +++ b/lib/services/kubernetes.go @@ -77,6 +77,7 @@ func MarshalKubeServer(kubeServer types.KubeServer, opts ...MarshalOption) ([]by if !cfg.PreserveResourceID { copy := *server copy.SetResourceID(0) + copy.SetRevision("") server = © } return utils.FastMarshal(server) @@ -110,6 +111,9 @@ func UnmarshalKubeServer(data []byte, opts ...MarshalOption) (types.KubeServer, if cfg.ID != 0 { s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } @@ -134,6 +138,7 @@ func MarshalKubeCluster(kubeCluster types.KubeCluster, opts ...MarshalOption) ([ if !cfg.PreserveResourceID { copy := *cluster copy.SetResourceID(0) + copy.SetRevision("") cluster = © } return utils.FastMarshal(cluster) @@ -167,6 +172,9 @@ func UnmarshalKubeCluster(data []byte, opts ...MarshalOption) (types.KubeCluster if cfg.ID != 0 { s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } diff --git a/lib/services/license.go b/lib/services/license.go index e8a84228fa222..f1195a8ddd438 100644 --- a/lib/services/license.go +++ b/lib/services/license.go @@ -64,6 +64,7 @@ func MarshalLicense(license types.License, opts ...MarshalOption) ([]byte, error // to prevent unexpected data races copy := *license copy.SetResourceID(0) + copy.SetRevision("") license = © } return utils.FastMarshal(license) diff --git a/lib/services/local/access.go b/lib/services/local/access.go index 8ada6d97fbb97..34cfa8965cfd5 100644 --- a/lib/services/local/access.go +++ b/lib/services/local/access.go @@ -54,7 +54,7 @@ func (s *AccessService) GetRoles(ctx context.Context) ([]types.Role, error) { out := make([]types.Role, 0, len(result.Items)) for _, item := range result.Items { role, err := services.UnmarshalRole(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { // Try to get the role name for the error, it allows admins to take action // against the "bad" role. @@ -132,7 +132,7 @@ func (s *AccessService) GetRole(ctx context.Context, name string) (types.Role, e return nil, trace.Wrap(err) } return services.UnmarshalRole(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) } // DeleteRole deletes a role from the backend @@ -161,7 +161,7 @@ func (s *AccessService) GetLock(ctx context.Context, name string) (types.Lock, e } return nil, trace.Wrap(err) } - return services.UnmarshalLock(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + return services.UnmarshalLock(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) } // GetLocks gets all/in-force locks that match at least one of the targets when specified. @@ -174,7 +174,7 @@ func (s *AccessService) GetLocks(ctx context.Context, inForceOnly bool, targets out := []types.Lock{} for _, item := range result.Items { - lock, err := services.UnmarshalLock(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + lock, err := services.UnmarshalLock(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/services/local/access_list_test.go b/lib/services/local/access_list_test.go index ff39a128bf5c8..210ce5ab8d926 100644 --- a/lib/services/local/access_list_test.go +++ b/lib/services/local/access_list_test.go @@ -57,7 +57,7 @@ func TestAccessListCRUD(t *testing.T) { require.Empty(t, out) cmpOpts := []cmp.Option{ - cmpopts.IgnoreFields(header.Metadata{}, "ID"), + cmpopts.IgnoreFields(header.Metadata{}, "ID", "Revision"), } // Create both access lists. @@ -143,7 +143,7 @@ func TestAccessListUpsertWithMembers(t *testing.T) { accessList1 := newAccessList(t, "accessList1") cmpOpts := []cmp.Option{ - cmpopts.IgnoreFields(header.Metadata{}, "ID"), + cmpopts.IgnoreFields(header.Metadata{}, "ID", "Revision"), } t.Run("create access list", func(t *testing.T) { @@ -220,7 +220,7 @@ func TestAccessListMembersCRUD(t *testing.T) { accessList2 := newAccessList(t, "accessList2") cmpOpts := []cmp.Option{ - cmpopts.IgnoreFields(header.Metadata{}, "ID"), + cmpopts.IgnoreFields(header.Metadata{}, "ID", "Revision"), } // Create both access lists. diff --git a/lib/services/local/access_test.go b/lib/services/local/access_test.go index 5d3d61770a5cb..2aff948de1687 100644 --- a/lib/services/local/access_test.go +++ b/lib/services/local/access_test.go @@ -78,7 +78,7 @@ func TestLockCRUD(t *testing.T) { require.NoError(t, err) require.Len(t, locks, 2) require.Empty(t, cmp.Diff([]types.Lock{lock1, lock2}, locks, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } }) t.Run("GetLocks with targets", func(t *testing.T) { @@ -88,7 +88,7 @@ func TestLockCRUD(t *testing.T) { require.NoError(t, err) require.Len(t, locks, 2) require.Empty(t, cmp.Diff([]types.Lock{lock1, lock2}, locks, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Match only one of the locks. roleTarget := types.LockTarget{Role: "role-A"} @@ -96,7 +96,7 @@ func TestLockCRUD(t *testing.T) { require.NoError(t, err) require.Len(t, locks, 1) require.Empty(t, cmp.Diff([]types.Lock{lock1}, locks, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Match none of the locks. locks, err = access.GetLocks(ctx, false, roleTarget) @@ -109,7 +109,7 @@ func TestLockCRUD(t *testing.T) { lock, err := access.GetLock(ctx, lock1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(lock1, lock, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Attempt to get a nonexistent lock. _, err = access.GetLock(ctx, "lock3") @@ -166,7 +166,7 @@ func TestLockCRUD(t *testing.T) { require.NoError(t, err) require.Len(t, locks, 2) require.Empty(t, cmp.Diff(newRemoteLocks, locks, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) for _, lock := range locks { require.True(t, strings.HasPrefix(lock.GetName(), clusterName+"/")) } @@ -181,7 +181,7 @@ func TestLockCRUD(t *testing.T) { require.NoError(t, err) require.Len(t, locks, 1) require.Empty(t, cmp.Diff(newRemoteLocks, locks, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) _, err = access.GetLock(ctx, lock2.GetName()) require.Error(t, err) require.True(t, trace.IsNotFound(err)) diff --git a/lib/services/local/apps.go b/lib/services/local/apps.go index 67c73114c7317..6ff7790475906 100644 --- a/lib/services/local/apps.go +++ b/lib/services/local/apps.go @@ -46,7 +46,7 @@ func (s *AppService) GetApps(ctx context.Context) ([]types.Application, error) { apps := make([]types.Application, len(result.Items)) for i, item := range result.Items { app, err := services.UnmarshalApp(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -65,7 +65,7 @@ func (s *AppService) GetApp(ctx context.Context, name string) (types.Application return nil, trace.Wrap(err) } app, err := services.UnmarshalApp(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/services/local/apps_test.go b/lib/services/local/apps_test.go index d059c59925f99..d41ea527208c8 100644 --- a/lib/services/local/apps_test.go +++ b/lib/services/local/apps_test.go @@ -71,14 +71,14 @@ func TestAppsCRUD(t *testing.T) { out, err = service.GetApps(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Application{app1, app2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific application. app, err := service.GetApp(ctx, app2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(app2, app, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch an application that doesn't exist. @@ -96,7 +96,7 @@ func TestAppsCRUD(t *testing.T) { app, err = service.GetApp(ctx, app1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(app1, app, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete an application. @@ -105,7 +105,7 @@ func TestAppsCRUD(t *testing.T) { out, err = service.GetApps(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Application{app2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete an application that doesn't exist. diff --git a/lib/services/local/configuration.go b/lib/services/local/configuration.go index 5bc58b2502a19..b8053b22498ff 100644 --- a/lib/services/local/configuration.go +++ b/lib/services/local/configuration.go @@ -68,7 +68,7 @@ func (s *ClusterConfigurationService) GetClusterName(opts ...services.MarshalOpt return nil, trace.Wrap(err) } return services.UnmarshalClusterName(item.Value, - services.AddOptions(opts, services.WithResourceID(item.ID))...) + services.AddOptions(opts, services.WithResourceID(item.ID), services.WithRevision(item.Revision))...) } // DeleteClusterName deletes types.ClusterName from the backend. @@ -133,7 +133,7 @@ func (s *ClusterConfigurationService) GetStaticTokens() (types.StaticTokens, err return nil, trace.Wrap(err) } return services.UnmarshalStaticTokens(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) } // SetStaticTokens sets the list of static tokens used to provision nodes. @@ -178,7 +178,7 @@ func (s *ClusterConfigurationService) GetAuthPreference(ctx context.Context) (ty return nil, trace.Wrap(err) } return services.UnmarshalAuthPreference(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) } // SetAuthPreference sets the cluster authentication preferences @@ -229,7 +229,7 @@ func (s *ClusterConfigurationService) GetClusterAuditConfig(ctx context.Context, } return nil, trace.Wrap(err) } - return services.UnmarshalClusterAuditConfig(item.Value, append(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires))...) + return services.UnmarshalClusterAuditConfig(item.Value, append(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision))...) } // SetClusterAuditConfig sets the cluster audit config on the backend. @@ -273,7 +273,7 @@ func (s *ClusterConfigurationService) GetClusterNetworkingConfig(ctx context.Con } return nil, trace.Wrap(err) } - return services.UnmarshalClusterNetworkingConfig(item.Value, append(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires))...) + return services.UnmarshalClusterNetworkingConfig(item.Value, append(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision))...) } // SetClusterNetworkingConfig sets the cluster networking config @@ -323,7 +323,7 @@ func (s *ClusterConfigurationService) GetSessionRecordingConfig(ctx context.Cont } return nil, trace.Wrap(err) } - return services.UnmarshalSessionRecordingConfig(item.Value, append(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires))...) + return services.UnmarshalSessionRecordingConfig(item.Value, append(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision))...) } // SetSessionRecordingConfig sets session recording config on the backend. diff --git a/lib/services/local/connection_diagnostic.go b/lib/services/local/connection_diagnostic.go index ad6183eb1d838..03b4cdfd26171 100644 --- a/lib/services/local/connection_diagnostic.go +++ b/lib/services/local/connection_diagnostic.go @@ -135,7 +135,7 @@ func (s *ConnectionDiagnosticService) GetConnectionDiagnostic(ctx context.Contex } connectionDiagnostic, err := services.UnmarshalConnectionDiagnostic(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/services/local/databases.go b/lib/services/local/databases.go index b61a2908400c8..0f70ac031a2a5 100644 --- a/lib/services/local/databases.go +++ b/lib/services/local/databases.go @@ -46,7 +46,7 @@ func (s *DatabaseService) GetDatabases(ctx context.Context) ([]types.Database, e databases := make([]types.Database, len(result.Items)) for i, item := range result.Items { database, err := services.UnmarshalDatabase(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -65,7 +65,7 @@ func (s *DatabaseService) GetDatabase(ctx context.Context, name string) (types.D return nil, trace.Wrap(err) } database, err := services.UnmarshalDatabase(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/services/local/databases_test.go b/lib/services/local/databases_test.go index 1607532241e90..0ac705109deea 100644 --- a/lib/services/local/databases_test.go +++ b/lib/services/local/databases_test.go @@ -84,14 +84,14 @@ func TestDatabasesCRUD(t *testing.T) { out, err = service.GetDatabases(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Database{dbBadURI, db1, db2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific database. db, err := service.GetDatabase(ctx, db2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(db2, db, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch a database that doesn't exist. @@ -109,7 +109,7 @@ func TestDatabasesCRUD(t *testing.T) { db, err = service.GetDatabase(ctx, db1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(db1, db, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete a database. @@ -118,7 +118,7 @@ func TestDatabasesCRUD(t *testing.T) { out, err = service.GetDatabases(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Database{dbBadURI, db2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete a database that doesn't exist. diff --git a/lib/services/local/desktops.go b/lib/services/local/desktops.go index f90a3c6573037..a5a0660602fa9 100644 --- a/lib/services/local/desktops.go +++ b/lib/services/local/desktops.go @@ -47,7 +47,7 @@ func (s *WindowsDesktopService) GetWindowsDesktops(ctx context.Context, filter t var desktops []types.WindowsDesktop for _, item := range result.Items { desktop, err := services.UnmarshalWindowsDesktop(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -184,7 +184,7 @@ func (s *WindowsDesktopService) ListWindowsDesktops(ctx context.Context, req typ } desktop, err := services.UnmarshalWindowsDesktop(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return false, trace.Wrap(err) } @@ -249,7 +249,7 @@ func (s *WindowsDesktopService) ListWindowsDesktopServices(ctx context.Context, } desktop, err := services.UnmarshalWindowsDesktopService(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return false, trace.Wrap(err) } diff --git a/lib/services/local/desktops_test.go b/lib/services/local/desktops_test.go index dbd3fc7ef0b4b..3a13df56659f1 100644 --- a/lib/services/local/desktops_test.go +++ b/lib/services/local/desktops_test.go @@ -76,7 +76,7 @@ func TestListWindowsDesktops(t *testing.T) { require.NoError(t, err) require.Empty(t, out.NextKey) require.Empty(t, cmp.Diff([]types.WindowsDesktop{d1, d2, d3}, out.Desktops, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Test pagination. diff --git a/lib/services/local/dynamic_access.go b/lib/services/local/dynamic_access.go index e48e20226427c..6c15bfbe6b20a 100644 --- a/lib/services/local/dynamic_access.go +++ b/lib/services/local/dynamic_access.go @@ -457,6 +457,7 @@ func itemToAccessRequest(item backend.Item, opts ...services.MarshalOption) (typ opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) req, err := services.UnmarshalAccessRequest( item.Value, @@ -491,6 +492,7 @@ func itemToPluginData(item backend.Item) (types.PluginData, error) { item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/services/local/events.go b/lib/services/local/events.go index 4e8d47c5ffa55..7b45f38254f8d 100644 --- a/lib/services/local/events.go +++ b/lib/services/local/events.go @@ -358,7 +358,7 @@ func (p *certAuthorityParser) parse(event backend.Event) (types.Resource, error) }, nil case types.OpPut: ca, err := services.UnmarshalCertAuthority(event.Item.Value, - services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires)) + services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), services.WithRevision(event.Item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -389,6 +389,7 @@ func (p *provisionTokenParser) parse(event backend.Event) (types.Resource, error token, err := services.UnmarshalProvisionToken(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -422,6 +423,7 @@ func (p *staticTokensParser) parse(event backend.Event) (types.Resource, error) tokens, err := services.UnmarshalStaticTokens(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -456,6 +458,7 @@ func (p *clusterAuditConfigParser) parse(event backend.Event) (types.Resource, e event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -490,6 +493,7 @@ func (p *clusterNetworkingConfigParser) parse(event backend.Event) (types.Resour event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -524,6 +528,7 @@ func (p *authPreferenceParser) parse(event backend.Event) (types.Resource, error event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -558,6 +563,7 @@ func (p *uiConfigParser) parse(event backend.Event) (types.Resource, error) { event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -592,6 +598,7 @@ func (p *sessionRecordingConfigParser) parse(event backend.Event) (types.Resourc event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -625,6 +632,7 @@ func (p *clusterNameParser) parse(event backend.Event) (types.Resource, error) { clusterName, err := services.UnmarshalClusterName(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -665,6 +673,7 @@ func (p *namespaceParser) parse(event backend.Event) (types.Resource, error) { namespace, err := services.UnmarshalNamespace(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -693,6 +702,7 @@ func (p *roleParser) parse(event backend.Event) (types.Resource, error) { resource, err := services.UnmarshalRole(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -779,6 +789,7 @@ func (p *userParser) parse(event backend.Event) (types.Resource, error) { resource, err := services.UnmarshalUser(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -861,6 +872,7 @@ func (p *tunnelConnectionParser) parse(event backend.Event) (types.Resource, err resource, err := services.UnmarshalTunnelConnection(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -889,6 +901,7 @@ func (p *reverseTunnelParser) parse(event backend.Event) (types.Resource, error) resource, err := services.UnmarshalReverseTunnel(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -993,6 +1006,7 @@ func (p *webSessionParser) parse(event backend.Event) (types.Resource, error) { resource, err := services.UnmarshalWebSession(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -1021,6 +1035,7 @@ func (p *webTokenParser) parse(event backend.Event) (types.Resource, error) { resource, err := services.UnmarshalWebToken(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -1062,6 +1077,7 @@ func (p *kubeServerParser) parse(event backend.Event) (types.Resource, error) { event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1099,6 +1115,7 @@ func (p *databaseServerParser) parse(event backend.Event) (types.Resource, error event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1124,6 +1141,7 @@ func (p *databaseServiceParser) parse(event backend.Event) (types.Resource, erro event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1148,6 +1166,7 @@ func (p *kubeClusterParser) parse(event backend.Event) (types.Resource, error) { return services.UnmarshalKubeCluster(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1172,6 +1191,7 @@ func (p *appParser) parse(event backend.Event) (types.Resource, error) { return services.UnmarshalApp(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1196,6 +1216,7 @@ func (p *databaseParser) parse(event backend.Event) (types.Resource, error) { return services.UnmarshalDatabase(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1211,6 +1232,7 @@ func parseServer(event backend.Event, kind string) (types.Resource, error) { kind, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -1247,6 +1269,7 @@ func (p *remoteClusterParser) parse(event backend.Event) (types.Resource, error) resource, err := services.UnmarshalRemoteCluster(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -1276,6 +1299,7 @@ func (p *lockParser) parse(event backend.Event) (types.Resource, error) { event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1308,6 +1332,7 @@ func (p *networkRestrictionsParser) parse(event backend.Event) (types.Resource, resource, err := services.UnmarshalNetworkRestrictions(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -1337,6 +1362,7 @@ func (p *windowsDesktopServicesParser) parse(event backend.Event) (types.Resourc event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1374,6 +1400,7 @@ func (p *windowsDesktopsParser) parse(event backend.Event) (types.Resource, erro event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1402,6 +1429,7 @@ func (p *installerParser) parse(event backend.Event) (types.Resource, error) { inst, err := services.UnmarshalInstaller(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -1436,6 +1464,7 @@ func (p *pluginParser) parse(event backend.Event) (types.Resource, error) { plugin, err := services.UnmarshalPlugin(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -1464,6 +1493,7 @@ func (p *samlIDPServiceProviderParser) parse(event backend.Event) (types.Resourc return services.UnmarshalSAMLIdPServiceProvider(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1488,6 +1518,7 @@ func (p *userGroupParser) parse(event backend.Event) (types.Resource, error) { return services.UnmarshalUserGroup(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1512,6 +1543,7 @@ func (p *oktaImportRuleParser) parse(event backend.Event) (types.Resource, error return services.UnmarshalOktaImportRule(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1536,6 +1568,7 @@ func (p *oktaAssignmentParser) parse(event backend.Event) (types.Resource, error return services.UnmarshalOktaAssignment(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1560,6 +1593,7 @@ func (p *integrationParser) parse(event backend.Event) (types.Resource, error) { return services.UnmarshalIntegration(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1619,6 +1653,7 @@ func (p *accessListParser) parse(event backend.Event) (types.Resource, error) { return services.UnmarshalAccessList(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1643,6 +1678,7 @@ func (p *userLoginStateParser) parse(event backend.Event) (types.Resource, error return services.UnmarshalUserLoginState(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) @@ -1667,6 +1703,7 @@ func (p *accessListMemberParser) parse(event backend.Event) (types.Resource, err return services.UnmarshalAccessListMember(event.Item.Value, services.WithResourceID(event.Item.ID), services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), ) default: return nil, trace.BadParameter("event %v is not supported", event.Type) diff --git a/lib/services/local/generic/generic.go b/lib/services/local/generic/generic.go index 2a8eaba6e53c9..29a79190a31f3 100644 --- a/lib/services/local/generic/generic.go +++ b/lib/services/local/generic/generic.go @@ -183,7 +183,7 @@ func (s *Service[T]) GetResource(ctx context.Context, name string) (resource T, return resource, trace.Wrap(err) } resource, err = s.unmarshalFunc(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) return resource, trace.Wrap(err) } @@ -257,7 +257,7 @@ func (s *Service[T]) UpdateAndSwapResource(ctx context.Context, name string, mod } resource, err := s.unmarshalFunc(existingItem.Value, - services.WithResourceID(existingItem.ID), services.WithExpires(existingItem.Expires)) + services.WithResourceID(existingItem.ID), services.WithExpires(existingItem.Expires), services.WithRevision(existingItem.Revision)) if err != nil { return trace.Wrap(err) } diff --git a/lib/services/local/generic/generic_test.go b/lib/services/local/generic/generic_test.go index 1e5efa1adc2ec..b5d03f2919cb8 100644 --- a/lib/services/local/generic/generic_test.go +++ b/lib/services/local/generic/generic_test.go @@ -87,6 +87,9 @@ func unmarshalResource(data []byte, opts ...services.MarshalOption) (*testResour if cfg.ID != 0 { r.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + r.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { r.SetExpiry(cfg.Expires) } @@ -134,7 +137,7 @@ func TestGenericCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]*testResource{r1, r2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a paginated list of resources. @@ -153,21 +156,21 @@ func TestGenericCRUD(t *testing.T) { require.Equal(t, 2, numPages) require.Empty(t, cmp.Diff([]*testResource{r1, r2}, paginatedOut, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a list of all resources allResources, err := service.GetResources(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff(paginatedOut, allResources, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific service provider. r, err := service.GetResource(ctx, r2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(r2, r, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch a resource that doesn't exist. @@ -185,7 +188,7 @@ func TestGenericCRUD(t *testing.T) { r, err = service.GetResource(ctx, r1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(r1, r, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Update a resource that doesn't exist. @@ -200,7 +203,7 @@ func TestGenericCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]*testResource{r2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Upsert a resource (create). @@ -210,7 +213,7 @@ func TestGenericCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]*testResource{r1, r2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Upsert a resource (update). @@ -221,7 +224,7 @@ func TestGenericCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]*testResource{r1, r2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Update and swap a value @@ -234,7 +237,7 @@ func TestGenericCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]*testResource{r1, r2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete a resource that doesn't exist. @@ -249,7 +252,7 @@ func TestGenericCRUD(t *testing.T) { r, err = unmarshalResource(item.Value) require.NoError(t, err) require.Empty(t, cmp.Diff(r1, r, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) return nil diff --git a/lib/services/local/kube.go b/lib/services/local/kube.go index d69d2b2b11a24..349b53ef05660 100644 --- a/lib/services/local/kube.go +++ b/lib/services/local/kube.go @@ -46,7 +46,7 @@ func (s *KubernetesService) GetKubernetesClusters(ctx context.Context) ([]types. kubeClusters := make([]types.KubeCluster, len(result.Items)) for i, item := range result.Items { cluster, err := services.UnmarshalKubeCluster(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -65,7 +65,7 @@ func (s *KubernetesService) GetKubernetesCluster(ctx context.Context, name strin return nil, trace.Wrap(err) } cluster, err := services.UnmarshalKubeCluster(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/services/local/kube_test.go b/lib/services/local/kube_test.go index 25a9b6b90019a..8346ce856291d 100644 --- a/lib/services/local/kube_test.go +++ b/lib/services/local/kube_test.go @@ -67,14 +67,14 @@ func TestKubernetesCRUD(t *testing.T) { out, err = service.GetKubernetesClusters(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.KubeCluster{kubeCluster1, kubeCluster2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific Kubernetes. cluster, err := service.GetKubernetesCluster(ctx, kubeCluster2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(kubeCluster2, cluster, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch a Kubernetes that doesn't exist. @@ -92,7 +92,7 @@ func TestKubernetesCRUD(t *testing.T) { cluster, err = service.GetKubernetesCluster(ctx, kubeCluster1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(kubeCluster1, cluster, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete a Kubernetes. @@ -101,7 +101,7 @@ func TestKubernetesCRUD(t *testing.T) { out, err = service.GetKubernetesClusters(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.KubeCluster{kubeCluster2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete a Kubernetes that doesn't exist. diff --git a/lib/services/local/okta_test.go b/lib/services/local/okta_test.go index 0feccbfda2648..cb7f4aadb5cfd 100644 --- a/lib/services/local/okta_test.go +++ b/lib/services/local/okta_test.go @@ -118,13 +118,13 @@ func TestOktaImportRuleCRUD(t *testing.T) { importRule, err := service.CreateOktaImportRule(ctx, importRule1) require.NoError(t, err) require.Empty(t, cmp.Diff(importRule1, importRule, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) importRule, err = service.CreateOktaImportRule(ctx, importRule2) require.NoError(t, err) require.Empty(t, cmp.Diff(importRule2, importRule, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch all import rules. @@ -132,7 +132,7 @@ func TestOktaImportRuleCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]types.OktaImportRule{importRule1, importRule2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a paginated list of import rules @@ -149,14 +149,14 @@ func TestOktaImportRuleCRUD(t *testing.T) { require.Len(t, paginatedOut, 2) require.Empty(t, cmp.Diff([]types.OktaImportRule{importRule1, importRule2}, paginatedOut, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific import rule. importRule, err = service.GetOktaImportRule(ctx, importRule2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(importRule2, importRule, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch an import rule that doesn't exist. @@ -174,7 +174,7 @@ func TestOktaImportRuleCRUD(t *testing.T) { importRule, err = service.GetOktaImportRule(ctx, importRule1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(importRule1, importRule, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete an import rule @@ -184,7 +184,7 @@ func TestOktaImportRuleCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]types.OktaImportRule{importRule2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete an import rule that doesn't exist. @@ -330,13 +330,13 @@ func TestOktaAssignmentCRUD(t *testing.T) { assignment, err := service.CreateOktaAssignment(ctx, assignment1) require.NoError(t, err) require.Empty(t, cmp.Diff(assignment1, assignment, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) assignment, err = service.CreateOktaAssignment(ctx, assignment2) require.NoError(t, err) require.Empty(t, cmp.Diff(assignment2, assignment, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch all assignments. @@ -344,7 +344,7 @@ func TestOktaAssignmentCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]types.OktaAssignment{assignment1, assignment2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a paginated list of assignments @@ -363,14 +363,14 @@ func TestOktaAssignmentCRUD(t *testing.T) { require.Equal(t, 2, numPages) require.Empty(t, cmp.Diff([]types.OktaAssignment{assignment1, assignment2}, paginatedOut, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific assignment. assignment, err = service.GetOktaAssignment(ctx, assignment2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(assignment2, assignment, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch an assignment that doesn't exist. @@ -392,7 +392,7 @@ func TestOktaAssignmentCRUD(t *testing.T) { assignment, err = service.GetOktaAssignment(ctx, assignment1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(assignment1, assignment, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fail to update the status for an assignment due to a bad transition. @@ -410,7 +410,7 @@ func TestOktaAssignmentCRUD(t *testing.T) { assignment, err = service.GetOktaAssignment(ctx, assignment1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(assignment1, assignment, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete an assignment @@ -420,7 +420,7 @@ func TestOktaAssignmentCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]types.OktaAssignment{assignment2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete an assignment that doesn't exist. diff --git a/lib/services/local/plugin_static_credentials_test.go b/lib/services/local/plugin_static_credentials_test.go index 12d80ad6e6476..c6250022060b2 100644 --- a/lib/services/local/plugin_static_credentials_test.go +++ b/lib/services/local/plugin_static_credentials_test.go @@ -83,7 +83,7 @@ func TestPluginStaticCredentialsCRUD(t *testing.T) { cred, err := service.GetPluginStaticCredentials(ctx, cred1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(cred1, cred, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch a static credential that doesn't exist. @@ -101,7 +101,7 @@ func TestPluginStaticCredentialsCRUD(t *testing.T) { }) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.PluginStaticCredentials{cred1}, creds, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) creds, err = service.GetPluginStaticCredentialsByLabels(ctx, map[string]string{ @@ -109,7 +109,7 @@ func TestPluginStaticCredentialsCRUD(t *testing.T) { }) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.PluginStaticCredentials{cred1, cred2}, creds, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) creds, err = service.GetPluginStaticCredentialsByLabels(ctx, map[string]string{ @@ -118,7 +118,7 @@ func TestPluginStaticCredentialsCRUD(t *testing.T) { }) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.PluginStaticCredentials{cred2}, creds, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete a static credential. diff --git a/lib/services/local/plugins.go b/lib/services/local/plugins.go index df44362d81f35..16722fe11e98e 100644 --- a/lib/services/local/plugins.go +++ b/lib/services/local/plugins.go @@ -92,7 +92,7 @@ func (s *PluginsService) GetPlugin(ctx context.Context, name string, withSecrets } plugin, err := services.UnmarshalPlugin(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -139,7 +139,7 @@ func (s *PluginsService) ListPlugins(ctx context.Context, limit int, startKey st plugins := make([]types.Plugin, 0, len(result.Items)) for _, item := range result.Items { - plugin, err := services.UnmarshalPlugin(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + plugin, err := services.UnmarshalPlugin(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, "", trace.Wrap(err) } @@ -199,7 +199,7 @@ func (s *PluginsService) updateAndSwap(ctx context.Context, name string, modify } plugin, err := services.UnmarshalPlugin(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return trace.Wrap(err) } diff --git a/lib/services/local/plugins_test.go b/lib/services/local/plugins_test.go index 88d7ffc321d1c..f34fb67966879 100644 --- a/lib/services/local/plugins_test.go +++ b/lib/services/local/plugins_test.go @@ -76,14 +76,14 @@ func TestPluginsCRUD(t *testing.T) { out, err = service.GetPlugins(ctx, true) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Plugin{plugin1, plugin2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific plugin. cluster, err := service.GetPlugin(ctx, plugin2.GetName(), true) require.NoError(t, err) require.Empty(t, cmp.Diff(plugin2, cluster, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch a plugin that doesn't exist. @@ -104,7 +104,7 @@ func TestPluginsCRUD(t *testing.T) { require.NoError(t, err) // Fields other than Status should remain unchanged require.Empty(t, cmp.Diff(plugin1, cluster, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), cmpopts.IgnoreFields(types.PluginV1{}, "Status"), )) require.Empty(t, cmp.Diff(status, cluster.GetStatus())) @@ -124,7 +124,7 @@ func TestPluginsCRUD(t *testing.T) { out, err = service.GetPlugins(ctx, true) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.Plugin{plugin2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete a plugin that doesn't exist. @@ -192,7 +192,7 @@ func TestListPlugins(t *testing.T) { fetchedPlugins = append(fetchedPlugins, page3...) require.Empty(t, cmp.Diff(insertedPlugins, fetchedPlugins, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) }) @@ -202,7 +202,7 @@ func TestListPlugins(t *testing.T) { require.Empty(t, nextKey) require.Empty(t, cmp.Diff(insertedPlugins, fetchedPlugins, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) }) } diff --git a/lib/services/local/presence.go b/lib/services/local/presence.go index 1b5dc6f27b17b..9b0360a58370d 100644 --- a/lib/services/local/presence.go +++ b/lib/services/local/presence.go @@ -76,7 +76,7 @@ func (s *PresenceService) GetNamespaces() ([]types.Namespace, error) { continue } ns, err := services.UnmarshalNamespace( - item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -122,7 +122,7 @@ func (s *PresenceService) GetNamespace(name string) (*types.Namespace, error) { return nil, trace.Wrap(err) } return services.UnmarshalNamespace( - item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) } // DeleteNamespace deletes a namespace with all the keys from the backend @@ -149,6 +149,7 @@ func (s *PresenceService) GetServerInfos(ctx context.Context) stream.Stream[type item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) if err != nil { s.log.Warnf("Skipping server info at %s, failed to unmarshal: %v", item.Key, err) @@ -171,7 +172,7 @@ func (s *PresenceService) GetServerInfo(ctx context.Context, name string) (types return nil, trace.Wrap(err) } si, err := services.UnmarshalServerInfo( - item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision), ) return si, trace.Wrap(err) } @@ -237,6 +238,7 @@ func (s *PresenceService) getServers(ctx context.Context, kind, prefix string) ( item.Value, kind, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -291,6 +293,7 @@ func (s *PresenceService) GetNode(ctx context.Context, namespace, name string) ( types.KindNode, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) } @@ -315,6 +318,7 @@ func (s *PresenceService) GetNodes(ctx context.Context, namespace string) ([]typ []services.MarshalOption{ services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), }..., ) if err != nil { @@ -436,7 +440,7 @@ func (s *PresenceService) GetReverseTunnel(name string, opts ...services.Marshal return nil, trace.Wrap(err) } return services.UnmarshalReverseTunnel(item.Value, - services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires))...) + services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision))...) } // GetReverseTunnels returns a list of registered servers @@ -452,7 +456,7 @@ func (s *PresenceService) GetReverseTunnels(ctx context.Context, opts ...service } for i, item := range result.Items { tunnel, err := services.UnmarshalReverseTunnel( - item.Value, services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires))...) + item.Value, services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision))...) if err != nil { return nil, trace.Wrap(err) } @@ -499,7 +503,7 @@ func (s *PresenceService) GetTrustedCluster(ctx context.Context, name string) (t if err != nil { return nil, trace.Wrap(err) } - return services.UnmarshalTrustedCluster(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + return services.UnmarshalTrustedCluster(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) } // GetTrustedClusters returns all TrustedClusters in the backend. @@ -512,7 +516,7 @@ func (s *PresenceService) GetTrustedClusters(ctx context.Context) ([]types.Trust out := make([]types.TrustedCluster, len(result.Items)) for i, item := range result.Items { tc, err := services.UnmarshalTrustedCluster(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -569,7 +573,7 @@ func (s *PresenceService) GetTunnelConnection(clusterName, connectionName string return nil, trace.Wrap(err) } conn, err := services.UnmarshalTunnelConnection(item.Value, - services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires))...) + services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision))...) if err != nil { return nil, trace.Wrap(err) } @@ -589,7 +593,7 @@ func (s *PresenceService) GetTunnelConnections(clusterName string, opts ...servi conns := make([]types.TunnelConnection, len(result.Items)) for i, item := range result.Items { conn, err := services.UnmarshalTunnelConnection(item.Value, - services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires))...) + services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision))...) if err != nil { return nil, trace.Wrap(err) } @@ -612,7 +616,8 @@ func (s *PresenceService) GetAllTunnelConnections(opts ...services.MarshalOption conn, err := services.UnmarshalTunnelConnection(item.Value, services.AddOptions(opts, services.WithResourceID(item.ID), - services.WithExpires(item.Expires))...) + services.WithExpires(item.Expires), + services.WithRevision(item.Revision))...) if err != nil { return nil, trace.Wrap(err) } @@ -714,7 +719,7 @@ func (s *PresenceService) GetRemoteClusters(opts ...services.MarshalOption) ([]t clusters := make([]types.RemoteCluster, len(result.Items)) for i, item := range result.Items { cluster, err := services.UnmarshalRemoteCluster(item.Value, - services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires))...) + services.AddOptions(opts, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision))...) if err != nil { return nil, trace.Wrap(err) } @@ -736,7 +741,7 @@ func (s *PresenceService) getRemoteCluster(ctx context.Context, clusterName stri return nil, nil, trace.Wrap(err) } rc, err := services.UnmarshalRemoteCluster(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, nil, trace.Wrap(err) } @@ -1145,7 +1150,8 @@ func (s *PresenceService) getKubernetesServers(ctx context.Context) ([]types.Kub server, err := services.UnmarshalKubeServer( item.Value, services.WithResourceID(item.ID), - services.WithExpires(item.Expires)) + services.WithExpires(item.Expires), + services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -1170,7 +1176,8 @@ func (s *PresenceService) GetDatabaseServers(ctx context.Context, namespace stri item.Value, services.AddOptions(opts, services.WithResourceID(item.ID), - services.WithExpires(item.Expires))...) + services.WithExpires(item.Expires), + services.WithRevision(item.Revision))...) if err != nil { return nil, trace.Wrap(err) } @@ -1263,7 +1270,8 @@ func (s *PresenceService) getApplicationServers(ctx context.Context, namespace s server, err := services.UnmarshalAppServer( item.Value, services.WithResourceID(item.ID), - services.WithExpires(item.Expires)) + services.WithExpires(item.Expires), + services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -1371,6 +1379,7 @@ func (s *PresenceService) GetWindowsDesktopServices(ctx context.Context) ([]type item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -1389,6 +1398,7 @@ func (s *PresenceService) GetWindowsDesktopService(ctx context.Context, name str result.Value, services.WithResourceID(result.ID), services.WithExpires(result.Expires), + services.WithRevision(result.Revision), ) if err != nil { return nil, trace.Wrap(err) @@ -1480,7 +1490,8 @@ func (s *PresenceService) GetUserGroups(ctx context.Context, opts ...services.Ma item.Value, services.AddOptions(opts, services.WithResourceID(item.ID), - services.WithExpires(item.Expires))...) + services.WithExpires(item.Expires), + services.WithRevision(item.Revision))...) if err != nil { return nil, trace.Wrap(err) } @@ -1807,6 +1818,7 @@ func backendItemToDatabaseServer(item backend.Item) (types.ResourceWithLabels, e item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) } @@ -1817,6 +1829,7 @@ func backendItemToDatabaseService(item backend.Item) (types.ResourceWithLabels, item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) } @@ -1827,6 +1840,8 @@ func backendItemToApplicationServer(item backend.Item) (types.ResourceWithLabels item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), + services.WithRevision(item.Revision), ) } @@ -1837,6 +1852,7 @@ func backendItemToKubernetesServer(item backend.Item) (types.ResourceWithLabels, item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) } @@ -1849,6 +1865,7 @@ func backendItemToServer(kind string) backendItemToResourceFunc { item.Value, kind, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) } } @@ -1860,6 +1877,7 @@ func backendItemToWindowsDesktopService(item backend.Item) (types.ResourceWithLa item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) } @@ -1870,6 +1888,7 @@ func backendItemToUserGroup(item backend.Item) (types.ResourceWithLabels, error) item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) } diff --git a/lib/services/local/presence_test.go b/lib/services/local/presence_test.go index a0a495e65fe9d..40a09b801997c 100644 --- a/lib/services/local/presence_test.go +++ b/lib/services/local/presence_test.go @@ -252,7 +252,7 @@ func TestApplicationServersCRUD(t *testing.T) { servers := types.AppServers(out) require.NoError(t, servers.SortByCustom(types.SortBy{Field: types.ResourceMetadataName})) require.Empty(t, cmp.Diff([]types.AppServer{serverA, serverB}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Delete an app server. err = presence.DeleteApplicationServer(ctx, serverA.GetNamespace(), serverA.GetHostID(), serverA.GetName()) @@ -262,7 +262,7 @@ func TestApplicationServersCRUD(t *testing.T) { out, err = presence.GetApplicationServers(ctx, apidefaults.Namespace) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.AppServer{serverB}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Upsert server with TTL. serverA.SetExpiry(clock.Now().UTC().Add(time.Hour)) @@ -337,8 +337,7 @@ func TestDatabaseServersCRUD(t *testing.T) { // Check again, expect a single server to be found. out, err = presence.GetDatabaseServers(ctx, server.GetNamespace()) require.NoError(t, err) - server.SetResourceID(out[0].GetResourceID()) - require.EqualValues(t, []types.DatabaseServer{server}, out) + require.Empty(t, cmp.Diff([]types.DatabaseServer{server}, out, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Make sure can't delete with empty namespace or host ID or name. err = presence.DeleteDatabaseServer(ctx, server.GetNamespace(), server.GetHostID(), "") @@ -424,7 +423,7 @@ func TestNodeCRUD(t *testing.T) { require.NoError(t, err) require.EqualValues(t, len(nodes), 2) require.Empty(t, cmp.Diff([]types.Server{node1, node2}, nodes, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // GetNodes should fail if namespace isn't provided _, err = presence.GetNodes(ctx, "") @@ -436,7 +435,7 @@ func TestNodeCRUD(t *testing.T) { node, err := presence.GetNode(ctx, apidefaults.Namespace, "node1") require.NoError(t, err) require.Empty(t, cmp.Diff(node1, node, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // GetNode should fail if node name isn't provided _, err = presence.GetNode(ctx, apidefaults.Namespace, "") @@ -1374,15 +1373,15 @@ func TestServerInfoCRUD(t *testing.T) { out, err = stream.Collect(presence.GetServerInfos(ctx)) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.ServerInfo{serverInfoA, serverInfoB}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) outInfo, err := presence.GetServerInfo(ctx, serverInfoA.GetName()) require.NoError(t, err) - require.Empty(t, cmp.Diff(serverInfoA, outInfo, cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + require.Empty(t, cmp.Diff(serverInfoA, outInfo, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) outInfo, err = presence.GetServerInfo(ctx, serverInfoB.GetName()) require.NoError(t, err) - require.Empty(t, cmp.Diff(serverInfoB, outInfo, cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + require.Empty(t, cmp.Diff(serverInfoB, outInfo, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) _, err = presence.GetServerInfo(ctx, "nonexistant") require.True(t, trace.IsNotFound(err)) @@ -1392,7 +1391,7 @@ func TestServerInfoCRUD(t *testing.T) { out, err = stream.Collect(presence.GetServerInfos(ctx)) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.ServerInfo{serverInfoB}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Update server info. serverInfoB.SetStaticLabels(map[string]string{ @@ -1403,7 +1402,7 @@ func TestServerInfoCRUD(t *testing.T) { out, err = stream.Collect(presence.GetServerInfos(ctx)) require.NoError(t, err) require.Empty(t, cmp.Diff([]types.ServerInfo{serverInfoB}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Delete all server infos. require.NoError(t, presence.DeleteAllServerInfos(ctx)) diff --git a/lib/services/local/provisioning.go b/lib/services/local/provisioning.go index 5f0fe8696412d..891297e1922b5 100644 --- a/lib/services/local/provisioning.go +++ b/lib/services/local/provisioning.go @@ -100,7 +100,7 @@ func (s *ProvisioningService) GetToken(ctx context.Context, token string) (types return nil, trace.Wrap(err) } - return services.UnmarshalProvisionToken(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + return services.UnmarshalProvisionToken(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) } // DeleteToken deletes a token by ID @@ -128,6 +128,7 @@ func (s *ProvisioningService) GetTokens(ctx context.Context) ([]types.ProvisionT item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/services/local/resource.go b/lib/services/local/resource.go index 717f5b186b91d..c38d17b207579 100644 --- a/lib/services/local/resource.go +++ b/lib/services/local/resource.go @@ -141,6 +141,7 @@ func itemToUser(item backend.Item) (types.User, error) { item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), + services.WithRevision(item.Revision), ) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/services/local/resource_test.go b/lib/services/local/resource_test.go index d907f48c35aa6..d3fc1608cf953 100644 --- a/lib/services/local/resource_test.go +++ b/lib/services/local/resource_test.go @@ -23,10 +23,10 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" "golang.org/x/crypto/bcrypt" - "google.golang.org/protobuf/testing/protocmp" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" @@ -50,7 +50,7 @@ func TestCreateResourcesProvisionToken(t *testing.T) { s := NewProvisioningService(tt.bk) fetchedToken, err := s.GetToken(ctx, "foo") require.NoError(t, err) - require.Empty(t, cmp.Diff(token, fetchedToken)) + require.Empty(t, cmp.Diff(token, fetchedToken, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } func TestUserResource(t *testing.T) { @@ -248,5 +248,5 @@ func TestBootstrapLock(t *testing.T) { l, err := tt.suite.Access.GetLock(ctx, "test") require.NoError(t, err) - require.Empty(t, cmp.Diff(nl, l, protocmp.Transform())) + require.Empty(t, cmp.Diff(nl, l, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } diff --git a/lib/services/local/restrictions.go b/lib/services/local/restrictions.go index 28ec416964039..f5145036794c9 100644 --- a/lib/services/local/restrictions.go +++ b/lib/services/local/restrictions.go @@ -66,7 +66,7 @@ func (s *RestrictionsService) GetNetworkRestrictions(ctx context.Context) (types return nil, trace.Wrap(err) } return services.UnmarshalNetworkRestrictions(item.Value, - services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) } // SetNetworkRestrictions upserts NetworkRestrictions diff --git a/lib/services/local/saml_idp_service_provider_test.go b/lib/services/local/saml_idp_service_provider_test.go index 4a90318e8bd77..29dab40861792 100644 --- a/lib/services/local/saml_idp_service_provider_test.go +++ b/lib/services/local/saml_idp_service_provider_test.go @@ -122,7 +122,7 @@ func TestSAMLIdPServiceProviderCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]types.SAMLIdPServiceProvider{sp1, sp2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a paginated list of service providers @@ -141,14 +141,14 @@ func TestSAMLIdPServiceProviderCRUD(t *testing.T) { require.Equal(t, 2, numPages) require.Empty(t, cmp.Diff([]types.SAMLIdPServiceProvider{sp1, sp2}, paginatedOut, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific service provider. sp, err := service.GetSAMLIdPServiceProvider(ctx, sp2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(sp2, sp, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch a service provider that doesn't exist. @@ -167,7 +167,7 @@ func TestSAMLIdPServiceProviderCRUD(t *testing.T) { sp, err = service.GetSAMLIdPServiceProvider(ctx, sp1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(sp1, sp, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Update a service provider to an existing entity ID. @@ -193,7 +193,7 @@ func TestSAMLIdPServiceProviderCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]types.SAMLIdPServiceProvider{sp2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete a service provider that doesn't exist. diff --git a/lib/services/local/trust.go b/lib/services/local/trust.go index 03a69038557b4..46a916e6b1494 100644 --- a/lib/services/local/trust.go +++ b/lib/services/local/trust.go @@ -177,7 +177,7 @@ func (s *CA) ActivateCertAuthority(id types.CertAuthID) error { } certAuthority, err := services.UnmarshalCertAuthority( - item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return trace.Wrap(err) } @@ -240,7 +240,7 @@ func (s *CA) GetCertAuthority(ctx context.Context, id types.CertAuthID, loadSign if err != nil { return nil, trace.Wrap(err) } - ca, err := services.UnmarshalCertAuthority(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + ca, err := services.UnmarshalCertAuthority(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -275,7 +275,7 @@ func (s *CA) GetCertAuthorities(ctx context.Context, caType types.CertAuthType, // Marshal values into a []types.CertAuthority slice. cas := make([]types.CertAuthority, len(result.Items)) for i, item := range result.Items { - ca, err := services.UnmarshalCertAuthority(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + ca, err := services.UnmarshalCertAuthority(item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/services/local/user_login_state_test.go b/lib/services/local/user_login_state_test.go index 9d3887b9a8fbc..f8ff64c89e8f8 100644 --- a/lib/services/local/user_login_state_test.go +++ b/lib/services/local/user_login_state_test.go @@ -50,7 +50,7 @@ func TestUserLoginStateCRUD(t *testing.T) { state2 := newUserLoginState(t, "state2") cmpOpts := []cmp.Option{ - cmpopts.IgnoreFields(header.Metadata{}, "ID"), + cmpopts.IgnoreFields(header.Metadata{}, "ID", "Revision"), } // Neither state should exist. diff --git a/lib/services/local/usergroup_test.go b/lib/services/local/usergroup_test.go index f3f26a5c244fa..b6f9a7c9d49a3 100644 --- a/lib/services/local/usergroup_test.go +++ b/lib/services/local/usergroup_test.go @@ -72,7 +72,7 @@ func TestUserGroupCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]types.UserGroup{g1, g2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a paginated list of user groups. @@ -91,14 +91,14 @@ func TestUserGroupCRUD(t *testing.T) { require.Equal(t, 2, numPages) require.Empty(t, cmp.Diff([]types.UserGroup{g1, g2}, paginatedOut, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Fetch a specific user group. sp, err := service.GetUserGroup(ctx, g2.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(g2, sp, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to fetch a user group that doesn't exist. @@ -116,7 +116,7 @@ func TestUserGroupCRUD(t *testing.T) { sp, err = service.GetUserGroup(ctx, g1.GetName()) require.NoError(t, err) require.Empty(t, cmp.Diff(g1, sp, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Delete a user group. @@ -126,7 +126,7 @@ func TestUserGroupCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, nextToken) require.Empty(t, cmp.Diff([]types.UserGroup{g2}, out, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) // Try to delete a user group that doesn't exist. diff --git a/lib/services/local/users.go b/lib/services/local/users.go index febea1f0de3c9..a9bbbb4df73c1 100644 --- a/lib/services/local/users.go +++ b/lib/services/local/users.go @@ -89,7 +89,7 @@ func (s *IdentityService) GetUsers(withSecrets bool) ([]types.User, error) { continue } u, err := services.UnmarshalUser( - item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, trace.Wrap(err) } @@ -334,7 +334,7 @@ func (s *IdentityService) getUser(ctx context.Context, user string, withSecrets } u, err := services.UnmarshalUser( - item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires)) + item.Value, services.WithResourceID(item.ID), services.WithExpires(item.Expires), services.WithRevision(item.Revision)) if err != nil { return nil, nil, trace.Wrap(err) } diff --git a/lib/services/local/users_test.go b/lib/services/local/users_test.go index 4b75581b8ef30..d201265a5fbbe 100644 --- a/lib/services/local/users_test.go +++ b/lib/services/local/users_test.go @@ -28,6 +28,7 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/uuid" "github.com/gravitational/trace" "github.com/jonboulle/clockwork" @@ -1074,14 +1075,14 @@ func TestIdentityService_UpdateAndSwapUser(t *testing.T) { } // Assert update response. - if diff := cmp.Diff(want, updated); diff != "" { + if diff := cmp.Diff(want, updated, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision")); diff != "" { t.Errorf("UpdateAndSwapUser return mismatch (-want +got)\n%s", diff) } // Assert stored. stored, err := identity.GetUser(test.user, test.withSecrets) require.NoError(t, err, "GetUser failed") - if diff := cmp.Diff(want, stored); diff != "" { + if diff := cmp.Diff(want, stored, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision")); diff != "" { t.Errorf("UpdateAndSwapUser storage mismatch (-want +got)\n%s", diff) } }) diff --git a/lib/services/lock.go b/lib/services/lock.go index cb29bab641c5f..10273502d8b22 100644 --- a/lib/services/lock.go +++ b/lib/services/lock.go @@ -96,6 +96,9 @@ func UnmarshalLock(bytes []byte, opts ...MarshalOption) (types.Lock, error) { if cfg.ID != 0 { lock.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + lock.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { lock.SetExpiry(cfg.Expires) } @@ -123,6 +126,7 @@ func MarshalLock(lock types.Lock, opts ...MarshalOption) ([]byte, error) { // to prevent unexpected data races copy := *lock copy.SetResourceID(0) + copy.SetRevision("") lock = © } return utils.FastMarshal(lock) diff --git a/lib/services/namespace.go b/lib/services/namespace.go index d0a3d581ed1e0..9cf88a73a5d7a 100644 --- a/lib/services/namespace.go +++ b/lib/services/namespace.go @@ -38,6 +38,7 @@ func MarshalNamespace(resource types.Namespace, opts ...MarshalOption) ([]byte, // to prevent unexpected data races copy := resource copy.SetResourceID(0) + copy.SetRevision("") resource = copy } return utils.FastMarshal(resource) @@ -68,6 +69,9 @@ func UnmarshalNamespace(data []byte, opts ...MarshalOption) (*types.Namespace, e if cfg.ID != 0 { namespace.Metadata.ID = cfg.ID } + if cfg.Revision != "" { + namespace.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { namespace.Metadata.Expires = &cfg.Expires } diff --git a/lib/services/networking.go b/lib/services/networking.go index fbb93710f55f9..25af65331e8da 100644 --- a/lib/services/networking.go +++ b/lib/services/networking.go @@ -48,6 +48,9 @@ func UnmarshalClusterNetworkingConfig(bytes []byte, opts ...MarshalOption) (type if cfg.ID != 0 { netConfig.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + netConfig.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { netConfig.SetExpiry(cfg.Expires) } @@ -72,6 +75,7 @@ func MarshalClusterNetworkingConfig(netConfig types.ClusterNetworkingConfig, opt // to prevent unexpected data races copy := *netConfig copy.SetResourceID(0) + copy.SetRevision("") netConfig = © } return utils.FastMarshal(netConfig) diff --git a/lib/services/oidc.go b/lib/services/oidc.go index 5df318160878d..062537c25ac96 100644 --- a/lib/services/oidc.go +++ b/lib/services/oidc.go @@ -123,6 +123,9 @@ func UnmarshalOIDCConnector(bytes []byte, opts ...MarshalOption) (types.OIDCConn if cfg.ID != 0 { c.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + c.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { c.SetExpiry(cfg.Expires) } @@ -150,6 +153,7 @@ func MarshalOIDCConnector(oidcConnector types.OIDCConnector, opts ...MarshalOpti // to prevent unexpected data races copy := *oidcConnector copy.SetResourceID(0) + copy.SetRevision("") oidcConnector = © } return utils.FastMarshal(oidcConnector) diff --git a/lib/services/okta.go b/lib/services/okta.go index 287d98ebe89a8..9ebbcbe37ad9c 100644 --- a/lib/services/okta.go +++ b/lib/services/okta.go @@ -94,6 +94,7 @@ func MarshalOktaImportRule(importRule types.OktaImportRule, opts ...MarshalOptio if !cfg.PreserveResourceID { copy := *i copy.SetResourceID(0) + copy.SetRevision("") i = © } return utils.FastMarshal(i) @@ -127,6 +128,9 @@ func UnmarshalOktaImportRule(data []byte, opts ...MarshalOption) (types.OktaImpo if cfg.ID != 0 { i.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + i.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { i.SetExpiry(cfg.Expires) } @@ -151,6 +155,7 @@ func MarshalOktaAssignment(assignment types.OktaAssignment, opts ...MarshalOptio if !cfg.PreserveResourceID { copy := *a copy.SetResourceID(0) + copy.SetRevision("") a = © } return utils.FastMarshal(a) @@ -184,6 +189,9 @@ func UnmarshalOktaAssignment(data []byte, opts ...MarshalOption) (types.OktaAssi if cfg.ID != 0 { a.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + a.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { a.SetExpiry(cfg.Expires) } diff --git a/lib/services/plugin_data.go b/lib/services/plugin_data.go index 95f6913bd366c..70e6bf2149897 100644 --- a/lib/services/plugin_data.go +++ b/lib/services/plugin_data.go @@ -65,6 +65,9 @@ func UnmarshalPluginData(raw []byte, opts ...MarshalOption) (types.PluginData, e if cfg.ID != 0 { data.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + data.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { data.SetExpiry(cfg.Expires) } diff --git a/lib/services/plugin_static_credentials.go b/lib/services/plugin_static_credentials.go index 6e94e730e7e31..35b6843f50a4b 100644 --- a/lib/services/plugin_static_credentials.go +++ b/lib/services/plugin_static_credentials.go @@ -55,6 +55,7 @@ func MarshalPluginStaticCredentials(pluginStaticCredentials types.PluginStaticCr if !cfg.PreserveResourceID { copy := *pluginStaticCredentials copy.SetResourceID(0) + copy.SetRevision("") pluginStaticCredentials = © } data, err := protojson.Marshal(protoadapt.MessageV2Of(pluginStaticCredentials)) @@ -92,6 +93,9 @@ func UnmarshalPluginStaticCredentials(data []byte, opts ...MarshalOption) (types if cfg.ID != 0 { pluginStaticCredentials.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + pluginStaticCredentials.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { pluginStaticCredentials.SetExpiry(cfg.Expires) } diff --git a/lib/services/plugins.go b/lib/services/plugins.go index 96a8febe89f59..f4216c76cfeed 100644 --- a/lib/services/plugins.go +++ b/lib/services/plugins.go @@ -54,6 +54,7 @@ func MarshalPlugin(plugin types.Plugin, opts ...MarshalOption) ([]byte, error) { if !cfg.PreserveResourceID { copy := *plugin copy.SetResourceID(0) + copy.SetRevision("") plugin = © } var buf bytes.Buffer @@ -92,6 +93,9 @@ func UnmarshalPlugin(data []byte, opts ...MarshalOption) (types.Plugin, error) { if cfg.ID != 0 { plugin.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + plugin.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { plugin.SetExpiry(cfg.Expires) } diff --git a/lib/services/provisioning.go b/lib/services/provisioning.go index f6bb1938bac4d..6a6247a0a3223 100644 --- a/lib/services/provisioning.go +++ b/lib/services/provisioning.go @@ -86,6 +86,9 @@ func UnmarshalProvisionToken(data []byte, opts ...MarshalOption) (types.Provisio if cfg.ID != 0 { v2.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + v2.SetRevision(cfg.Revision) + } return v2, nil case types.V2: var p types.ProvisionTokenV2 @@ -98,6 +101,9 @@ func UnmarshalProvisionToken(data []byte, opts ...MarshalOption) (types.Provisio if cfg.ID != 0 { p.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + p.SetRevision(cfg.Revision) + } return &p, nil } return nil, trace.BadParameter("server resource version %v is not supported", h.Version) @@ -121,6 +127,7 @@ func MarshalProvisionToken(provisionToken types.ProvisionToken, opts ...MarshalO // to prevent unexpected data races copy := *provisionToken copy.SetResourceID(0) + copy.SetRevision("") provisionToken = © } if cfg.GetVersion() == types.V1 { diff --git a/lib/services/remotecluster.go b/lib/services/remotecluster.go index a463aea5cd3c1..4d7aef5571f99 100644 --- a/lib/services/remotecluster.go +++ b/lib/services/remotecluster.go @@ -48,6 +48,9 @@ func UnmarshalRemoteCluster(bytes []byte, opts ...MarshalOption) (types.RemoteCl if cfg.ID != 0 { cluster.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + cluster.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { cluster.SetExpiry(cfg.Expires) } @@ -73,6 +76,7 @@ func MarshalRemoteCluster(remoteCluster types.RemoteCluster, opts ...MarshalOpti // to prevent unexpected data races copy := *remoteCluster copy.SetResourceID(0) + copy.SetRevision("") remoteCluster = © } return utils.FastMarshal(remoteCluster) diff --git a/lib/services/resource.go b/lib/services/resource.go index 9782d42f1876d..46c9a0cc902cf 100644 --- a/lib/services/resource.go +++ b/lib/services/resource.go @@ -30,12 +30,15 @@ import ( // MarshalConfig specifies marshaling options type MarshalConfig struct { - // Version specifies particular version we should marshal resources with + // Version specifies a particular version we should marshal resources with Version string // ID is a record ID to assign ID int64 + // Revision of the resource to assign. + Revision string + // PreserveResourceID preserves resource IDs in resource // specs when marshaling PreserveResourceID bool @@ -81,6 +84,14 @@ func WithResourceID(id int64) MarshalOption { } } +// WithRevision assigns Revision to the resource +func WithRevision(rev string) MarshalOption { + return func(c *MarshalConfig) error { + c.Revision = rev + return nil + } +} + // WithExpires assigns expiry value func WithExpires(expires time.Time) MarshalOption { return func(c *MarshalConfig) error { diff --git a/lib/services/restrictions.go b/lib/services/restrictions.go index fced07890e0ff..2435e0fdc2467 100644 --- a/lib/services/restrictions.go +++ b/lib/services/restrictions.go @@ -67,6 +67,9 @@ func UnmarshalNetworkRestrictions(bytes []byte, opts ...MarshalOption) (types.Ne if cfg.ID != 0 { nr.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + nr.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { nr.SetExpiry(cfg.Expires) } @@ -93,6 +96,7 @@ func MarshalNetworkRestrictions(restrictions types.NetworkRestrictions, opts ... // to prevent unexpected data races copy := *restrictions copy.SetResourceID(0) + copy.SetRevision("") restrictions = © } return utils.FastMarshal(restrictions) diff --git a/lib/services/role.go b/lib/services/role.go index 0cf266441ffb0..754ba22ebefdc 100644 --- a/lib/services/role.go +++ b/lib/services/role.go @@ -3134,6 +3134,9 @@ func UnmarshalRole(bytes []byte, opts ...MarshalOption) (types.Role, error) { if cfg.ID != 0 { role.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + role.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { role.SetExpiry(cfg.Expires) } @@ -3161,6 +3164,7 @@ func MarshalRole(role types.Role, opts ...MarshalOption) ([]byte, error) { // to prevent unexpected data races copy := *role copy.SetResourceID(0) + copy.SetRevision("") role = © } return utils.FastMarshal(role) diff --git a/lib/services/saml.go b/lib/services/saml.go index b0121584228f3..9d8132287781a 100644 --- a/lib/services/saml.go +++ b/lib/services/saml.go @@ -299,6 +299,9 @@ func UnmarshalSAMLConnector(bytes []byte, opts ...MarshalOption) (types.SAMLConn if cfg.ID != 0 { c.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + c.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { c.SetExpiry(cfg.Expires) } @@ -327,6 +330,7 @@ func MarshalSAMLConnector(samlConnector types.SAMLConnector, opts ...MarshalOpti // to prevent unexpected data races copy := *samlConnector copy.SetResourceID(0) + copy.SetRevision("") samlConnector = © } return utils.FastMarshal(samlConnector) diff --git a/lib/services/saml_idp_service_provider.go b/lib/services/saml_idp_service_provider.go index edb05544c2186..8f0bd379b8c35 100644 --- a/lib/services/saml_idp_service_provider.go +++ b/lib/services/saml_idp_service_provider.go @@ -62,6 +62,7 @@ func MarshalSAMLIdPServiceProvider(serviceProvider types.SAMLIdPServiceProvider, if !cfg.PreserveResourceID { copy := *sp copy.SetResourceID(0) + copy.SetRevision("") sp = © } return utils.FastMarshal(sp) @@ -95,6 +96,9 @@ func UnmarshalSAMLIdPServiceProvider(data []byte, opts ...MarshalOption) (types. if cfg.ID != 0 { s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } diff --git a/lib/services/semaphore.go b/lib/services/semaphore.go index 78df632e1888b..653225518a0d6 100644 --- a/lib/services/semaphore.go +++ b/lib/services/semaphore.go @@ -301,6 +301,9 @@ func UnmarshalSemaphore(bytes []byte, opts ...MarshalOption) (types.Semaphore, e if cfg.ID != 0 { semaphore.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + semaphore.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { semaphore.SetExpiry(cfg.Expires) } @@ -325,6 +328,7 @@ func MarshalSemaphore(semaphore types.Semaphore, opts ...MarshalOption) ([]byte, // to prevent unexpected data races copy := *semaphore copy.SetResourceID(0) + copy.SetRevision("") semaphore = © } return utils.FastMarshal(semaphore) diff --git a/lib/services/server.go b/lib/services/server.go index 87bf4cdaa4c29..b02b8291aacb5 100644 --- a/lib/services/server.go +++ b/lib/services/server.go @@ -363,6 +363,9 @@ func UnmarshalServer(bytes []byte, kind string, opts ...MarshalOption) (types.Se if cfg.ID != 0 { s.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + s.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { s.SetExpiry(cfg.Expires) } @@ -394,6 +397,7 @@ func MarshalServer(server types.Server, opts ...MarshalOption) ([]byte, error) { // to prevent unexpected data races copy := *server copy.SetResourceID(0) + copy.SetRevision("") server = © } return utils.FastMarshal(server) diff --git a/lib/services/server_info.go b/lib/services/server_info.go index 6a7b895f52ebe..13e0859b3ea65 100644 --- a/lib/services/server_info.go +++ b/lib/services/server_info.go @@ -45,6 +45,9 @@ func UnmarshalServerInfo(bytes []byte, opts ...MarshalOption) (types.ServerInfo, if cfg.ID != 0 { si.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + si.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { si.SetExpiry(cfg.Expires) } @@ -73,6 +76,7 @@ func MarshalServerInfo(si types.ServerInfo, opts ...MarshalOption) ([]byte, erro // to prevent unexpected data races copy := *si copy.SetResourceID(0) + copy.SetRevision("") si = © } bytes, err := utils.FastMarshal(si) diff --git a/lib/services/services_test.go b/lib/services/services_test.go index e8af818905b84..edefd14546276 100644 --- a/lib/services/services_test.go +++ b/lib/services/services_test.go @@ -43,22 +43,24 @@ func TestOptions(t *testing.T) { require.Len(t, out, 0) // make sure original option list is not affected - in := []MarshalOption{} - out = AddOptions(in, WithResourceID(1)) - require.Len(t, out, 1) + var in []MarshalOption + out = AddOptions(in, WithResourceID(1), WithRevision("abc")) + require.Len(t, out, 2) require.Len(t, in, 0) cfg, err := CollectOptions(out) require.NoError(t, err) - require.Equal(t, cfg.ID, int64(1)) + require.Equal(t, int64(1), cfg.ID) + require.Equal(t, "abc", cfg.Revision) // Add a couple of other parameters - out = AddOptions(in, WithResourceID(2), WithVersion(types.V2)) - require.Len(t, out, 2) + out = AddOptions(in, WithResourceID(2), WithVersion(types.V2), WithRevision("xyz")) + require.Len(t, out, 3) require.Len(t, in, 0) cfg, err = CollectOptions(out) require.NoError(t, err) - require.Equal(t, cfg.ID, int64(2)) - require.Equal(t, cfg.Version, types.V2) + require.Equal(t, int64(2), cfg.ID) + require.Equal(t, types.V2, cfg.Version) + require.Equal(t, "xyz", cfg.Revision) } // TestCommandLabels tests command labels diff --git a/lib/services/session.go b/lib/services/session.go index 9404219da0b55..aedb735526a01 100644 --- a/lib/services/session.go +++ b/lib/services/session.go @@ -53,6 +53,9 @@ func UnmarshalWebSession(bytes []byte, opts ...MarshalOption) (types.WebSession, if cfg.ID != 0 { ws.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + ws.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { ws.SetExpiry(cfg.Expires) } @@ -80,6 +83,7 @@ func MarshalWebSession(webSession types.WebSession, opts ...MarshalOption) ([]by // to prevent unexpected data races copy := *webSession copy.SetResourceID(0) + copy.SetRevision("") webSession = © } return utils.FastMarshal(webSession) @@ -106,6 +110,7 @@ func MarshalWebToken(webToken types.WebToken, opts ...MarshalOption) ([]byte, er // to prevent unexpected data races copy := *webToken copy.SetResourceID(0) + copy.SetRevision("") webToken = © } return utils.FastMarshal(webToken) diff --git a/lib/services/sessionrecording.go b/lib/services/sessionrecording.go index 28eb4186ffcdb..0d5b0a91caa2a 100644 --- a/lib/services/sessionrecording.go +++ b/lib/services/sessionrecording.go @@ -57,6 +57,9 @@ func UnmarshalSessionRecordingConfig(bytes []byte, opts ...MarshalOption) (types if cfg.ID != 0 { recConfig.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + recConfig.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { recConfig.SetExpiry(cfg.Expires) } @@ -84,6 +87,7 @@ func MarshalSessionRecordingConfig(recConfig types.SessionRecordingConfig, opts // to prevent unexpected data races copy := *recConfig copy.SetResourceID(0) + copy.SetRevision("") recConfig = © } return utils.FastMarshal(recConfig) diff --git a/lib/services/statictokens.go b/lib/services/statictokens.go index 9cbc6990193c3..bfaa6a407f66e 100644 --- a/lib/services/statictokens.go +++ b/lib/services/statictokens.go @@ -46,6 +46,9 @@ func UnmarshalStaticTokens(bytes []byte, opts ...MarshalOption) (types.StaticTok if cfg.ID != 0 { staticTokens.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + staticTokens.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { staticTokens.SetExpiry(cfg.Expires) } @@ -70,6 +73,7 @@ func MarshalStaticTokens(staticToken types.StaticTokens, opts ...MarshalOption) // to prevent unexpected data races copy := *staticToken copy.SetResourceID(0) + copy.SetRevision("") staticToken = © } return utils.FastMarshal(staticToken) diff --git a/lib/services/suite/suite.go b/lib/services/suite/suite.go index 392cc85ce6c2f..2638030c55431 100644 --- a/lib/services/suite/suite.go +++ b/lib/services/suite/suite.go @@ -309,23 +309,22 @@ func (s *ServicesTestSuite) CertAuthCRUD(t *testing.T) { out, err := s.CAS.GetCertAuthority(ctx, ca.GetID(), true) require.NoError(t, err) - ca.SetResourceID(out.GetResourceID()) - require.Equal(t, out, ca) + require.Empty(t, cmp.Diff(out, ca, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) cas, err := s.CAS.GetCertAuthorities(ctx, types.UserCA, false) require.NoError(t, err) ca2 := ca.Clone().(*types.CertAuthorityV2) ca2.Spec.ActiveKeys.SSH[0].PrivateKey = nil ca2.Spec.ActiveKeys.TLS[0].Key = nil - require.Equal(t, cas[0], ca2) + require.Empty(t, cmp.Diff(cas[0], ca2, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) cas, err = s.CAS.GetCertAuthorities(ctx, types.UserCA, true) require.NoError(t, err) - require.Equal(t, cas[0], ca) + require.Empty(t, cmp.Diff(cas[0], ca, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) cas, err = s.CAS.GetCertAuthorities(ctx, types.UserCA, true) require.NoError(t, err) - require.Equal(t, cas[0], ca) + require.Empty(t, cmp.Diff(cas[0], ca, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.CAS.DeleteCertAuthority(ctx, *ca.ID()) require.NoError(t, err) @@ -349,8 +348,7 @@ func (s *ServicesTestSuite) CertAuthCRUD(t *testing.T) { out, err = s.CAS.GetCertAuthority(ctx, ca.GetID(), true) require.NoError(t, err) - newCA.SetResourceID(out.GetResourceID()) - require.Empty(t, cmp.Diff(&newCA, out, cmpopts.EquateApproxTime(time.Second))) + require.Empty(t, cmp.Diff(&newCA, out, cmpopts.EquateApproxTime(time.Second), cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } // NewServer creates a new server resource @@ -382,14 +380,12 @@ func (s *ServicesTestSuite) ServerCRUD(t *testing.T) { node, err := s.PresenceS.GetNode(ctx, srv.Metadata.Namespace, srv.GetName()) require.NoError(t, err) - srv.SetResourceID(node.GetResourceID()) - require.Empty(t, cmp.Diff(node, srv)) + require.Empty(t, cmp.Diff(node, srv, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) out, err = s.PresenceS.GetNodes(ctx, srv.Metadata.Namespace) require.NoError(t, err) require.Len(t, out, 1) - srv.SetResourceID(out[0].GetResourceID()) - require.Empty(t, cmp.Diff(out, []types.Server{srv})) + require.Empty(t, cmp.Diff(out, []types.Server{srv}, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.PresenceS.DeleteNode(ctx, srv.Metadata.Namespace, srv.GetName()) require.NoError(t, err) @@ -409,8 +405,7 @@ func (s *ServicesTestSuite) ServerCRUD(t *testing.T) { out, err = s.PresenceS.GetProxies() require.NoError(t, err) require.Len(t, out, 1) - proxy.SetResourceID(out[0].GetResourceID()) - require.Empty(t, cmp.Diff(out, []types.Server{proxy})) + require.Empty(t, cmp.Diff(out, []types.Server{proxy}, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.PresenceS.DeleteProxy(ctx, proxy.GetName()) require.NoError(t, err) @@ -430,8 +425,7 @@ func (s *ServicesTestSuite) ServerCRUD(t *testing.T) { out, err = s.PresenceS.GetAuthServers() require.NoError(t, err) require.Len(t, out, 1) - auth.SetResourceID(out[0].GetResourceID()) - require.Empty(t, cmp.Diff(out, []types.Server{auth})) + require.Empty(t, cmp.Diff(out, []types.Server{auth}, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } // AppServerCRUD tests CRUD functionality for services.Server. @@ -465,8 +459,7 @@ func (s *ServicesTestSuite) AppServerCRUD(t *testing.T) { out, err = s.PresenceS.GetApplicationServers(ctx, server.GetNamespace()) require.NoError(t, err) require.Len(t, out, 1) - server.SetResourceID(out[0].GetResourceID()) - require.Empty(t, cmp.Diff([]types.AppServer{server}, out)) + require.Empty(t, cmp.Diff([]types.AppServer{server}, out, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) // Remove the application. err = s.PresenceS.DeleteApplicationServer(ctx, server.Metadata.Namespace, server.GetHostID(), server.GetName()) @@ -504,8 +497,7 @@ func (s *ServicesTestSuite) ReverseTunnelsCRUD(t *testing.T) { out, err = s.PresenceS.GetReverseTunnels(context.Background()) require.NoError(t, err) require.Len(t, out, 1) - tunnel.SetResourceID(out[0].GetResourceID()) - require.Empty(t, cmp.Diff(out, []types.ReverseTunnel{tunnel})) + require.Empty(t, cmp.Diff(out, []types.ReverseTunnel{tunnel}, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.PresenceS.DeleteReverseTunnel(tunnel.Spec.ClusterName) require.NoError(t, err) @@ -699,16 +691,14 @@ func (s *ServicesTestSuite) RolesCRUD(t *testing.T) { require.NoError(t, err) rout, err := s.Access.GetRole(ctx, role.Metadata.Name) require.NoError(t, err) - role.SetResourceID(rout.GetResourceID()) - require.Empty(t, cmp.Diff(rout, &role)) + require.Empty(t, cmp.Diff(rout, &role, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) role.Spec.Allow.Logins = []string{"bob"} err = s.Access.UpsertRole(ctx, &role) require.NoError(t, err) rout, err = s.Access.GetRole(ctx, role.Metadata.Name) require.NoError(t, err) - role.SetResourceID(rout.GetResourceID()) - require.Empty(t, cmp.Diff(rout, &role)) + require.Empty(t, cmp.Diff(rout, &role, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.Access.DeleteRole(ctx, role.Metadata.Name) require.NoError(t, err) @@ -820,13 +810,12 @@ func (s *ServicesTestSuite) TunnelConnectionsCRUD(t *testing.T) { out, err = s.PresenceS.GetTunnelConnections(clusterName) require.NoError(t, err) require.Equal(t, len(out), 1) - conn.SetResourceID(out[0].GetResourceID()) - require.Empty(t, cmp.Diff(out[0], conn)) + require.Empty(t, cmp.Diff(out[0], conn, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) out, err = s.PresenceS.GetAllTunnelConnections() require.NoError(t, err) require.Equal(t, len(out), 1) - require.Empty(t, cmp.Diff(out[0], conn)) + require.Empty(t, cmp.Diff(out[0], conn, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) dt = dt.Add(time.Hour) conn.SetLastHeartbeat(dt) @@ -837,8 +826,7 @@ func (s *ServicesTestSuite) TunnelConnectionsCRUD(t *testing.T) { out, err = s.PresenceS.GetTunnelConnections(clusterName) require.NoError(t, err) require.Equal(t, len(out), 1) - conn.SetResourceID(out[0].GetResourceID()) - require.Empty(t, cmp.Diff(out[0], conn)) + require.Empty(t, cmp.Diff(out[0], conn, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.PresenceS.DeleteAllTunnelConnections() require.NoError(t, err) @@ -857,8 +845,7 @@ func (s *ServicesTestSuite) TunnelConnectionsCRUD(t *testing.T) { out, err = s.PresenceS.GetTunnelConnections(clusterName) require.NoError(t, err) require.Equal(t, len(out), 1) - conn.SetResourceID(out[0].GetResourceID()) - require.Empty(t, cmp.Diff(out[0], conn)) + require.Empty(t, cmp.Diff(out[0], conn, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.PresenceS.DeleteTunnelConnection(clusterName, conn.GetName()) require.NoError(t, err) @@ -945,8 +932,7 @@ func (s *ServicesTestSuite) RemoteClustersCRUD(t *testing.T) { out, err = s.PresenceS.GetRemoteClusters() require.NoError(t, err) require.Equal(t, len(out), 1) - rc.SetResourceID(out[0].GetResourceID()) - require.Empty(t, cmp.Diff(out[0], rc)) + require.Empty(t, cmp.Diff(out[0], rc, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.PresenceS.DeleteAllRemoteClusters() require.NoError(t, err) @@ -962,7 +948,7 @@ func (s *ServicesTestSuite) RemoteClustersCRUD(t *testing.T) { out, err = s.PresenceS.GetRemoteClusters() require.NoError(t, err) require.Equal(t, len(out), 1) - require.Empty(t, cmp.Diff(out[0], rc)) + require.Empty(t, cmp.Diff(out[0], rc, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.PresenceS.DeleteRemoteCluster(ctx, clusterName) require.NoError(t, err) @@ -1027,8 +1013,7 @@ func (s *ServicesTestSuite) StaticTokens(t *testing.T) { out, err := s.ConfigS.GetStaticTokens() require.NoError(t, err) - staticTokens.SetResourceID(out.GetResourceID()) - require.Empty(t, cmp.Diff(staticTokens, out)) + require.Empty(t, cmp.Diff(staticTokens, out, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.ConfigS.DeleteStaticTokens() require.NoError(t, err) @@ -1074,8 +1059,7 @@ func (s *ServicesTestSuite) ClusterName(t *testing.T, opts ...Option) { gotName, err := s.ConfigS.GetClusterName() require.NoError(t, err) - clusterName.SetResourceID(gotName.GetResourceID()) - require.Empty(t, cmp.Diff(clusterName, gotName)) + require.Empty(t, cmp.Diff(clusterName, gotName, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) err = s.ConfigS.DeleteClusterName() require.NoError(t, err) @@ -1088,8 +1072,7 @@ func (s *ServicesTestSuite) ClusterName(t *testing.T, opts ...Option) { gotName, err = s.ConfigS.GetClusterName() require.NoError(t, err) - clusterName.SetResourceID(gotName.GetResourceID()) - require.Empty(t, cmp.Diff(clusterName, gotName)) + require.Empty(t, cmp.Diff(clusterName, gotName, cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"))) } // ClusterNetworkingConfig tests cluster networking configuration. diff --git a/lib/services/trustedcluster.go b/lib/services/trustedcluster.go index 811311eb5c825..fbbe2aa569d18 100644 --- a/lib/services/trustedcluster.go +++ b/lib/services/trustedcluster.go @@ -161,6 +161,9 @@ func UnmarshalTrustedCluster(bytes []byte, opts ...MarshalOption) (types.Trusted if cfg.ID != 0 { trustedCluster.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + trustedCluster.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { trustedCluster.SetExpiry(cfg.Expires) } @@ -189,6 +192,7 @@ func MarshalTrustedCluster(trustedCluster types.TrustedCluster, opts ...MarshalO // to prevent unexpected data races copy := *trustedCluster copy.SetResourceID(0) + copy.SetRevision("") trustedCluster = © } return utils.FastMarshal(trustedCluster) diff --git a/lib/services/tunnel.go b/lib/services/tunnel.go index d423f90acfd93..5ac6c1ef5fdf9 100644 --- a/lib/services/tunnel.go +++ b/lib/services/tunnel.go @@ -65,6 +65,9 @@ func UnmarshalReverseTunnel(bytes []byte, opts ...MarshalOption) (types.ReverseT if cfg.ID != 0 { r.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + r.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { r.SetExpiry(cfg.Expires) } @@ -91,6 +94,7 @@ func MarshalReverseTunnel(reverseTunnel types.ReverseTunnel, opts ...MarshalOpti // to prevent unexpected data races copy := *reverseTunnel copy.SetResourceID(0) + copy.SetRevision("") reverseTunnel = © } return utils.FastMarshal(reverseTunnel) diff --git a/lib/services/tunnelconn.go b/lib/services/tunnelconn.go index ee90ca49635e1..e59be870d8910 100644 --- a/lib/services/tunnelconn.go +++ b/lib/services/tunnelconn.go @@ -82,6 +82,9 @@ func UnmarshalTunnelConnection(data []byte, opts ...MarshalOption) (types.Tunnel if cfg.ID != 0 { r.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + r.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { r.SetExpiry(cfg.Expires) } @@ -108,6 +111,7 @@ func MarshalTunnelConnection(tunnelConnection types.TunnelConnection, opts ...Ma // to prevent unexpected data races copy := *tunnelConnection copy.SetResourceID(0) + copy.SetRevision("") tunnelConnection = © } return utils.FastMarshal(tunnelConnection) diff --git a/lib/services/ui_config.go b/lib/services/ui_config.go index a2add1de21836..2f5afa78725c7 100644 --- a/lib/services/ui_config.go +++ b/lib/services/ui_config.go @@ -45,6 +45,9 @@ func UnmarshalUIConfig(data []byte, opts ...MarshalOption) (types.UIConfig, erro if cfg.ID != 0 { uiconfig.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + uiconfig.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { uiconfig.SetExpiry(cfg.Expires) } @@ -67,6 +70,7 @@ func MarshalUIConfig(uiconfig types.UIConfig, opts ...MarshalOption) ([]byte, er // to prevent unexpected data races copy := *uiconfig copy.SetResourceID(0) + copy.SetRevision("") uiconfig = © } return utils.FastMarshal(uiconfig) diff --git a/lib/services/unified_resource_test.go b/lib/services/unified_resource_test.go index 796dc095db746..d60bf34df4cbd 100644 --- a/lib/services/unified_resource_test.go +++ b/lib/services/unified_resource_test.go @@ -196,7 +196,8 @@ func TestUnifiedResourceWatcher(t *testing.T) { expectedRes, res, cmpopts.EquateEmpty(), - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), + cmpopts.IgnoreFields(header.Metadata{}, "ID", "Revision"), // Ignore order. cmpopts.SortSlices(func(a, b types.ResourceWithLabels) bool { return a.GetName() < b.GetName() }), )) @@ -223,7 +224,8 @@ func TestUnifiedResourceWatcher(t *testing.T) { expectedRes, res, cmpopts.EquateEmpty(), - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), + cmpopts.IgnoreFields(header.Metadata{}, "ID", "Revision"), // Ignore order. cmpopts.SortSlices(func(a, b types.ResourceWithLabels) bool { return a.GetName() < b.GetName() }), )) diff --git a/lib/services/user.go b/lib/services/user.go index 315fdff63261d..6c91294978d29 100644 --- a/lib/services/user.go +++ b/lib/services/user.go @@ -56,7 +56,7 @@ func ValidateUserRoles(ctx context.Context, u types.User, roleGetter RoleGetter) func UsersEquals(u types.User, other types.User) bool { return cmp.Equal(u, other, ignoreProtoXXXFields(), - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), cmpopts.SortSlices(func(a, b *types.MFADevice) bool { return a.Metadata.Name < b.Metadata.Name }), @@ -105,6 +105,9 @@ func UnmarshalUser(bytes []byte, opts ...MarshalOption) (types.User, error) { if cfg.ID != 0 { u.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + u.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { u.SetExpiry(cfg.Expires) } @@ -132,6 +135,7 @@ func MarshalUser(user types.User, opts ...MarshalOption) ([]byte, error) { // to prevent unexpected data races copy := *user copy.SetResourceID(0) + copy.SetRevision("") user = © } return utils.FastMarshal(user) diff --git a/lib/services/user_login_state.go b/lib/services/user_login_state.go index bbf99f157fec4..782b100835bf2 100644 --- a/lib/services/user_login_state.go +++ b/lib/services/user_login_state.go @@ -84,6 +84,9 @@ func UnmarshalUserLoginState(data []byte, opts ...MarshalOption) (*userloginstat if cfg.ID != 0 { uls.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + uls.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { uls.SetExpiry(cfg.Expires) } diff --git a/lib/services/usergroup.go b/lib/services/usergroup.go index 20d82c5ebcf86..5a3a32c7322cc 100644 --- a/lib/services/usergroup.go +++ b/lib/services/usergroup.go @@ -57,6 +57,7 @@ func MarshalUserGroup(group types.UserGroup, opts ...MarshalOption) ([]byte, err if !cfg.PreserveResourceID { copy := *g copy.SetResourceID(0) + copy.SetRevision("") g = © } return utils.FastMarshal(g) @@ -90,6 +91,9 @@ func UnmarshalUserGroup(data []byte, opts ...MarshalOption) (types.UserGroup, er if cfg.ID != 0 { g.SetResourceID(cfg.ID) } + if cfg.Revision != "" { + g.SetRevision(cfg.Revision) + } if !cfg.Expires.IsZero() { g.SetExpiry(cfg.Expires) } diff --git a/lib/services/watcher_test.go b/lib/services/watcher_test.go index 9c6a0d4a34c47..41d6edff9452e 100644 --- a/lib/services/watcher_test.go +++ b/lib/services/watcher_test.go @@ -523,7 +523,7 @@ func expectLockInForce(t *testing.T, expectedLock types.Lock, err error) { func resourceDiff(res1, res2 types.Resource) string { return cmp.Diff(res1, res2, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), cmpopts.EquateEmpty()) } @@ -1276,7 +1276,7 @@ func TestOktaAssignmentWatcher(t *testing.T) { require.Empty(t, cmp.Diff(expected, changeset, - cmpopts.IgnoreFields(types.Metadata{}, "ID")), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision")), "should be no differences in the changeset after adding the first assignment") case <-w.Done(): t.Fatal("Watcher has unexpectedly exited.") @@ -1300,7 +1300,7 @@ func TestOktaAssignmentWatcher(t *testing.T) { cmp.Diff( expected, changeset, - cmpopts.IgnoreFields(types.Metadata{}, "ID")), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision")), "should be no difference in the changeset after adding the second assignment") case <-w.Done(): t.Fatal("Watcher has unexpectedly exited.") @@ -1324,7 +1324,7 @@ func TestOktaAssignmentWatcher(t *testing.T) { cmp.Diff( expected, changeset, - cmpopts.IgnoreFields(types.Metadata{}, "ID")), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision")), "should be no difference in the changeset after update") case <-w.Done(): t.Fatal("Watcher has unexpectedly exited.") @@ -1346,7 +1346,7 @@ func TestOktaAssignmentWatcher(t *testing.T) { cmp.Diff( expected, changeset, - cmpopts.IgnoreFields(types.Metadata{}, "ID")), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision")), "should be no difference in the changeset after deleting the first assignment") case <-w.Done(): t.Fatal("Watcher has unexpectedly exited.") diff --git a/lib/srv/app/server_test.go b/lib/srv/app/server_test.go index 73d37d391edb1..664fa977cc017 100644 --- a/lib/srv/app/server_test.go +++ b/lib/srv/app/server_test.go @@ -415,7 +415,7 @@ func TestStart(t *testing.T) { sort.Sort(types.AppServers(servers)) require.Empty(t, cmp.Diff([]types.AppServer{serverAWS, serverFoo}, servers, - cmpopts.IgnoreFields(types.Metadata{}, "ID", "Expires"))) + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision", "Expires"))) // Check the expiry time is correct. for _, server := range servers { diff --git a/lib/srv/app/watcher_test.go b/lib/srv/app/watcher_test.go index a1b4e7f2278ef..cf9ae7eab3f1c 100644 --- a/lib/srv/app/watcher_test.go +++ b/lib/srv/app/watcher_test.go @@ -62,7 +62,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.Apps{app0}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -79,7 +79,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.Apps{app0, app1}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -96,7 +96,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.Apps{app0, app1}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -113,7 +113,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.Apps{app0, app1}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -129,7 +129,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.Apps{app0, app1, app2}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -145,7 +145,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.Apps{app0, app1, app2}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -161,7 +161,7 @@ func TestWatcher(t *testing.T) { case a := <-reconcileCh: sort.Sort(a) require.Empty(t, cmp.Diff(types.Apps{app0, app2}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") @@ -175,7 +175,7 @@ func TestWatcher(t *testing.T) { select { case a := <-reconcileCh: require.Empty(t, cmp.Diff(types.Apps{app0}, a, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), )) case <-time.After(time.Second): t.Fatal("Didn't receive reconcile event after 1s.") diff --git a/lib/srv/db/watcher_test.go b/lib/srv/db/watcher_test.go index 1c14f0ae5dc2d..339ab4b853d29 100644 --- a/lib/srv/db/watcher_test.go +++ b/lib/srv/db/watcher_test.go @@ -341,7 +341,7 @@ func assertReconciledResource(t *testing.T, ch chan types.Databases, databases t sort.Sort(d) require.Equal(t, len(d), len(databases)) require.Empty(t, cmp.Diff(databases, d, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), cmpopts.IgnoreFields(types.DatabaseStatusV3{}, "CACert"), )) case <-time.After(time.Second): diff --git a/lib/srv/discovery/discovery_test.go b/lib/srv/discovery/discovery_test.go index c45c32973e51f..a6ff0eabadaa6 100644 --- a/lib/srv/discovery/discovery_test.go +++ b/lib/srv/discovery/discovery_test.go @@ -1483,7 +1483,7 @@ func TestDiscoveryDatabase(t *testing.T) { actualDatabases, err := tlsServer.Auth().GetDatabases(ctx) require.NoError(t, err) require.Empty(t, cmp.Diff(tc.expectDatabases, actualDatabases, - cmpopts.IgnoreFields(types.Metadata{}, "ID"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision"), cmpopts.IgnoreFields(types.DatabaseStatusV3{}, "CACert"), )) case <-time.After(time.Second): diff --git a/tool/tctl/common/resource_command_test.go b/tool/tctl/common/resource_command_test.go index 2412ada58eb6b..a8615272db0e4 100644 --- a/tool/tctl/common/resource_command_test.go +++ b/tool/tctl/common/resource_command_test.go @@ -913,7 +913,7 @@ func (test *dynamicResourceTest[T]) run(t *testing.T) { resources = mustDecodeJSON[[]T](t, buf) require.Len(t, resources, 3) require.Empty(t, cmp.Diff([]T{test.fooResource, test.fooBar1Resource, test.fooBar2Resource}, resources, - cmpopts.IgnoreFields(types.Metadata{}, "ID", "Namespace"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision", "Namespace"), )) // Fetch specific resource. @@ -923,7 +923,7 @@ func (test *dynamicResourceTest[T]) run(t *testing.T) { resources = mustDecodeJSON[[]T](t, buf) require.Len(t, resources, 1) require.Empty(t, cmp.Diff([]T{test.fooResource}, resources, - cmpopts.IgnoreFields(types.Metadata{}, "ID", "Namespace"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision", "Namespace"), )) // Remove a resource. @@ -936,7 +936,7 @@ func (test *dynamicResourceTest[T]) run(t *testing.T) { resources = mustDecodeJSON[[]T](t, buf) require.Len(t, resources, 2) require.Empty(t, cmp.Diff([]T{test.fooBar1Resource, test.fooBar2Resource}, resources, - cmpopts.IgnoreFields(types.Metadata{}, "ID", "Namespace"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision", "Namespace"), )) if !test.runDiscoveredNameChecks { @@ -950,7 +950,7 @@ func (test *dynamicResourceTest[T]) run(t *testing.T) { resources = mustDecodeJSON[[]T](t, buf) require.Len(t, resources, 2) require.Empty(t, cmp.Diff([]T{test.fooBar1Resource, test.fooBar2Resource}, resources, - cmpopts.IgnoreFields(types.Metadata{}, "ID", "Namespace"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision", "Namespace"), )) // Removing multiple resources ("foo-bar-1" and "foo-bar-2") by discovered name is an error. @@ -967,7 +967,7 @@ func (test *dynamicResourceTest[T]) run(t *testing.T) { resources = mustDecodeJSON[[]T](t, buf) require.Len(t, resources, 1) require.Empty(t, cmp.Diff([]T{test.fooBar1Resource}, resources, - cmpopts.IgnoreFields(types.Metadata{}, "ID", "Namespace"), + cmpopts.IgnoreFields(types.Metadata{}, "ID", "Revision", "Namespace"), )) }