Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions otdfctl/cmd/migrate/namespaced_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package migrate

import (
"errors"
"os"

otdfctl "github.com/opentdf/platform/otdfctl/cmd/common"
namespacedpolicy "github.com/opentdf/platform/otdfctl/migrations/namespacedpolicy"
Expand Down Expand Up @@ -61,7 +60,7 @@ func migrateNamespacedPolicy(cmd *cobra.Command, args []string) {
executeNamespacedPolicyCommit(cmd, h, plan, interactive, prompter)
}

if _, err := os.Stdout.WriteString(namespacedpolicy.RenderNamespacedPolicySummary(plan, commit) + "\n"); err != nil {
if _, err := cmd.OutOrStdout().Write([]byte(namespacedpolicy.RenderNamespacedPolicySummary(plan, commit) + "\n")); err != nil {
cli.ExitWithError("could not write namespaced-policy summary", err)
}
}
Expand All @@ -82,7 +81,7 @@ func confirmNamespacedPolicyCommit(cmd *cobra.Command, plan *namespacedpolicy.Pl
func executeNamespacedPolicyCommit(cmd *cobra.Command, h namespacedpolicy.ExecutorHandler, plan *namespacedpolicy.Plan, interactive bool, prompter namespacedpolicy.InteractivePrompter) {
if err := confirmNamespacedPolicyCommit(cmd, plan, interactive, prompter); err != nil {
if errors.Is(err, namespacedpolicy.ErrNamespacedPolicyBackupNotConfirmed) || errors.Is(err, namespacedpolicy.ErrInteractiveReviewAborted) {
writeNamespacedPolicySummary(plan, false, "aborted")
writeNamespacedPolicySummary(cmd, plan, false, "aborted")
}
cli.ExitWithError("could not review namespaced-policy commit", err)
}
Expand All @@ -93,13 +92,13 @@ func executeNamespacedPolicyCommit(cmd *cobra.Command, h namespacedpolicy.Execut
}

if err := executor.Execute(cmd.Context(), plan); err != nil {
writeNamespacedPolicySummary(plan, true, "failure")
writeNamespacedPolicySummary(cmd, plan, true, "failure")
cli.ExitWithError("could not execute namespaced-policy commit", err)
}
}

func writeNamespacedPolicySummary(plan *namespacedpolicy.Plan, commit bool, result string) {
if _, err := os.Stdout.WriteString(namespacedpolicy.RenderNamespacedPolicySummaryWithResult(plan, commit, result) + "\n"); err != nil {
func writeNamespacedPolicySummary(cmd *cobra.Command, plan *namespacedpolicy.Plan, commit bool, result string) {
if _, err := cmd.OutOrStdout().Write([]byte(namespacedpolicy.RenderNamespacedPolicySummaryWithResult(plan, commit, result) + "\n")); err != nil {
cli.ExitWithError("could not write namespaced-policy summary", err)
}
}
33 changes: 0 additions & 33 deletions otdfctl/cmd/migrate/prune/namespacedPolicy.go

This file was deleted.

97 changes: 97 additions & 0 deletions otdfctl/cmd/migrate/prune/namespaced_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package prune

import (
"errors"

otdfctl "github.com/opentdf/platform/otdfctl/cmd/common"
namespacedpolicy "github.com/opentdf/platform/otdfctl/migrations/namespacedpolicy"
"github.com/opentdf/platform/otdfctl/pkg/cli"
"github.com/opentdf/platform/otdfctl/pkg/man"
"github.com/spf13/cobra"
)

func pruneNamespacedPolicyCmd() *cobra.Command {
doc := man.Docs.GetCommand("migrate/prune/namespaced-policy", man.WithRun(pruneNamespacedPolicy))
doc.Args = cobra.NoArgs
doc.Hidden = true
Comment thread
c-r33d marked this conversation as resolved.
doc.Flags().StringP(
doc.GetDocFlag("scope").Name,
doc.GetDocFlag("scope").Shorthand,
doc.GetDocFlag("scope").Default,
doc.GetDocFlag("scope").Description,
)

return &doc.Command
}

func pruneNamespacedPolicy(cmd *cobra.Command, args []string) {
c := cli.New(cmd, args)
scope := c.Flags.GetRequiredString("scope")
prompter := &namespacedpolicy.HuhPrompter{}

commit, err := cmd.InheritedFlags().GetBool("commit")
if err != nil {
cli.ExitWithError("could not read --commit flag", err)
}
interactive, err := cmd.InheritedFlags().GetBool("interactive")
if err != nil {
cli.ExitWithError("could not read --interactive flag", err)
}

h := otdfctl.NewHandler(c)
defer h.Close()

planner, err := namespacedpolicy.NewPrunePlanner(&h, scope)
if err != nil {
cli.ExitWithError("could not create namespaced-policy prune planner", err)
}

plan, err := planner.Plan(cmd.Context())
if err != nil {
cli.ExitWithError("could not build namespaced-policy prune plan", err)
}

if interactive {
if err := namespacedpolicy.ReviewPrunePlan(cmd.Context(), plan, prompter); err != nil {
if errors.Is(err, namespacedpolicy.ErrInteractiveReviewAborted) {
writeNamespacedPolicyPruneSummary(cmd, plan, false, "aborted")
}
cli.ExitWithError("could not review namespaced-policy prune plan", err)
}
}

if commit {
executeNamespacedPolicyPruneCommit(cmd, h, plan, interactive, prompter)
}

if _, err := cmd.OutOrStdout().Write([]byte(namespacedpolicy.RenderNamespacedPolicyPruneSummary(plan, commit) + "\n")); err != nil {
cli.ExitWithError("could not write namespaced-policy prune summary", err)
}
}

func executeNamespacedPolicyPruneCommit(cmd *cobra.Command, h namespacedpolicy.ExecutorHandler, plan *namespacedpolicy.PrunePlan, interactive bool, prompter namespacedpolicy.InteractivePrompter) {
if interactive {
if err := namespacedpolicy.ConfirmNamespacedPolicyPruneBackup(cmd.Context(), prompter); err != nil {
if errors.Is(err, namespacedpolicy.ErrNamespacedPolicyBackupNotConfirmed) {
writeNamespacedPolicyPruneSummary(cmd, plan, false, "aborted")
}
cli.ExitWithError("could not confirm namespaced-policy prune backup", err)
}
}

executor, err := namespacedpolicy.NewExecutor(h)
if err != nil {
cli.ExitWithError("could not create namespaced-policy prune executor", err)
}

if err := executor.ExecutePrune(cmd.Context(), plan); err != nil {
writeNamespacedPolicyPruneSummary(cmd, plan, true, "failure")
cli.ExitWithError("could not execute namespaced-policy prune commit", err)
}
}

func writeNamespacedPolicyPruneSummary(cmd *cobra.Command, plan *namespacedpolicy.PrunePlan, commit bool, result string) {
if _, err := cmd.OutOrStdout().Write([]byte(namespacedpolicy.RenderNamespacedPolicyPruneSummaryWithResult(plan, commit, result) + "\n")); err != nil {
cli.ExitWithError("could not write namespaced-policy prune summary", err)
}
}
7 changes: 7 additions & 0 deletions otdfctl/migrations/namespacedpolicy/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var (
ErrMissingSubjectConditionSetTarget = errors.New("missing subject condition set target")
ErrTargetNamespaceRequired = errors.New("target namespace is required")
ErrMissingCreatedTargetID = errors.New("missing created target id")
ErrMissingPruneSourceID = errors.New("missing prune source id")
ErrUnsupportedStatus = errors.New("unsupported status")
)

Expand All @@ -40,6 +41,12 @@ type ExecutorHandler interface {
CreateRegisteredResource(ctx context.Context, namespace string, name string, values []string, metadata *common.MetadataMutable) (*policy.RegisteredResource, error)
CreateRegisteredResourceValue(ctx context.Context, resourceID string, value string, actionAttributeValues []*registeredresources.ActionAttributeValue, metadata *common.MetadataMutable) (*policy.RegisteredResourceValue, error)
GetRegisteredResource(ctx context.Context, id, name, namespace string) (*policy.RegisteredResource, error)
DeleteAction(ctx context.Context, id string) error
DeleteSubjectConditionSet(ctx context.Context, id string) error
DeleteSubjectMapping(ctx context.Context, id string) (*policy.SubjectMapping, error)
DeleteRegisteredResource(ctx context.Context, id string) error
DeleteRegisteredResourceValue(ctx context.Context, id string) error
DeleteObligationTrigger(ctx context.Context, id string) (*policy.ObligationTrigger, error)
}

type Executor struct {
Expand Down
109 changes: 90 additions & 19 deletions otdfctl/migrations/namespacedpolicy/execute_test_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,38 @@ func wantError(is error, format string, args ...any) *expectedError {
}

type mockExecutorHandler struct {
created map[string]map[string]*createdActionCall
results map[string]map[string]*policy.Action // ! Should be renamed to actionResults
errs map[string]map[string]error
createdSubjectConditions map[string]map[string]*createdSubjectConditionSetCall
subjectConditionSetResult map[string]map[string]*policy.SubjectConditionSet
subjectConditionSetErrs map[string]map[string]error
createdSubjectMappings map[string]map[string]*createdSubjectMappingCall
subjectMappingResults map[string]map[string]*policy.SubjectMapping
subjectMappingErrs map[string]map[string]error
createdObligationTriggers map[string]map[string]*createdObligationTriggerCall
obligationTriggerResult map[string]map[string]*policy.ObligationTrigger
obligationTriggerErrs map[string]map[string]error
createdRegisteredResources map[string]map[string]*createdRegisteredResourceCall
registeredResourceResult map[string]map[string]*policy.RegisteredResource
registeredResourcesByID map[string]*policy.RegisteredResource
registeredResourceErrs map[string]map[string]error
createdRegisteredResourceValues map[string]map[string]*createdRegisteredResourceValueCall
registeredResourceValueResult map[string]map[string]*policy.RegisteredResourceValue
registeredResourceValueErrs map[string]map[string]error
created map[string]map[string]*createdActionCall
results map[string]map[string]*policy.Action // ! Should be renamed to actionResults
errs map[string]map[string]error
createdSubjectConditions map[string]map[string]*createdSubjectConditionSetCall
subjectConditionSetResult map[string]map[string]*policy.SubjectConditionSet
subjectConditionSetErrs map[string]map[string]error
createdSubjectMappings map[string]map[string]*createdSubjectMappingCall
subjectMappingResults map[string]map[string]*policy.SubjectMapping
subjectMappingErrs map[string]map[string]error
createdObligationTriggers map[string]map[string]*createdObligationTriggerCall
obligationTriggerResult map[string]map[string]*policy.ObligationTrigger
obligationTriggerErrs map[string]map[string]error
createdRegisteredResources map[string]map[string]*createdRegisteredResourceCall
registeredResourceResult map[string]map[string]*policy.RegisteredResource
registeredResourcesByID map[string]*policy.RegisteredResource
registeredResourceErrs map[string]map[string]error
createdRegisteredResourceValues map[string]map[string]*createdRegisteredResourceValueCall
registeredResourceValueResult map[string]map[string]*policy.RegisteredResourceValue
registeredResourceValueErrs map[string]map[string]error
deleteCalls []string
deletedActions []string
deleteActionErrs map[string]error
deletedSubjectConditionSets []string
deleteSubjectConditionSetErrs map[string]error
deletedSubjectMappings []string
deleteSubjectMappingErrs map[string]error
deletedRegisteredResources []string
deleteRegisteredResourceErrs map[string]error
deletedRegisteredResourceValues []string
deleteRegisteredResourceValueErrs map[string]error
deletedObligationTriggers []string
deleteObligationTriggerErrs map[string]error
}

type createdActionCall struct {
Expand Down Expand Up @@ -296,3 +309,61 @@ func (m *mockExecutorHandler) CreateRegisteredResourceValue(_ context.Context, r

return nil, errMissingMockRegisteredResourceValue
}

func (m *mockExecutorHandler) DeleteAction(_ context.Context, id string) error {
m.deleteCalls = append(m.deleteCalls, "action:"+id)
m.deletedActions = append(m.deletedActions, id)
if m.deleteActionErrs == nil {
return nil
}
return m.deleteActionErrs[id]
}

func (m *mockExecutorHandler) DeleteSubjectConditionSet(_ context.Context, id string) error {
m.deleteCalls = append(m.deleteCalls, "subject-condition-set:"+id)
m.deletedSubjectConditionSets = append(m.deletedSubjectConditionSets, id)
if m.deleteSubjectConditionSetErrs == nil {
return nil
}
return m.deleteSubjectConditionSetErrs[id]
}

func (m *mockExecutorHandler) DeleteSubjectMapping(_ context.Context, id string) (*policy.SubjectMapping, error) {
m.deleteCalls = append(m.deleteCalls, "subject-mapping:"+id)
m.deletedSubjectMappings = append(m.deletedSubjectMappings, id)
if m.deleteSubjectMappingErrs != nil {
if err := m.deleteSubjectMappingErrs[id]; err != nil {
return nil, err
}
}
return &policy.SubjectMapping{Id: id}, nil
}

func (m *mockExecutorHandler) DeleteRegisteredResource(_ context.Context, id string) error {
m.deleteCalls = append(m.deleteCalls, "registered-resource:"+id)
m.deletedRegisteredResources = append(m.deletedRegisteredResources, id)
if m.deleteRegisteredResourceErrs == nil {
return nil
}
return m.deleteRegisteredResourceErrs[id]
}

func (m *mockExecutorHandler) DeleteRegisteredResourceValue(_ context.Context, id string) error {
m.deleteCalls = append(m.deleteCalls, "registered-resource-value:"+id)
m.deletedRegisteredResourceValues = append(m.deletedRegisteredResourceValues, id)
if m.deleteRegisteredResourceValueErrs == nil {
return nil
}
return m.deleteRegisteredResourceValueErrs[id]
}

func (m *mockExecutorHandler) DeleteObligationTrigger(_ context.Context, id string) (*policy.ObligationTrigger, error) {
m.deleteCalls = append(m.deleteCalls, "obligation-trigger:"+id)
m.deletedObligationTriggers = append(m.deletedObligationTriggers, id)
if m.deleteObligationTriggerErrs != nil {
if err := m.deleteObligationTriggerErrs[id]; err != nil {
return nil, err
}
}
return &policy.ObligationTrigger{Id: id}, nil
}
38 changes: 25 additions & 13 deletions otdfctl/migrations/namespacedpolicy/interactive_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,21 @@ const (
backupAbortDetail = "Choose abort if you have not created a backup yet."
backupConfirmLabel = "Yes, continue"
backupCancelLabel = "Abort"
sourceIDText = "Source ID: "
actionText = "Action: "
actionsText = "Actions: "
resourceText = "Resource: "
targetNamespaceText = "Target namespace: "
attributeValueText = "Attribute value: "
obligationValueText = "Obligation value: "
valuesText = "Values: "
actionBindingsText = "Action bindings: "
subjectSetsTextFmt = "Subject sets: %d"
scsSourceText = "Subject condition set source: "

//nolint:gosec // user-facing backup prompt text, not credentials
pruneBackupWarningTitle = "WARNING: This operation will prune migrated namespaced policy and permanently delete legacy policy objects."
pruneBackupConfirmDetail = "Commit mode will delete legacy/global policy objects from the target system."
sourceIDText = "Source ID: "
actionText = "Action: "
actionsText = "Actions: "
resourceText = "Resource: "
targetNamespaceText = "Target namespace: "
attributeValueText = "Attribute value: "
obligationValueText = "Obligation value: "
valuesText = "Values: "
actionBindingsText = "Action bindings: "
subjectSetsTextFmt = "Subject sets: %d"
scsSourceText = "Subject condition set source: "

createActionDescription = "This will create a new namespaced action."
createSubjectConditionSetDesc = "This will create a new namespaced subject condition set."
Expand All @@ -55,18 +59,26 @@ const (
var ErrNamespacedPolicyBackupNotConfirmed = errors.New("user did not confirm backup")

func ConfirmNamespacedPolicyBackup(ctx context.Context, prompter InteractivePrompter) error {
return confirmNamespacedPolicyBackup(ctx, prompter, backupWarningTitle, backupConfirmDetail)
}

func ConfirmNamespacedPolicyPruneBackup(ctx context.Context, prompter InteractivePrompter) error {
Comment thread
c-r33d marked this conversation as resolved.
return confirmNamespacedPolicyBackup(ctx, prompter, pruneBackupWarningTitle, pruneBackupConfirmDetail)
}

func confirmNamespacedPolicyBackup(ctx context.Context, prompter InteractivePrompter, warningTitle, confirmDetail string) error {
if prompter == nil {
prompter = &HuhPrompter{}
}

styles := migrations.NewDisplayStyles()
fmt.Println(styles.Warning().Render(backupWarningTitle))
fmt.Println(styles.Warning().Render(warningTitle))
fmt.Println(styles.Warning().Render(backupWarningBody))

err := prompter.Confirm(ctx, ConfirmPrompt{
Title: backupConfirmTitle,
Description: []string{
backupConfirmDetail,
confirmDetail,
backupAbortDetail,
},
ConfirmLabel: backupConfirmLabel,
Expand Down
Loading
Loading