From e035f4dbf3efd734e79b53b7ad57b3eeeb5e1507 Mon Sep 17 00:00:00 2001 From: rosstimothy <39066650+rosstimothy@users.noreply.github.com> Date: Tue, 26 Sep 2023 12:58:04 -0400 Subject: [PATCH] Propagate resource revision to/from the backend (#32040) * Set revision on resources retrieved from the backend Adds a new `MarshalOption` that ensures the resource revision is set when unmarshalling a backend item. The new `WithRevision` option was also applied everywhere that the legacy `WithResourceID` was being used. * Prevent storing resource revision in the backend item value The revision follows the same semantics as the resource id for marshalling. This prevents both items from showing up in the value of the backend item, which can prevent compare and swap operations from completing succesfully. Each backend is responsible for persisting the revision in some manner. The existing PreserveResourceID was reused to prevent having to make multiple copies of a resource when clearing the id and revision. The marshal option will be updated in a follow up when the resource id is removed. * Ignore revision in resource comparisons --- lib/auth/apiserver_test.go | 2 +- lib/auth/auth_with_roles_test.go | 34 ++++++----- lib/auth/grpcserver_test.go | 54 ++++++++-------- lib/auth/init_test.go | 2 +- lib/auth/okta/service_test.go | 14 ++--- lib/auth/tls_test.go | 15 +++-- lib/auth/trust/trustv1/service_test.go | 6 +- lib/auth/trustedcluster_test.go | 15 ++--- lib/auth/userloginstate/service_test.go | 2 +- lib/authz/permissions_test.go | 2 +- lib/cache/cache_test.go | 44 +++++-------- lib/kube/proxy/watcher_test.go | 16 ++--- lib/services/access_list.go | 8 +++ lib/services/access_request.go | 4 ++ lib/services/app.go | 8 +++ lib/services/audit.go | 4 ++ lib/services/authentication.go | 3 + lib/services/authority.go | 6 +- lib/services/clustername.go | 4 ++ lib/services/compare.go | 2 +- lib/services/connection_diagnostic.go | 5 ++ lib/services/database.go | 4 ++ lib/services/databaseserver.go | 4 ++ lib/services/databaseservice.go | 4 ++ lib/services/desktop.go | 8 +++ lib/services/github.go | 1 + lib/services/installer.go | 4 ++ lib/services/integration.go | 4 ++ lib/services/kubernetes.go | 8 +++ lib/services/license.go | 1 + lib/services/local/access.go | 8 +-- lib/services/local/access_list_test.go | 6 +- lib/services/local/access_test.go | 12 ++-- lib/services/local/apps.go | 4 +- lib/services/local/apps_test.go | 8 +-- lib/services/local/configuration.go | 12 ++-- lib/services/local/connection_diagnostic.go | 2 +- lib/services/local/databases.go | 4 +- lib/services/local/databases_test.go | 8 +-- lib/services/local/desktops.go | 6 +- lib/services/local/desktops_test.go | 2 +- lib/services/local/dynamic_access.go | 2 + lib/services/local/events.go | 39 +++++++++++- lib/services/local/generic/generic.go | 4 +- lib/services/local/generic/generic_test.go | 23 ++++--- lib/services/local/kube.go | 4 +- lib/services/local/kube_test.go | 8 +-- lib/services/local/okta_test.go | 30 ++++----- .../local/plugin_static_credentials_test.go | 8 +-- lib/services/local/plugins.go | 6 +- lib/services/local/plugins_test.go | 12 ++-- lib/services/local/presence.go | 51 +++++++++++----- lib/services/local/presence_test.go | 21 +++---- lib/services/local/provisioning.go | 3 +- lib/services/local/resource.go | 1 + lib/services/local/resource_test.go | 6 +- lib/services/local/restrictions.go | 2 +- .../local/saml_idp_service_provider_test.go | 10 +-- lib/services/local/trust.go | 6 +- lib/services/local/user_login_state_test.go | 2 +- lib/services/local/usergroup_test.go | 10 +-- lib/services/local/users.go | 4 +- lib/services/local/users_test.go | 5 +- lib/services/lock.go | 4 ++ lib/services/namespace.go | 4 ++ lib/services/networking.go | 4 ++ lib/services/oidc.go | 4 ++ lib/services/okta.go | 8 +++ lib/services/plugin_data.go | 3 + lib/services/plugin_static_credentials.go | 4 ++ lib/services/plugins.go | 4 ++ lib/services/provisioning.go | 7 +++ lib/services/remotecluster.go | 4 ++ lib/services/resource.go | 13 +++- lib/services/restrictions.go | 4 ++ lib/services/role.go | 4 ++ lib/services/saml.go | 4 ++ lib/services/saml_idp_service_provider.go | 4 ++ lib/services/semaphore.go | 4 ++ lib/services/server.go | 4 ++ lib/services/server_info.go | 4 ++ lib/services/services_test.go | 18 +++--- lib/services/session.go | 5 ++ lib/services/sessionrecording.go | 4 ++ lib/services/statictokens.go | 4 ++ lib/services/suite/suite.go | 61 +++++++------------ lib/services/trustedcluster.go | 4 ++ lib/services/tunnel.go | 4 ++ lib/services/tunnelconn.go | 4 ++ lib/services/ui_config.go | 4 ++ lib/services/unified_resource_test.go | 6 +- lib/services/user.go | 6 +- lib/services/user_login_state.go | 3 + lib/services/usergroup.go | 4 ++ lib/services/watcher_test.go | 10 +-- lib/srv/app/server_test.go | 2 +- lib/srv/app/watcher_test.go | 16 ++--- lib/srv/db/watcher_test.go | 2 +- lib/srv/discovery/discovery_test.go | 2 +- tool/tctl/common/resource_command_test.go | 10 +-- 100 files changed, 558 insertions(+), 315 deletions(-) diff --git a/lib/auth/apiserver_test.go b/lib/auth/apiserver_test.go index 6a89bcf783d..85a50df1d7e 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 4c812d35797..0039db02d28 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 b71bcaf9eaf..c131bf41b36 100644 --- a/lib/auth/grpcserver_test.go +++ b/lib/auth/grpcserver_test.go @@ -2441,7 +2441,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, "") @@ -2453,7 +2453,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, "") @@ -2552,7 +2552,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() @@ -2561,7 +2561,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"} @@ -2569,7 +2569,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) @@ -2582,7 +2582,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") @@ -2662,7 +2662,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. @@ -2672,7 +2672,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. @@ -2681,7 +2681,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. @@ -2732,14 +2732,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. @@ -2757,7 +2757,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. @@ -2766,7 +2766,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. @@ -2813,7 +2813,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())) @@ -2866,7 +2866,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())) @@ -2921,14 +2921,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. @@ -2946,7 +2946,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. @@ -2955,7 +2955,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. @@ -3037,7 +3037,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. @@ -3059,7 +3059,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. @@ -3075,7 +3075,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. @@ -3138,7 +3138,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) { @@ -3240,7 +3240,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. @@ -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"), )) // Delete a service provider. @@ -3263,7 +3263,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. @@ -4161,7 +4161,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 d6a7f32cf24..4119d020ce5 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 ad1aba00345..65c0871336a 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 325c4f3c55a..ae7512046ba 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) { @@ -4096,7 +4095,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"), )) } }) @@ -4263,7 +4262,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"), )) } }) @@ -4353,7 +4352,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) @@ -4423,7 +4422,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 702374f7fff..18c274c9f37 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 e6bc49a459d..8eddfaab017 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 208c7b4923c..31059314198 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 71eaf2ba845..a562f7d5fdf 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 44fb7e917b2..12262e7be8b 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 ef8f10738ab..49e52fba753 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 38cb3153c57..151330bccb8 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 5875e338f6f..d3d4337aada 100644 --- a/lib/services/access_request.go +++ b/lib/services/access_request.go @@ -1577,6 +1577,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) } @@ -1601,6 +1604,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 dc7b1a0ef6e..27b21a2fdd1 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 5549b1c82ee..e84afea75ee 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 fdb007e6bce..0ccabecabf6 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 0167895dbec..a99de420e03 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 bf773fad1f7..2dbda8ad5d0 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 e26020c0050..54d1e1073db 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 a8e15ba4492..6b185818a91 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 47bcca47e2c..8ba97583e50 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 d39a314bd3d..2a4fe31dcea 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 f81b0809b99..f6e37ca1abe 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 b4d927412a8..8f289ad1219 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 8cacbea8547..c05b37f1db9 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 adc165056e8..24492972ca9 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 4e025c7faa6..f65ae7e510a 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 ae4cce596ac..cff01d058c5 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 e8a84228fa2..f1195a8ddd4 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 8ada6d97fbb..34cfa8965cf 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 aaee4487e76..135a26b6dc8 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. @@ -179,7 +179,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) { @@ -256,7 +256,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 5d3d61770a5..2aff948de16 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 67c73114c73..6ff77904759 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 d059c59925f..d41ea527208 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 5bc58b2502a..b8053b22498 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 ad6183eb1d8..03b4cdfd261 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 b61a2908400..0f70ac031a2 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 1607532241e..0ac705109de 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 f90a3c65730..a5a0660602f 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 dbd3fc7ef0b..3a13df56659 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 acf914fe721..46a4cdd56db 100644 --- a/lib/services/local/dynamic_access.go +++ b/lib/services/local/dynamic_access.go @@ -507,6 +507,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, @@ -541,6 +542,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 4e8d47c5ffa..7b45f38254f 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 066d7753ab1..5c35270082b 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 1e5efa1adc2..b5d03f2919c 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 d69d2b2b11a..349b53ef056 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 25a9b6b9001..8346ce85629 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 0feccbfda26..cb7f4aadb5c 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 12d80ad6e64..c6250022060 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 df44362d81f..16722fe11e9 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 88d7ffc321d..f34fb679668 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 1b5dc6f27b1..9b0360a5837 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 a0a495e65fe..40a09b80199 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 5f0fe869641..891297e1922 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 717f5b186b9..c38d17b2075 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 d907f48c35a..d3fc1608cf9 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 28ec4169640..f5145036794 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 4a90318e8bd..29dab408617 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 03a69038557..46a916e6b14 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 9d3887b9a8f..f8ff64c89e8 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 f3f26a5c244..b6f9a7c9d49 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 febea1f0de3..a9bbbb4df73 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 4b75581b8ef..d201265a5fb 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 cb29bab641c..10273502d8b 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 d0a3d581ed1..9cf88a73a5d 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 fbb93710f55..25af65331e8 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 5df31816087..062537c25ac 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 287d98ebe89..9ebbcbe37ad 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 95f6913bd36..70e6bf21498 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 6e94e730e7e..35b6843f50a 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 96a8febe89f..f4216c76cfe 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 f6bb1938bac..6a6247a0a32 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 a463aea5cd3..4d7aef5571f 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 9782d42f187..46c9a0cc902 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 fced07890e0..2435e0fdc24 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 4621b66e499..38230bf7807 100644 --- a/lib/services/role.go +++ b/lib/services/role.go @@ -3133,6 +3133,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) } @@ -3160,6 +3163,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 b0121584228..9d813228778 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 edb05544c21..8f0bd379b8c 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 78df632e188..653225518a0 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 87bf4cdaa4c..b02b8291aac 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 6a7b895f52e..13e0859b3ea 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 e8af818905b..edefd145462 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 9404219da0b..aedb735526a 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 28eb4186ffc..0d5b0a91caa 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 9cbc6990193..bfaa6a407f6 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 392cc85ce6c..2638030c554 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 811311eb5c8..fbbe2aa569d 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 d423f90acfd..5ac6c1ef5fd 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 ee90ca49635..e59be870d89 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 a2add1de218..2f5afa78725 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 796dc095db7..d60bf34df4c 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 315fdff6326..6c91294978d 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 652a9d0f51f..55356848f6b 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 20d82c5ebcf..5a3a32c7322 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 9c6a0d4a34c..41d6edff945 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 73d37d391ed..664fa977cc0 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 a1b4e7f2278..cf9ae7eab3f 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 1c14f0ae5dc..339ab4b853d 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 980696fe502..f587aa47b4d 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 2412ada58eb..a8615272db0 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"), )) }