@ -3,11 +3,14 @@ package secret
import (
"context"
"errors"
"math/rand/v2"
"net/http"
"strconv"
"strings"
"testing"
secretv0alpha1 "github.com/grafana/grafana/pkg/apis/secret/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/secret"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/tests/apis"
"github.com/grafana/grafana/pkg/tests/testinfra"
@ -29,6 +32,9 @@ func TestIntegrationKeeper(t *testing.T) {
t . Skip ( "skipping integration test" )
}
ctx , cancel := context . WithCancel ( context . Background ( ) )
t . Cleanup ( cancel )
helper := apis . NewK8sTestHelper ( t , testinfra . GrafanaOpts {
AppModeProduction : false , // required for experimental APIs
EnableFeatureToggles : [ ] string {
@ -38,12 +44,12 @@ func TestIntegrationKeeper(t *testing.T) {
} ,
} )
ctx , cancel := context . WithCancel ( context . Background ( ) )
t . Cleanup ( cancel )
permissions := map [ string ] ResourcePermission { ResourceKeepers : { Actions : ActionsAllKeepers } }
genericUserEditor := mustCreateUsers ( t , helper , permissions ) . Editor
client := helper . GetResourceClient ( apis . ResourceClientArgs {
// #TODO: figure out permissions topic
User : helper . Org1 . Admin ,
User : genericUserEditor ,
GVR : gvrKeepers ,
} )
@ -63,7 +69,7 @@ func TestIntegrationKeeper(t *testing.T) {
} )
t . Run ( "creating a keeper returns it" , func ( t * testing . T ) {
raw := mustGenerateKeeper ( t , helper , helper . Org1 . Admin , nil )
raw := mustGenerateKeeper ( t , helper , genericUserEditor , nil )
keeper := new ( secretv0alpha1 . Keeper )
err := runtime . DefaultUnstructuredConverter . FromUnstructured ( raw . Object , keeper )
@ -155,7 +161,7 @@ func TestIntegrationKeeper(t *testing.T) {
} )
t . Run ( "creating a keeper with a provider then changing the provider does not return an error" , func ( t * testing . T ) {
rawAWS := mustGenerateKeeper ( t , helper , helper . Org1 . Admin , nil )
rawAWS := mustGenerateKeeper ( t , helper , genericUserEditor , nil )
testDataKeeperGCP := rawAWS . DeepCopy ( )
testDataKeeperGCP . Object [ "spec" ] . ( map [ string ] any ) [ "aws" ] = nil
@ -224,11 +230,30 @@ func TestIntegrationKeeper(t *testing.T) {
} )
t . Run ( "creating a keeper that references a securevalue that is stored in a non-SQL type Keeper returns an error" , func ( t * testing . T ) {
// 0. Create user with required permissions.
permissions := map [ string ] ResourcePermission {
ResourceKeepers : { Actions : ActionsAllKeepers } ,
// needed for this test to create (and delete for cleanup) securevalues.
ResourceSecureValues : {
Actions : [ ] string {
secret . ActionSecretSecureValuesCreate ,
secret . ActionSecretSecureValuesDelete ,
} ,
} ,
}
editor := mustCreateUsers ( t , helper , permissions ) . Editor
clientP := helper . GetResourceClient ( apis . ResourceClientArgs {
User : editor ,
GVR : gvrKeepers ,
} )
// 1. Create a non-SQL keeper without using `secureValueName`.
keeperAWS := mustGenerateKeeper ( t , helper , helper . Org1 . Admin , nil )
keeperAWS := mustGenerateKeeper ( t , helper , editor , nil )
// 2. Create a secureValue that is stored in the previously created keeper (non-SQL).
secureValue := mustGenerateSecureValue ( t , helper , helper . Org1 . Admin , keeperAWS . GetName ( ) )
secureValue := mustGenerateSecureValue ( t , helper , editor , keeperAWS . GetName ( ) )
// 3. Create another keeper that uses the secureValue, which fails.
testDataAnotherKeeper := keeperAWS . DeepCopy ( )
@ -236,14 +261,28 @@ func TestIntegrationKeeper(t *testing.T) {
"secureValueName" : secureValue . GetName ( ) ,
}
keeperAnother , err := client . Resource . Create ( ctx , testDataAnotherKeeper , metav1 . CreateOptions { } )
keeperAnother , err := clientP . Resource . Create ( ctx , testDataAnotherKeeper , metav1 . CreateOptions { } )
require . Error ( t , err )
require . Nil ( t , keeperAnother )
} )
t . Run ( "creating a keeper that references a securevalue that is stored in a SQL type Keeper returns no error" , func ( t * testing . T ) {
// 0. Create user with required permissions.
permissions := map [ string ] ResourcePermission {
ResourceKeepers : { Actions : ActionsAllKeepers } ,
// needed for this test to create (and delete for cleanup) securevalues.
ResourceSecureValues : {
Actions : [ ] string {
secret . ActionSecretSecureValuesCreate ,
secret . ActionSecretSecureValuesDelete ,
} ,
} ,
}
editor := mustCreateUsers ( t , helper , permissions ) . Editor
// 1. Create a SQL keeper.
keeperSQL := mustGenerateKeeper ( t , helper , helper . Org1 . Admin , map [ string ] any {
keeperSQL := mustGenerateKeeper ( t , helper , editor , map [ string ] any {
"title" : "SQL Keeper" ,
"sql" : map [ string ] any {
"encryption" : map [ string ] any { "envelope" : map [ string ] any { } } ,
@ -251,10 +290,10 @@ func TestIntegrationKeeper(t *testing.T) {
} )
// 2. Create a secureValue that is stored in the previously created keeper (SQL).
secureValue := mustGenerateSecureValue ( t , helper , helper . Org1 . Admin , keeperSQL . GetName ( ) )
secureValue := mustGenerateSecureValue ( t , helper , editor , keeperSQL . GetName ( ) )
// 3. Create a non-SQL keeper that uses the secureValue.
keeperAWS := mustGenerateKeeper ( t , helper , helper . Org1 . Admin , map [ string ] any {
keeperAWS := mustGenerateKeeper ( t , helper , editor , map [ string ] any {
"title" : "AWS Keeper" ,
"aws" : map [ string ] any {
"accessKeyId" : map [ string ] any { "secureValueName" : secureValue . GetName ( ) } ,
@ -265,50 +304,54 @@ func TestIntegrationKeeper(t *testing.T) {
} )
t . Run ( "creating keepers in multiple namespaces" , func ( t * testing . T ) {
adminOrg1 := helper . Org1 . Admin
adminOrg2 := helper . OrgB . Admin
permissions := map [ string ] ResourcePermission {
ResourceKeepers : { Actions : ActionsAllKeepers } ,
}
editorOrgA := mustCreateUsers ( t , helper , permissions ) . Editor
editorOrgB := mustCreateUsers ( t , helper , permissions ) . Editor
keeperOrg1 := mustGenerateKeeper ( t , helper , adminOrg1 , nil )
keeperOrg2 := mustGenerateKeeper ( t , helper , adminOrg2 , nil )
keeperOrgA := mustGenerateKeeper ( t , helper , editorOrgA , nil )
keeperOrgB := mustGenerateKeeper ( t , helper , editorOrgB , nil )
clientOrg1 := helper . GetResourceClient ( apis . ResourceClientArgs { User : adminOrg1 , GVR : gvrKeepers } )
clientOrg2 := helper . GetResourceClient ( apis . ResourceClientArgs { User : adminOrg2 , GVR : gvrKeepers } )
clientOrgA := helper . GetResourceClient ( apis . ResourceClientArgs { User : editorOrgA , GVR : gvrKeepers } )
clientOrgB := helper . GetResourceClient ( apis . ResourceClientArgs { User : editorOrgB , GVR : gvrKeepers } )
// Create
t . Run ( "creating a keeper with the same name as one from another namespace does not return an error" , func ( t * testing . T ) {
// Org1 creating a keeper with the same name from Org2 .
// OrgA creating a keeper with the same name from OrgB .
testData := helper . LoadYAMLOrJSONFile ( "testdata/keeper-aws-generate.yaml" )
testData . SetName ( keeperOrg2 . GetName ( ) )
testData . SetName ( keeperOrgB . GetName ( ) )
raw , err := clientOrg1 . Resource . Create ( ctx , testData , metav1 . CreateOptions { } )
raw , err := clientOrgA . Resource . Create ( ctx , testData , metav1 . CreateOptions { } )
require . NoError ( t , err )
require . NotNil ( t , raw )
// Org2 creating a keeper with the same name from Org1 .
// OrgA creating a keeper with the same name from OrgB .
testData = helper . LoadYAMLOrJSONFile ( "testdata/keeper-aws-generate.yaml" )
testData . SetName ( keeperOrg1 . GetName ( ) )
testData . SetName ( keeperOrgA . GetName ( ) )
raw , err = clientOrg2 . Resource . Create ( ctx , testData , metav1 . CreateOptions { } )
raw , err = clientOrgB . Resource . Create ( ctx , testData , metav1 . CreateOptions { } )
require . NoError ( t , err )
require . NotNil ( t , raw )
require . NoError ( t , clientOrg1 . Resource . Delete ( ctx , keeperOrg2 . GetName ( ) , metav1 . DeleteOptions { } ) )
require . NoError ( t , clientOrg2 . Resource . Delete ( ctx , keeperOrg1 . GetName ( ) , metav1 . DeleteOptions { } ) )
require . NoError ( t , clientOrgA . Resource . Delete ( ctx , keeperOrgB . GetName ( ) , metav1 . DeleteOptions { } ) )
require . NoError ( t , clientOrgB . Resource . Delete ( ctx , keeperOrgA . GetName ( ) , metav1 . DeleteOptions { } ) )
} )
// Read
t . Run ( "fetching a keeper from another namespace returns not found" , func ( t * testing . T ) {
var statusErr * apierrors . StatusError
// Org1 trying to fetch keeper from Org2 .
raw , err := clientOrg1 . Resource . Get ( ctx , keeperOrg2 . GetName ( ) , metav1 . GetOptions { } )
// OrgA trying to fetch keeper from OrgB .
raw , err := clientOrgA . Resource . Get ( ctx , keeperOrgB . GetName ( ) , metav1 . GetOptions { } )
require . Error ( t , err )
require . Nil ( t , raw )
require . True ( t , errors . As ( err , & statusErr ) )
require . Equal ( t , http . StatusNotFound , int ( statusErr . Status ( ) . Code ) )
// Org2 trying to fetch keeper from Org1 .
raw , err = clientOrg2 . Resource . Get ( ctx , keeperOrg1 . GetName ( ) , metav1 . GetOptions { } )
// OrgB trying to fetch keeper from OrgA .
raw , err = clientOrgB . Resource . Get ( ctx , keeperOrgA . GetName ( ) , metav1 . GetOptions { } )
require . Error ( t , err )
require . Nil ( t , raw )
require . True ( t , errors . As ( err , & statusErr ) )
@ -319,23 +362,23 @@ func TestIntegrationKeeper(t *testing.T) {
t . Run ( "updating a keeper from another namespace returns not found" , func ( t * testing . T ) {
var statusErr * apierrors . StatusError
// Org1 trying to update securevalue from Org2 .
// OrgA trying to update securevalue from OrgB .
testData := helper . LoadYAMLOrJSONFile ( "testdata/keeper-aws-generate.yaml" )
testData . SetName ( keeperOrg2 . GetName ( ) )
testData . SetName ( keeperOrgB . GetName ( ) )
testData . Object [ "spec" ] . ( map [ string ] any ) [ "title" ] = "New title"
raw , err := clientOrg1 . Resource . Update ( ctx , testData , metav1 . UpdateOptions { } )
raw , err := clientOrgA . Resource . Update ( ctx , testData , metav1 . UpdateOptions { } )
require . Error ( t , err )
require . Nil ( t , raw )
require . True ( t , errors . As ( err , & statusErr ) )
require . Equal ( t , http . StatusNotFound , int ( statusErr . Status ( ) . Code ) )
// Org2 trying to update keeper from Org1 .
// OrgB trying to update keeper from OrgA .
testData = helper . LoadYAMLOrJSONFile ( "testdata/keeper-aws-generate.yaml" )
testData . SetName ( keeperOrg1 . GetName ( ) )
testData . SetName ( keeperOrgA . GetName ( ) )
testData . Object [ "spec" ] . ( map [ string ] any ) [ "title" ] = "New title"
raw , err = clientOrg2 . Resource . Update ( ctx , testData , metav1 . UpdateOptions { } )
raw , err = clientOrgB . Resource . Update ( ctx , testData , metav1 . UpdateOptions { } )
require . Error ( t , err )
require . Nil ( t , raw )
require . True ( t , errors . As ( err , & statusErr ) )
@ -344,40 +387,243 @@ func TestIntegrationKeeper(t *testing.T) {
// Delete
t . Run ( "deleting a keeper from another namespace does not return an error but does not delete it" , func ( t * testing . T ) {
// Org1 trying to delete keeper from Org2 .
err := clientOrg1 . Resource . Delete ( ctx , keeperOrg2 . GetName ( ) , metav1 . DeleteOptions { } )
// OrgA trying to delete keeper from OrgB .
err := clientOrgA . Resource . Delete ( ctx , keeperOrgB . GetName ( ) , metav1 . DeleteOptions { } )
require . NoError ( t , err )
// Check that it still exists from the perspective of Org2 .
raw , err := clientOrg2 . Resource . Get ( ctx , keeperOrg2 . GetName ( ) , metav1 . GetOptions { } )
// Check that it still exists from the perspective of OrgB .
raw , err := clientOrgB . Resource . Get ( ctx , keeperOrgB . GetName ( ) , metav1 . GetOptions { } )
require . NoError ( t , err )
require . NotNil ( t , raw )
// Org2 trying to delete keeper from Org1 .
err = clientOrg2 . Resource . Delete ( ctx , keeperOrg1 . GetName ( ) , metav1 . DeleteOptions { } )
// OrgB trying to delete keeper from OrgA .
err = clientOrgB . Resource . Delete ( ctx , keeperOrgA . GetName ( ) , metav1 . DeleteOptions { } )
require . NoError ( t , err )
// Check that it still exists from the perspective of Org1 .
raw , err = clientOrg1 . Resource . Get ( ctx , keeperOrg1 . GetName ( ) , metav1 . GetOptions { } )
// Check that it still exists from the perspective of OrgA .
raw , err = clientOrgA . Resource . Get ( ctx , keeperOrgA . GetName ( ) , metav1 . GetOptions { } )
require . NoError ( t , err )
require . NotNil ( t , raw )
} )
// List
t . Run ( "listing keeper from a namespace does not return the ones from another namespace" , func ( t * testing . T ) {
// Org1 listing keeper.
listOrg1 , err := clientOrg1 . Resource . List ( ctx , metav1 . ListOptions { } )
// OrgA listing keeper.
listOrgA , err := clientOrgA . Resource . List ( ctx , metav1 . ListOptions { } )
require . NoError ( t , err )
require . NotNil ( t , listOrgA )
require . Len ( t , listOrgA . Items , 1 )
require . Equal ( t , * keeperOrgA , listOrgA . Items [ 0 ] )
// OrgB listing keeper.
listOrgB , err := clientOrgB . Resource . List ( ctx , metav1 . ListOptions { } )
require . NoError ( t , err )
require . NotNil ( t , listOrgB )
require . Len ( t , listOrgB . Items , 1 )
require . Equal ( t , * keeperOrgB , listOrgB . Items [ 0 ] )
} )
} )
t . Run ( "keeper actions without having required permissions" , func ( t * testing . T ) {
// Create users on a random org without specifying secrets-related permissions.
editorP := mustCreateUsers ( t , helper , nil ) . Editor
clientP := helper . GetResourceClient ( apis . ResourceClientArgs {
User : editorP ,
GVR : gvrKeepers ,
} )
// GET
rawGet , err := clientP . Resource . Get ( ctx , "some-keeper" , metav1 . GetOptions { } )
require . Error ( t , err )
require . Nil ( t , rawGet )
var statusGetErr * apierrors . StatusError
require . True ( t , errors . As ( err , & statusGetErr ) )
require . EqualValues ( t , http . StatusForbidden , statusGetErr . Status ( ) . Code )
// LIST
rawList , err := clientP . Resource . List ( ctx , metav1 . ListOptions { } )
require . Error ( t , err )
require . Nil ( t , rawList )
var statusListErr * apierrors . StatusError
require . True ( t , errors . As ( err , & statusListErr ) )
require . EqualValues ( t , http . StatusForbidden , statusListErr . Status ( ) . Code )
// CREATE
testKeeper := helper . LoadYAMLOrJSONFile ( "testdata/keeper-gcp-generate.yaml" ) // to pass validation before authz.
rawCreate , err := clientP . Resource . Create ( ctx , testKeeper , metav1 . CreateOptions { } )
require . Error ( t , err )
require . Nil ( t , rawCreate )
var statusCreateErr * apierrors . StatusError
require . True ( t , errors . As ( err , & statusCreateErr ) )
require . EqualValues ( t , http . StatusForbidden , statusCreateErr . Status ( ) . Code )
// UPDATE
testKeeper . SetName ( "test" ) // to pass validation before authz.
rawUpdate , err := clientP . Resource . Update ( ctx , testKeeper , metav1 . UpdateOptions { } )
require . Error ( t , err )
require . Nil ( t , rawUpdate )
var statusUpdateErr * apierrors . StatusError
require . True ( t , errors . As ( err , & statusUpdateErr ) )
require . EqualValues ( t , http . StatusForbidden , statusUpdateErr . Status ( ) . Code )
// DELETE
err = clientP . Resource . Delete ( ctx , "some-keeper" , metav1 . DeleteOptions { } )
require . Error ( t , err )
var statusDeleteErr * apierrors . StatusError
require . True ( t , errors . As ( err , & statusDeleteErr ) )
require . EqualValues ( t , http . StatusForbidden , statusDeleteErr . Status ( ) . Code )
} )
t . Run ( "keeper actions with permissions but with limited scope" , func ( t * testing . T ) {
suffix := strconv . FormatInt ( rand . Int64 ( ) , 10 )
// Fix the Keeper names.
keeperName := "kp-" + suffix
testKeeper := helper . LoadYAMLOrJSONFile ( "testdata/keeper-gcp-generate.yaml" )
testKeeper . SetName ( keeperName )
keeperNameAnother := "kp-another-" + suffix
testKeeperAnother := helper . LoadYAMLOrJSONFile ( "testdata/keeper-gcp-generate.yaml" )
testKeeperAnother . SetName ( keeperNameAnother )
// Fix the org ID because we will create another user with scope "all" permissions on the same org, to compare.
orgID := rand . Int64 ( ) + 2
// Permissions which allow any action, but scoped actions (get, update, delete) only on `keeperName` and NO OTHER Keeper.
scopedLimitedPermissions := map [ string ] ResourcePermission {
ResourceKeepers : {
Actions : ActionsAllKeepers ,
Name : keeperName ,
} ,
}
// Create users (+ client) with permission to manage ONLY the Keeper `keeperName`.
editorLimited := mustCreateUsersWithOrg ( t , helper , orgID , scopedLimitedPermissions ) . Editor
clientScopedLimited := helper . GetResourceClient ( apis . ResourceClientArgs {
User : editorLimited ,
GVR : gvrKeepers ,
} )
// Create users (+ client) with permission to manage ANY Keepers.
scopedAllPermissions := map [ string ] ResourcePermission {
ResourceKeepers : {
Actions : ActionsAllKeepers ,
Name : "*" , // this or not sending a `Name` have the same effect.
} ,
}
editorAll := mustCreateUsersWithOrg ( t , helper , orgID , scopedAllPermissions ) . Editor
clientScopedAll := helper . GetResourceClient ( apis . ResourceClientArgs {
User : editorAll ,
GVR : gvrKeepers ,
} )
t . Run ( "CREATE" , func ( t * testing . T ) {
// Create the Keeper with the limited client.
rawCreateLimited , err := clientScopedLimited . Resource . Create ( ctx , testKeeper , metav1 . CreateOptions { } )
require . NoError ( t , err )
require . NotNil ( t , rawCreateLimited )
// Create another Keeper with the limited client, because the action is not scoped (no `name` available).
rawCreateLimited , err = clientScopedLimited . Resource . Create ( ctx , testKeeperAnother , metav1 . CreateOptions { } )
require . NoError ( t , err )
require . NotNil ( t , rawCreateLimited )
} )
t . Run ( "READ" , func ( t * testing . T ) {
// Retrieve `keeperName` from the limited client.
rawGetLimited , err := clientScopedLimited . Resource . Get ( ctx , keeperName , metav1 . GetOptions { } )
require . NoError ( t , err )
require . NotNil ( t , rawGetLimited )
require . Equal ( t , rawGetLimited . GetUID ( ) , rawGetLimited . GetUID ( ) )
// Retrieve `keeperName` from the scope-all client.
rawGetAll , err := clientScopedAll . Resource . Get ( ctx , keeperName , metav1 . GetOptions { } )
require . NoError ( t , err )
require . NotNil ( t , rawGetAll )
require . Equal ( t , rawGetAll . GetUID ( ) , rawGetLimited . GetUID ( ) )
// Even though we can create it, we cannot retrieve `keeperNameAnother` from the limited client.
rawGetLimited , err = clientScopedLimited . Resource . Get ( ctx , keeperNameAnother , metav1 . GetOptions { } )
require . Error ( t , err )
require . Nil ( t , rawGetLimited )
var statusGetErr * apierrors . StatusError
require . True ( t , errors . As ( err , & statusGetErr ) )
require . EqualValues ( t , http . StatusForbidden , statusGetErr . Status ( ) . Code )
// Retrieve `keeperNameAnother` from the scope-all client.
rawGetAll , err = clientScopedAll . Resource . Get ( ctx , keeperNameAnother , metav1 . GetOptions { } )
require . NoError ( t , err )
require . NotNil ( t , rawGetAll )
} )
t . Run ( "LIST" , func ( t * testing . T ) {
// List Keepers from the limited client should return only 1, but it doesn't.
rawList , err := clientScopedLimited . Resource . List ( ctx , metav1 . ListOptions { } )
require . NoError ( t , err )
require . NotNil ( t , rawList )
require . Len ( t , rawList . Items , 1 ) // TODO: Can view both Keepers. How can we limit that?
// List Keepers from the scope-all client should return all of them.
rawList , err = clientScopedAll . Resource . List ( ctx , metav1 . ListOptions { } )
require . NoError ( t , err )
require . NotNil ( t , rawList )
require . Len ( t , rawList . Items , 2 )
} )
t . Run ( "UPDATE" , func ( t * testing . T ) {
// Update `keeperName` from the limited client.
testKeeperUpdate := testKeeper . DeepCopy ( )
testKeeperUpdate . Object [ "spec" ] . ( map [ string ] any ) [ "title" ] = "keeper-title-1234"
rawUpdate , err := clientScopedLimited . Resource . Update ( ctx , testKeeperUpdate , metav1 . UpdateOptions { } )
require . NoError ( t , err )
require . NotNil ( t , rawUpdate )
// Try to update `keeperNameAnother` from the limited client.
testKeeperAnotherUpdate := testKeeperAnother . DeepCopy ( )
testKeeperAnotherUpdate . Object [ "spec" ] . ( map [ string ] any ) [ "title" ] = "keeper-title-5678"
rawUpdate , err = clientScopedLimited . Resource . Update ( ctx , testKeeperAnotherUpdate , metav1 . UpdateOptions { } )
require . Error ( t , err )
require . Nil ( t , rawUpdate )
var statusUpdateErr * apierrors . StatusError
require . True ( t , errors . As ( err , & statusUpdateErr ) )
require . EqualValues ( t , http . StatusForbidden , statusUpdateErr . Status ( ) . Code )
// Update `keeperNameAnother` from the scope-all client.
rawUpdate , err = clientScopedAll . Resource . Update ( ctx , testKeeperAnotherUpdate , metav1 . UpdateOptions { } )
require . NoError ( t , err )
require . NotNil ( t , rawUpdate )
} )
// Keep this last for cleaning up the resources.
t . Run ( "DELETE" , func ( t * testing . T ) {
// Try to delete `keeperNameAnother` from the limited client.
err := clientScopedLimited . Resource . Delete ( ctx , keeperNameAnother , metav1 . DeleteOptions { } )
require . Error ( t , err )
var statusDeleteErr * apierrors . StatusError
require . True ( t , errors . As ( err , & statusDeleteErr ) )
require . EqualValues ( t , http . StatusForbidden , statusDeleteErr . Status ( ) . Code )
// Delete `keeperNameAnother` from the scope-all client.
err = clientScopedAll . Resource . Delete ( ctx , keeperNameAnother , metav1 . DeleteOptions { } )
require . NoError ( t , err )
require . NotNil ( t , listOrg1 )
require . Len ( t , listOrg1 . Items , 1 )
require . Equal ( t , * keeperOrg1 , listOrg1 . Items [ 0 ] )
// Org2 listing keeper.
listOrg2 , err := clientOrg2 . Resource . List ( ctx , metav1 . ListOptions { } )
// Delete `keeperName` from the limited client.
err = clientScopedLimited . Resource . Delete ( ctx , keeperName , metav1 . DeleteOptions { } )
require . NoError ( t , err )
require . NotNil ( t , listOrg2 )
require . Len ( t , listOrg2 . Items , 1 )
require . Equal ( t , * keeperOrg2 , listOrg2 . Items [ 0 ] )
} )
} )
}