github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/commands/live/destroy/cmddestroy.go (about) 1 // Copyright 2021 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package destroy 16 17 import ( 18 "context" 19 "fmt" 20 "os" 21 22 "github.com/GoogleContainerTools/kpt/internal/docs/generated/livedocs" 23 "github.com/GoogleContainerTools/kpt/internal/util/argutil" 24 "github.com/GoogleContainerTools/kpt/internal/util/strings" 25 "github.com/GoogleContainerTools/kpt/pkg/live" 26 "github.com/GoogleContainerTools/kpt/pkg/status" 27 "github.com/spf13/cobra" 28 "k8s.io/cli-runtime/pkg/genericclioptions" 29 "k8s.io/kubectl/pkg/cmd/util" 30 "sigs.k8s.io/cli-utils/cmd/flagutils" 31 "sigs.k8s.io/cli-utils/pkg/apply" 32 "sigs.k8s.io/cli-utils/pkg/common" 33 "sigs.k8s.io/cli-utils/pkg/inventory" 34 "sigs.k8s.io/cli-utils/pkg/printers" 35 ) 36 37 func NewRunner( 38 ctx context.Context, 39 factory util.Factory, 40 ioStreams genericclioptions.IOStreams, 41 ) *Runner { 42 r := &Runner{ 43 ctx: ctx, 44 ioStreams: ioStreams, 45 factory: factory, 46 destroyRunner: runDestroy, 47 } 48 c := &cobra.Command{ 49 Use: "destroy [PKG_PATH | -]", 50 RunE: r.runE, 51 PreRunE: r.preRunE, 52 Short: livedocs.DestroyShort, 53 Long: livedocs.DestroyShort + "\n" + livedocs.DestroyLong, 54 Example: livedocs.DestroyExamples, 55 } 56 r.Command = c 57 58 c.Flags().StringVar(&r.output, "output", printers.DefaultPrinter(), 59 fmt.Sprintf("Output format, must be one of %s", strings.JoinStringsWithQuotes(printers.SupportedPrinters()))) 60 c.Flags().StringVar(&r.inventoryPolicyString, flagutils.InventoryPolicyFlag, flagutils.InventoryPolicyStrict, 61 "It determines the behavior when the resources don't belong to current inventory. Available options "+ 62 fmt.Sprintf("%q and %q.", flagutils.InventoryPolicyStrict, flagutils.InventoryPolicyAdopt)) 63 c.Flags().BoolVar(&r.dryRun, "dry-run", false, 64 "dry-run apply for the resources in the package.") 65 c.Flags().BoolVar(&r.printStatusEvents, "show-status-events", false, 66 "Print status events (always enabled for table output)") 67 return r 68 } 69 70 // NewCommand returns a cobra command. 71 func NewCommand(ctx context.Context, factory util.Factory, 72 ioStreams genericclioptions.IOStreams) *cobra.Command { 73 return NewRunner(ctx, factory, ioStreams).Command 74 } 75 76 // Runner contains the run function that contains the cli functionality for the 77 // destroy command. 78 type Runner struct { 79 ctx context.Context 80 Command *cobra.Command 81 PreProcess func(info inventory.Info, strategy common.DryRunStrategy) (inventory.Policy, error) 82 ioStreams genericclioptions.IOStreams 83 factory util.Factory 84 85 output string 86 inventoryPolicyString string 87 dryRun bool 88 printStatusEvents bool 89 90 inventoryPolicy inventory.Policy 91 92 // TODO(mortent): This is needed for now since we don't have a good way to 93 // stub out the Destroyer with an interface for testing purposes. 94 destroyRunner func(r *Runner, inv inventory.Info, strategy common.DryRunStrategy) error 95 } 96 97 // preRunE validates the inventoryPolicy and the output type. 98 func (r *Runner) preRunE(_ *cobra.Command, _ []string) error { 99 var err error 100 r.inventoryPolicy, err = flagutils.ConvertInventoryPolicy(r.inventoryPolicyString) 101 if err != nil { 102 return err 103 } 104 105 if found := printers.ValidatePrinterType(r.output); !found { 106 return fmt.Errorf("unknown output type %q", r.output) 107 } 108 109 return nil 110 } 111 112 // runE handles the input flags and args, sets up the Destroyer, and 113 // invokes the 114 func (r *Runner) runE(c *cobra.Command, args []string) error { 115 if len(args) == 0 { 116 // default to the current working directory 117 cwd, err := os.Getwd() 118 if err != nil { 119 return err 120 } 121 args = append(args, cwd) 122 } 123 124 path := args[0] 125 var err error 126 if args[0] != "-" { 127 path, err = argutil.ResolveSymlink(r.ctx, path) 128 if err != nil { 129 return err 130 } 131 } 132 133 _, inv, err := live.Load(r.factory, path, c.InOrStdin()) 134 if err != nil { 135 return err 136 } 137 138 invInfo, err := live.ToInventoryInfo(inv) 139 if err != nil { 140 return err 141 } 142 143 dryRunStrategy := common.DryRunNone 144 if r.dryRun { 145 dryRunStrategy = common.DryRunClient 146 } 147 148 // TODO(mortent): Figure out if we can do this differently. 149 if r.PreProcess != nil { 150 r.inventoryPolicy, err = r.PreProcess(invInfo, dryRunStrategy) 151 if err != nil { 152 return err 153 } 154 } 155 156 return r.destroyRunner(r, invInfo, dryRunStrategy) 157 } 158 159 func runDestroy(r *Runner, inv inventory.Info, dryRunStrategy common.DryRunStrategy) error { 160 // Run the destroyer. It will return a channel where we can receive updates 161 // to keep track of progress and any issues. 162 invClient, err := inventory.NewClient(r.factory, live.WrapInventoryObj, live.InvToUnstructuredFunc, inventory.StatusPolicyAll, live.ResourceGroupGVK) 163 if err != nil { 164 return err 165 } 166 167 statusWatcher, err := status.NewStatusWatcher(r.factory) 168 if err != nil { 169 return err 170 } 171 172 destroyer, err := apply.NewDestroyerBuilder(). 173 WithFactory(r.factory). 174 WithInventoryClient(invClient). 175 WithStatusWatcher(statusWatcher). 176 Build() 177 if err != nil { 178 return err 179 } 180 181 options := apply.DestroyerOptions{ 182 InventoryPolicy: r.inventoryPolicy, 183 DryRunStrategy: dryRunStrategy, 184 EmitStatusEvents: true, 185 } 186 ch := destroyer.Run(context.Background(), inv, options) 187 188 // Print the preview strategy unless the output format is json. 189 if dryRunStrategy.ClientOrServerDryRun() && r.output != printers.JSONPrinter { 190 if dryRunStrategy.ServerDryRun() { 191 fmt.Println("Dry-run strategy: server") 192 } else { 193 fmt.Println("Dry-run strategy: client") 194 } 195 } 196 // The printer will print updates from the channel. It will block 197 // until the channel is closed. 198 printer := printers.GetPrinter(r.output, r.ioStreams) 199 return printer.Print(ch, dryRunStrategy, r.printStatusEvents) 200 }