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  }