Skip to content

Commit

Permalink
feat(manifest-service): add DeleteEnvironment transformer (#2217)
Browse files Browse the repository at this point in the history
Add transformer for deleting the environment folder from the manifest
repo.
Ref: SRX-R3DKS5
  • Loading branch information
EdSwordsmith authored Jan 24, 2025
1 parent d1127c0 commit d051703
Show file tree
Hide file tree
Showing 3 changed files with 256 additions and 2 deletions.
3 changes: 3 additions & 0 deletions services/manifest-repo-export-service/pkg/cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,9 @@ func getTransformer(ctx context.Context, eslEventType db.EventType) (repository.
case db.EvtUndeployApplication:
//exhaustruct:ignore
return &repository.UndeployApplication{}, nil
case db.EvtDeleteEnvironment:
//exhaustruct:ignore
return &repository.DeleteEnvironment{}, nil
}
return nil, fmt.Errorf("could not find transformer for event type %v", eslEventType)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"slices"

api "github.com/freiheit-com/kuberpult/pkg/api/v1"
"github.com/freiheit-com/kuberpult/pkg/argocd"
"github.com/freiheit-com/kuberpult/pkg/auth"
"github.com/freiheit-com/kuberpult/pkg/config"
"github.com/freiheit-com/kuberpult/pkg/conversion"
Expand Down Expand Up @@ -1955,3 +1956,43 @@ func (c *DeleteEnvironmentGroupLock) Transform(
// group locks are handled on the cd-service, and split into environment locks
return "empty commit for group lock deletion", nil
}

type DeleteEnvironment struct {
Environment string `json:"env"`
TransformerMetadata `json:"metadata"`
TransformerEslVersion db.TransformerID `json:"-"` // Tags the transformer with EventSourcingLight eslVersion
}

func (d *DeleteEnvironment) GetEslVersion() db.TransformerID {
return d.TransformerEslVersion
}

func (d *DeleteEnvironment) SetEslVersion(id db.TransformerID) {
d.TransformerEslVersion = id
}

func (d *DeleteEnvironment) GetDBEventType() db.EventType {
return db.EvtDeleteEnvironment
}

func (d *DeleteEnvironment) Transform(ctx context.Context, state *State, t TransformerContext, transaction *sql.Tx) (string, error) {
fs := state.Filesystem
envDir := fs.Join("environments", d.Environment)
argoCdAppFile := fs.Join("argocd", string(argocd.V1Alpha1), fmt.Sprintf("%s.yaml", d.Environment))

err := fs.Remove(envDir)
if errors.Is(err, os.ErrNotExist) {
logger.FromContext(ctx).Sugar().Warnf("DeleteEnvironment: environment directory %q does not exist.", envDir)
} else if err != nil {
return "", fmt.Errorf("error deleting the environment directory %q: %w", envDir, err)
}

err = fs.Remove(argoCdAppFile)
if errors.Is(err, os.ErrNotExist) {
logger.FromContext(ctx).Sugar().Warnf("DeleteEnvironment: environment's argocd app file %q does not exist.", envDir)
} else if err != nil {
return "", fmt.Errorf("error deleting the environment's argocd app file %q: %w", argoCdAppFile, err)
}

return fmt.Sprintf("delete environment %q", d.Environment), nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,8 @@ func verifyContent(fs billy.Filesystem, required []*FilenameAndData) error {
for _, contentRequirement := range required {
if data, err := util.ReadFile(fs, contentRequirement.path); err != nil {
return fmt.Errorf("error while opening file %s, error: %w", contentRequirement.path, err)
} else if string(data) != string(contentRequirement.fileData) {
return fmt.Errorf("actual file content of file '%s' is not equal to required content.\nExpected: '%s', actual: '%s'", contentRequirement.path, contentRequirement.fileData, string(data))
} else if diff := cmp.Diff(string(data), string(contentRequirement.fileData)); diff != "" {
return fmt.Errorf("actual file content of file '%s' is not equal to required content.\nDiff: %s", contentRequirement.path, diff)
}
}
return nil
Expand Down Expand Up @@ -3021,3 +3021,213 @@ func TestUndeployLogic(t *testing.T) {
})
}
}

