Skip to content

Commit

Permalink
feat: purge functionality for documents (#1501)
Browse files Browse the repository at this point in the history
  • Loading branch information
jskelin authored Jun 12, 2024
1 parent b6c8652 commit 78a2c9c
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 8 deletions.
4 changes: 3 additions & 1 deletion cmd/monaco/purge/purge.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"context"
"errors"
"fmt"
"path/filepath"

"github.com/dynatrace/dynatrace-configuration-as-code/v2/cmd/monaco/dynatrace"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/internal/errutils"
"github.com/dynatrace/dynatrace-configuration-as-code/v2/internal/log"
Expand All @@ -29,7 +31,6 @@ import (
manifestloader "github.com/dynatrace/dynatrace-configuration-as-code/v2/pkg/manifest/loader"
"github.com/spf13/afero"
"golang.org/x/exp/maps"
"path/filepath"
)

func purge(fs afero.Fs, deploymentManifestPath string, environmentNames []string, apiNames []string) error {
Expand Down Expand Up @@ -98,5 +99,6 @@ func getClientSet(env manifest.EnvironmentDefinition) (delete.ClientSet, error)
Settings: clients.Settings(),
Automation: clients.Automation(),
Buckets: clients.Bucket(),
Documents: clients.Document(),
}, nil
}
9 changes: 9 additions & 0 deletions internal/featureflags/temporary.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,15 @@ func Documents() FeatureFlag {
}
}

// DeleteDocuments toggles whether documents are deleted
// Introduced: 2024-04-16; v2.14.2
func DeleteDocuments() FeatureFlag {
return FeatureFlag{
envName: "MONACO_FEAT_DELETE_DOCUMENTS",
defaultEnabled: false,
}
}

