feat: OnDeleteCatalog (#10)
* feat: OnDeleteCatalog Delete managed creds (creds we rotated previously) when a host catalog is deleted
This commit is contained in:
1 parent
08fd6e5b01
commit
ba53ae16b2
4 files changed
+135
-3
No files matched your search
@@ -234,10 +234,39 @@ func (p *HostPlugin) OnDeleteCatalog(ctx context.Context, req *pb.OnDeleteCatalo
|
||||
return nil, status.Error(codes.InvalidArgument, "new catalog missing attributes")
|
||||
}
|
||||
|
||||
if _, err := getCatalogAttributes(attrs); err != nil {
|
||||
catalogAttributes, err := getCatalogAttributes(attrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credState, err := credential.PersistedStateFromProto(
|
||||
req.GetPersisted().GetSecrets(),
|
||||
catalogAttributes.CredentialAttributes)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error getting persisted state from proto: %s", err)
|
||||
}
|
||||
|
||||
_, err = newGCPCatalogPersistedState(
|
||||
append([]gcpCatalogPersistedStateOption{
|
||||
withCredentials(credState),
|
||||
}, p.testCatalogStateOpts...)...,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
|
||||
}
|
||||
|
||||
// try to delete static credentials for dynamic and static credentials
|
||||
if credState.CredentialsConfig.IsRotatable() {
|
||||
if !credState.CredsLastRotatedTime.IsZero() {
|
||||
// Delete old/existing credentials. This is done with the same
|
||||
// credentials to ensure that it has the proper permissions to do
|
||||
// it.
|
||||
if err := credState.DeleteCreds(ctx, p.testGCPClientOpts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.OnDeleteCatalogResponse{}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -23,7 +24,9 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
@@ -844,6 +847,106 @@ func TestUpdateCatalog(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCatalog(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pb.OnDeleteCatalogRequest
|
||||
catalogOpts []gcpCatalogPersistedStateOption
|
||||
deleteCredError error
|
||||
expectedRsp *pb.OnDeleteCatalogResponse
|
||||
expectedErr string
|
||||
expectedErrCode codes.Code
|
||||
}{
|
||||
{
|
||||
name: "persisted state setup error",
|
||||
req: &pb.OnDeleteCatalogRequest{
|
||||
Catalog: &hostcatalogs.HostCatalog{
|
||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
cred.ConstProjectId: structpb.NewStringValue("test-project-id"),
|
||||
cred.ConstZone: structpb.NewStringValue("us-central1-a"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Persisted: &pb.HostCatalogPersisted{
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
cred.ConstPrivateKeyId: structpb.NewStringValue("test-private-key-id"),
|
||||
cred.ConstPrivateKey: structpb.NewStringValue("test-private-key"),
|
||||
cred.ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
catalogOpts: []gcpCatalogPersistedStateOption{
|
||||
func(s *gcpCatalogPersistedState) error {
|
||||
return fmt.Errorf("error loading persisted state")
|
||||
},
|
||||
},
|
||||
expectedErr: "error loading persisted state",
|
||||
expectedErrCode: codes.InvalidArgument,
|
||||
},
|
||||
{
|
||||
name: "delete error",
|
||||
req: &pb.OnDeleteCatalogRequest{
|
||||
Catalog: &hostcatalogs.HostCatalog{
|
||||
Attrs: &hostcatalogs.HostCatalog_Attributes{
|
||||
Attributes: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
cred.ConstClientEmail: structpb.NewStringValue("test@test.com"),
|
||||
cred.ConstProjectId: structpb.NewStringValue("test-project-id"),
|
||||
cred.ConstZone: structpb.NewStringValue("us-central1-a"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Persisted: &pb.HostCatalogPersisted{
|
||||
Secrets: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
cred.ConstPrivateKeyId: structpb.NewStringValue("test-private-key-id"),
|
||||
cred.ConstPrivateKey: structpb.NewStringValue("test-private-key"),
|
||||
cred.ConstCredsLastRotatedTime: structpb.NewStringValue("2006-01-02T15:04:05+07:00"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
deleteCredError: fmt.Errorf("failed to delete access key"),
|
||||
expectedErr: "failed to delete access key",
|
||||
expectedErrCode: codes.Unknown,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
testIAMAdminServer := cred.NewTestIAMAdminServer(nil, tc.deleteCredError)
|
||||
|
||||
gsrv := cred.NewGRPCServer()
|
||||
adminpb.RegisterIAMServer(gsrv.Server, testIAMAdminServer)
|
||||
addr, err := gsrv.Start()
|
||||
require.NoError(err)
|
||||
|
||||
p := &HostPlugin{
|
||||
testGCPClientOpts: []option.ClientOption{
|
||||
option.WithEndpoint(addr),
|
||||
option.WithoutAuthentication(),
|
||||
option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())),
|
||||
option.WithTokenSource(nil),
|
||||
},
|
||||
testCatalogStateOpts: tc.catalogOpts,
|
||||
}
|
||||
|
||||
_, err = p.OnDeleteCatalog(context.Background(), tc.req)
|
||||
require.Error(err)
|
||||
require.Contains(err.Error(), tc.expectedErr)
|
||||
require.Equal(status.Code(err).String(), tc.expectedErrCode.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
p := &HostPlugin{}
|
||||
|
||||
Reference in new issue
Block a user