package integration_test import ( "context" "encoding/hex" "fmt" "github.com/golang/mock/gomock" "github.com/google/uuid" "k8s.io/utils/ptr" "edge-infra.dev/pkg/edge/api/graph/model" "edge-infra.dev/pkg/edge/api/mocks" "edge-infra.dev/pkg/edge/api/services/edgenode" "edge-infra.dev/pkg/edge/api/services/edgenode/activationcode" "edge-infra.dev/pkg/edge/api/services/edgenode/common" "edge-infra.dev/pkg/lib/crypto" pxe "edge-infra.dev/pkg/sds/ien/k8s/controllers/pxe/common" "edge-infra.dev/test/framework/integration" ) const ( // for use in activation_code_test.go (terminal activation) activationClusterEdgeID = "d7e6b441-2781-49f0-af49-24b7acef586c" // for use in activation_code_test.go (terminal activation) activationTerminalID = "84defb06-85b8-4db4-b734-f3dc100853ea" // for use in activation_code_test.go (terminal activation) activationProjectID = "test-org" // for use in activation_code_test.go (terminal activation) activationBannerID = "3396a52c-6a22-4049-9593-5a63b596a101" ) func (s *Suite) initEdgeNodeServices() (*mocks.MockSecretService, *mocks.MockGCPService, common.ActivationCode) { mock := gomock.NewController(s.T()) secretSvc := mocks.NewMockSecretService(mock) gcpSvc := mocks.NewMockGCPService(mock) activationSvc := edgenode.NewActivationCodeService(s.DB, s.Resolver.TerminalService, s.Resolver.StoreClusterService, s.Resolver.ClusterConfigService, secretSvc, gcpSvc, s.Resolver.BannerService) return secretSvc, gcpSvc, activationSvc } func (s *Suite) TestSyncToStore() { integration.SkipIf(s.Framework) // initialise test services _, gcpSvc, activationSvc := s.initEdgeNodeServices() // set up test resources terminalID := uuid.NewString() cluster := &model.Cluster{ProjectID: activationProjectID} code := "ACTIVATION_CODE" expectSyncToStore(gcpSvc, terminalID, activationProjectID) s.NoError(err) err = activationSvc.SyncToStore(context.Background(), terminalID, cluster, code) s.NoError(err) } // expectSyncToStore sets up the expectations for the SyncToStore service function func expectSyncToStore(gcpSvc *mocks.MockGCPService, terminalID string, projectID string) { secretName := fmt.Sprintf("%s-%s", activationcode.SecretType, terminalID) gcpSvc.EXPECT().AddSecret(gomock.Any(), secretName, activationcode.Workload, activationcode.SecretType, gomock.Any(), projectID, ptr.To(activationcode.Workload), nil).Return(nil) } func expectFetchFromSecret(gcpSvc *mocks.MockGCPService, secretResp []*model.SecretManagerResponse, terminalID string) { secretName := fmt.Sprintf("%s-%s", activationcode.SecretType, terminalID) gcpSvc.EXPECT().GetSecrets(gomock.Any(), &secretName, ptr.To(activationcode.Workload), ptr.To(activationcode.SecretType), true, activationProjectID).Return(secretResp, nil) } func (s *Suite) TestFetchFromSecret() { integration.SkipIf(s.Framework) _, gcpSvc, activationSvc := s.initEdgeNodeServices() terminalID := activationTerminalID secretName := fmt.Sprintf("%s-%s", activationcode.SecretType, terminalID) testCases := []struct { secretResp []*model.SecretManagerResponse err error }{ { // need secret's values secretResp: []*model.SecretManagerResponse{ {Name: secretName}, }, err: activationcode.ErrInvalidActivationSecret, }, { // cannot have no secrets secretResp: []*model.SecretManagerResponse{}, err: activationcode.ErrInvalidActivationSecretCount, }, { // cannot have more than 1 secret secretResp: []*model.SecretManagerResponse{ {Name: secretName}, {Name: secretName}, }, err: activationcode.ErrInvalidActivationSecretCount, }, { // secret cannot have multiple values secretResp: []*model.SecretManagerResponse{ { Name: secretName, Values: []*model.KeyValuesOutput{ { Key: "key1", Value: "value1", }, { Key: "key2", Value: "value2", }, }, }, }, err: activationcode.ErrInvalidActivationSecret, }, { // valid secretResp: []*model.SecretManagerResponse{ { Name: secretName, Values: []*model.KeyValuesOutput{ { Key: "key1", Value: "value1", }, }, }, }, err: nil, }, } for _, test := range testCases { expectFetchFromSecret(gcpSvc, test.secretResp, terminalID) _, err := activationSvc.FetchFromSecret(context.Background(), terminalID) s.ErrorIs(err, test.err) } } func (s *Suite) TestRefreshActivationCode() { integration.SkipIf(s.Framework) // initialise test services _, gcpSvc, activationSvc := s.initEdgeNodeServices() terminalID := activationTerminalID expectSyncToStore(gcpSvc, terminalID, activationProjectID) // hashed version is seeded for this test terminal existingCode := "XJTQ8UKN9DWNRF3" newCode, err := activationSvc.Refresh(context.Background(), terminalID) s.NoError(err) s.NotEqual(existingCode, newCode) } func (s *Suite) TestCreateActivationCode() { integration.SkipIf(s.Framework) // initialise test services _, gcpSvc, activationSvc := s.initEdgeNodeServices() // set up test resources terminal := &model.Terminal{ TerminalID: activationTerminalID, ClusterEdgeID: activationClusterEdgeID, } cluster := &model.Cluster{ ProjectID: activationProjectID, } code, err := crypto.GenerateRandomActivationCode() s.NoError(err) // set up expectations for internal call to SyncToStore expectSyncToStore(gcpSvc, terminal.TerminalID, cluster.ProjectID) ctx := context.Background() err = activationSvc.Create(ctx, code, terminal) s.NoError(err) // check activation code correctly created in DB dbCode, err := activationSvc.Fetch(ctx, terminal.TerminalID) s.NoError(err) s.NotNil(dbCode) s.Equal(hex.EncodeToString(code.Hashed()), *dbCode) } func expectDeleteActivationCode(gcpSvc *mocks.MockGCPService, secretSvc *mocks.MockSecretService, terminalID string, projectID string) { secretName := fmt.Sprintf("%s-%s", activationcode.SecretType, terminalID) gcpSvc.EXPECT().DeleteSecret(gomock.Any(), secretName, projectID).Return(true, nil) secretSvc.EXPECT().DeleteExternalSecret(gomock.Any(), secretName, pxe.PXENamespace, projectID, gomock.Not(nil), gomock.Any(), secretName).Return(nil) } func (s *Suite) TestSyncAllToStore() { integration.SkipIf(s.Framework) _, gcpSvc, activationSvc := s.initEdgeNodeServices() // set up test resources cluster := &model.Cluster{ ClusterEdgeID: activationClusterEdgeID, ProjectID: activationProjectID, } terminalID := activationTerminalID secretName := fmt.Sprintf("%s-%s", activationcode.SecretType, terminalID) secretResp := []*model.SecretManagerResponse{ { Name: secretName, Values: []*model.KeyValuesOutput{ { Key: "key1", Value: "value1", }, }, }, } expectFetchFromSecret(gcpSvc, secretResp, terminalID) expectSyncToStore(gcpSvc, terminalID, cluster.ProjectID) err := activationSvc.SyncAllToStore(context.Background(), cluster.ClusterEdgeID) s.NoError(err) } func (s *Suite) TestMarkUsed() { integration.SkipIf(s.Framework) // initialise test services secretSvc, gcpSvc, activationSvc := s.initEdgeNodeServices() // set up test resources terminalID := activationTerminalID cluster := &model.Cluster{ ProjectID: activationProjectID, BannerEdgeID: activationBannerID, } expectDeleteActivationCode(gcpSvc, secretSvc, terminalID, cluster.ProjectID) ctx := context.Background() err := activationSvc.MarkUsed(ctx, terminalID, cluster, nil) s.NoError(err) // check activation code correctly reset in DB dbCode, err := activationSvc.Fetch(ctx, terminalID) s.NoError(err) s.NotNil(dbCode) s.Equal("", *dbCode) } func (s *Suite) TestCleanupStore() { integration.SkipIf(s.Framework) // initialise test services secretSvc, gcpSvc, activationSvc := s.initEdgeNodeServices() // set up test resources cluster := &model.Cluster{ ProjectID: activationProjectID, ClusterEdgeID: activationClusterEdgeID, BannerEdgeID: activationBannerID, } terminalID := activationTerminalID expectDeleteActivationCode(gcpSvc, secretSvc, terminalID, cluster.ProjectID) err := activationSvc.CleanupStore(context.Background(), cluster) s.NoError(err) }