// Documents toggles whether insertAfter config parameter is persisted for ordered settings.
// Introduced: 2024-05-15; v2.14.0
func PersistSettingsOrder() FeatureFlag {
Expand Down
11 changes: 10 additions & 1 deletion pkg/delete/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func Configs(ctx context.Context, clients ClientSet, _ api.APIs, automationResou
}
err = bucket.Delete(ctx, clients.Buckets, entries)
} else if t == "document" {
if featureflags.Documents().Enabled() {
if featureflags.Documents().Enabled() && featureflags.DeleteDocuments().Enabled() {
if clients.Documents == nil {
log.WithCtxFields(ctx).WithFields(field.Type(t)).Warn("Skipped deletion of %d Document configuration(s) as API client was unavailable.", len(entries))
continue
Expand Down Expand Up @@ -143,6 +143,15 @@ func All(ctx context.Context, clients ClientSet, apis api.APIs) error {
errs++
}

if featureflags.Documents().Enabled() && featureflags.DeleteDocuments().Enabled() {
if clients.Documents == nil {
log.Warn("Skipped deletion of Documents configurations as appropriate client was unavailable.")
} else if err := document.DeleteAll(ctx, clients.Documents); err != nil {
log.Error("Failed to delete all Document configurations: %v", err)
errs++
}
}

if errs > 0 {
return fmt.Errorf("failed to delete all configurations for %d types", errs)
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/delete/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func TestDeleteSettings(t *testing.T) {

}

func TestDeleteAutomations(t *testing.T) {
func TestDelete_Automations(t *testing.T) {
t.Run("TestDeleteAutomations", func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodDelete && strings.Contains(req.RequestURI, "workflows") {
Expand Down Expand Up @@ -1056,6 +1056,7 @@ func TestDeleteClassicKeyUserActionsWeb(t *testing.T) {

func TestDelete_Documents(t *testing.T) {
t.Setenv(featureflags.Documents().EnvName(), "true")
t.Setenv(featureflags.DeleteDocuments().EnvName(), "true")
t.Run("delete via coordinate", func(t *testing.T) {
given := pointer.DeletePointer{
Type: "document",
Expand Down
6 changes: 3 additions & 3 deletions pkg/delete/internal/classic/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func Delete(ctx context.Context, client client.ConfigClient, dps []pointer.Delet
if theAPI.HasParent() {
parentID, e = resolveIdentifier(ctx, client, theAPI.Parent, toIdentifier(dp.Scope, "", ""))
if e != nil && !is404(e) {
log.WithFields(field.Error(e)).Error("unable to resolve config ID: %w")
log.WithFields(field.Error(e)).Error("unable to resolve config ID: %v", e)
err = errors.Join(err, e)
continue
} else if parentID == "" {
Expand All @@ -58,7 +58,7 @@ func Delete(ctx context.Context, client client.ConfigClient, dps []pointer.Delet
if id == "" {
id, e = resolveIdentifier(ctx, client, &a, toIdentifier(dp.Identifier, dp.ActionType, dp.Domain))
if e != nil && !is404(e) {
log.WithFields(field.Error(e)).Error("unable to resolve config ID: %w")
log.WithFields(field.Error(e)).Error("unable to resolve config ID: %v", e)
err = errors.Join(err, e)
continue
} else if id == "" {
Expand All @@ -68,7 +68,7 @@ func Delete(ctx context.Context, client client.ConfigClient, dps []pointer.Delet
}

if e := client.DeleteConfigById(a, id); e != nil && !is404(e) {
log.WithFields(field.Error(e)).Error("failed to delete config: %w", e)
log.WithFields(field.Error(e)).Error("failed to delete config: %v", e)
err = errors.Join(err, e)
}
log.Debug("successfully deleted")
Expand Down
19 changes: 17 additions & 2 deletions pkg/delete/internal/document/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ func deleteSingle(ctx context.Context, c client, dp pointer.DeletePointer) error

_, err := c.Delete(ctx, id)
if err != nil && !isAPIErrorStatusNotFound(err) {
logger.Error("Failed to delete entry with id '%s' - %v", id, err)
return err
return fmt.Errorf("failed to delete entry with id '%s' - %w", id, err)
}

logger.Debug("Config with ID '%s' successfully deleted", id)
Expand Down Expand Up @@ -108,3 +107,19 @@ func isAPIErrorStatusNotFound(err error) bool {

return apiErr.StatusCode == http.StatusNotFound
}

func DeleteAll(ctx context.Context, c client) error {
listResponse, err := c.List(ctx, fmt.Sprintf("type='%s' or type='%s'", documents.Dashboard, documents.Notebook))
if err != nil {
return err
}

var retErr error
for _, x := range listResponse.Responses {
err := deleteSingle(ctx, c, pointer.DeletePointer{Type: x.Type, OriginObjectId: x.ID})
if err != nil {
retErr = errors.Join(retErr, err)
}
}
return retErr
}
80 changes: 80 additions & 0 deletions pkg/delete/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,86 @@ func TestLoad_DocumentsEntry(t *testing.T) {
})
}

func TestLoad_Automation(t *testing.T) {
tests := []testCase{
{
name: "declare via coordinate",
given: []byte(`delete:
- type: workflow
project: my-project
id: my-workflow
- type: scheduling-rule
project: my-project
id: my-rule
- type: business-calendar
project: my-project
id: my-calendar
`),
want: delete.DeleteEntries{
"workflow": {{
Project: "my-project",
Type: "workflow",
Identifier: "my-workflow",
}},
"scheduling-rule": {{
Project: "my-project",
Type: "scheduling-rule",
Identifier: "my-rule",
}},
"business-calendar": {{
Project: "my-project",
Type: "business-calendar",
Identifier: "my-calendar",
}},
},
},
{
name: "declare via originId",
given: []byte(`delete:
- type: workflow
objectId: workflow-id
- type: scheduling-rule
objectId: rule-id
- type: business-calendar
objectId: calendar-id
`),
want: delete.DeleteEntries{
"workflow": {{
OriginObjectId: "workflow-id",
Type: "workflow",
}},
"scheduling-rule": {{
OriginObjectId: "rule-id",
Type: "scheduling-rule",
}},
"business-calendar": {{
OriginObjectId: "calendar-id",
Type: "business-calendar",
}},
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
actual, err := delete.LoadEntriesFromFile(createDeleteFile(t, tc.given))
if tc.want != nil {
require.NoError(t, err)
require.Equal(t, tc.want, actual)
} else {
require.Error(t, err)
assert.Empty(t, actual)
}
})
}
}

type testCase struct {
name string
given []byte
want delete.DeleteEntries
}

func createDeleteFile(t testing.TB, content []byte) (afero.Fs, string) {
t.Helper()

Expand Down

0 comments on commit 78a2c9c

Please sign in to comment.