feat: OnDeleteCatalog (#10)

* feat: OnDeleteCatalog

Delete managed creds (creds we rotated previously) when a host catalog is deleted
This commit is contained in:
Elim Tsiagbey authored and GitHub committed 2024-10-28 11:00:14 -04:00
1 parent 08fd6e5b01
commit ba53ae16b2
4 files changed
+135 -3

No files matched your search

+30 -1
View File
@@ -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
}
+103
View File
@@ -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{}