func TestDeleteEnvironment(t *testing.T) {
const authorName = "testAuthorName"
const authorEmail = "[email protected]"
envAcceptanceConfig := config.EnvironmentConfig{
Upstream: &config.EnvironmentConfigUpstream{Environment: envAcceptance, Latest: true},
ArgoCd: &config.EnvironmentConfigArgoCd{},
}
envAcceptance2Config := config.EnvironmentConfig{
Upstream: &config.EnvironmentConfigUpstream{Environment: envAcceptance2, Latest: true},
ArgoCd: &config.EnvironmentConfigArgoCd{},
}

tcs := []struct {
Name string
Transformers []Transformer
expectedData []*FilenameAndData
expectedMissing []*FilenameAndData
expectedMessage string
expectedError error
}{
{
Name: "create an environment and delete it",
Transformers: []Transformer{
&CreateEnvironment{
Environment: envAcceptance,
Config: envAcceptanceConfig,
TransformerMetadata: TransformerMetadata{
AuthorName: authorName,
AuthorEmail: authorEmail,
},
TransformerEslVersion: 1,
},
&DeleteEnvironment{
Environment: envAcceptance,
TransformerMetadata: TransformerMetadata{
AuthorName: authorName,
AuthorEmail: authorEmail,
},
TransformerEslVersion: 2,
},
},
expectedMissing: []*FilenameAndData{
{
path: "/environments/acceptance",
fileData: []byte(authorEmail),
},
{
path: "/argocd/v1alpha1/acceptance.yaml",
fileData: []byte(authorEmail),
},
},
expectedMessage: "delete environment \"acceptance\"",
},
{
Name: "create two environments and delete one of them",
Transformers: []Transformer{
&CreateEnvironment{
Environment: envAcceptance,
Config: envAcceptanceConfig,
TransformerMetadata: TransformerMetadata{
AuthorName: authorName,
AuthorEmail: authorEmail,
},
TransformerEslVersion: 1,
},
&CreateEnvironment{
Environment: envAcceptance2,
Config: envAcceptance2Config,
TransformerMetadata: TransformerMetadata{
AuthorName: authorName,
AuthorEmail: authorEmail,
},
TransformerEslVersion: 2,
},
&DeleteEnvironment{
Environment: envAcceptance,
TransformerMetadata: TransformerMetadata{
AuthorName: authorName,
AuthorEmail: authorEmail,
},
TransformerEslVersion: 3,
},
},
expectedMissing: []*FilenameAndData{
{
path: "/environments/acceptance",
fileData: []byte(authorEmail),
},
{
path: "/argocd/v1alpha1/acceptance.yaml",
fileData: []byte(authorEmail),
},
},
expectedData: []*FilenameAndData{
{
path: "/environments/acceptance2/config.json",
fileData: []byte(`{
"upstream": {
"environment": "acceptance2",
"latest": true
},
"argocd": {
"destination": {
"name": "",
"server": ""
}
}
}
`),
},
{
path: "/argocd/v1alpha1/acceptance2.yaml",
fileData: []byte(`apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: acceptance2
spec:
description: acceptance2
destinations:
- {}
sourceRepos:
- '*'
`),
},
},
expectedMessage: "delete environment \"acceptance\"",
},
{
Name: "delete an environment that does not exist",
Transformers: []Transformer{
&DeleteEnvironment{
Environment: envAcceptance,
TransformerMetadata: TransformerMetadata{
AuthorName: authorName,
AuthorEmail: authorEmail,
},
TransformerEslVersion: 1,
},
},
expectedMessage: "delete environment \"acceptance\"",
},
}

for _, tc := range tcs {
tc := tc
t.Run(tc.Name, func(t *testing.T) {
repo, _ := setupRepositoryTestWithPath(t)
ctx := AddGeneratorToContext(testutil.MakeTestContext(), testutil.NewIncrementalUUIDGenerator())

dbHandler := repo.State().DBHandler
err := dbHandler.WithTransactionR(ctx, 0, false, func(ctx context.Context, transaction *sql.Tx) error {
// setup:
// this 'INSERT INTO' would be done one the cd-server side, so we emulate it here:
err2 := dbHandler.DBWriteMigrationsTransformer(ctx, transaction)
if err2 != nil {
t.Fatal(err2)
}
err2 = dbHandler.DBWriteEnvironment(ctx, transaction, envAcceptance, envAcceptanceConfig, []string{})
if err2 != nil {
return err2
}
err2 = dbHandler.DBWriteEnvironment(ctx, transaction, envAcceptance2, envAcceptance2Config, []string{})
if err2 != nil {
return err2
}
//populate the database
for _, tr := range tc.Transformers {
err2 := dbHandler.DBWriteEslEventInternal(ctx, tr.GetDBEventType(), transaction, t, db.ESLMetadata{AuthorName: tr.GetMetadata().AuthorName, AuthorEmail: tr.GetMetadata().AuthorEmail})
if err2 != nil {
t.Fatal(err2)
}
}

for _, t := range tc.Transformers {
err := repo.Apply(ctx, transaction, t)
if err != nil {
return err
}
// just for testing, we push each transformer change separately.
// if you need to debug this test, you can git clone the repo
// and we will only see anything if we push.
err = repo.PushRepo(ctx)
if err != nil {
return err
}
}

actualMsg := repo.State().Commit.Message()
if diff := cmp.Diff(tc.expectedMessage, actualMsg); diff != "" {
t.Errorf("commit message mismatch (-want, +got):\n%s", diff)
}

return nil
})

if diff := cmp.Diff(tc.expectedError, err, cmpopts.EquateErrors()); diff != "" {
t.Errorf("error mismatch (-want, +got):\n%s", diff)
}
updatedState := repo.State()

if err := verifyContent(updatedState.Filesystem, tc.expectedData); err != nil {
t.Fatalf("Error while verifying content: %v.\nFilesystem content:\n%s", err, strings.Join(listFiles(updatedState.Filesystem), "\n"))
}
if err := verifyMissing(updatedState.Filesystem, tc.expectedMissing); err != nil {
t.Fatalf("Error while verifying missing content: %v.\nFilesystem content:\n%s", err, strings.Join(listFiles(updatedState.Filesystem), "\n"))
}
})
}
}

0 comments on commit d051703

Please sign in to comment.