...

Source file src/sigs.k8s.io/cli-utils/cmd/preview/cmdpreview.go

Documentation: sigs.k8s.io/cli-utils/cmd/preview

     1  // Copyright 2020 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package preview
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/spf13/cobra"
    13  	"k8s.io/cli-runtime/pkg/genericclioptions"
    14  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    15  	"k8s.io/kubectl/pkg/util/i18n"
    16  	"sigs.k8s.io/cli-utils/cmd/flagutils"
    17  	"sigs.k8s.io/cli-utils/pkg/apply"
    18  	"sigs.k8s.io/cli-utils/pkg/apply/event"
    19  	"sigs.k8s.io/cli-utils/pkg/common"
    20  	"sigs.k8s.io/cli-utils/pkg/inventory"
    21  	"sigs.k8s.io/cli-utils/pkg/manifestreader"
    22  	"sigs.k8s.io/cli-utils/pkg/printers"
    23  )
    24  
    25  var (
    26  	noPrune        = false
    27  	previewDestroy = false
    28  )
    29  
    30  // GetRunner creates and returns the Runner which stores the cobra command.
    31  func GetRunner(factory cmdutil.Factory, invFactory inventory.ClientFactory,
    32  	loader manifestreader.ManifestLoader, ioStreams genericclioptions.IOStreams) *Runner {
    33  	r := &Runner{
    34  		factory:    factory,
    35  		invFactory: invFactory,
    36  		loader:     loader,
    37  		ioStreams:  ioStreams,
    38  	}
    39  	cmd := &cobra.Command{
    40  		Use:                   "preview (DIRECTORY | STDIN)",
    41  		DisableFlagsInUseLine: true,
    42  		Short:                 i18n.T("Preview the apply of a configuration"),
    43  		Args:                  cobra.MaximumNArgs(1),
    44  		RunE:                  r.RunE,
    45  	}
    46  
    47  	cmd.Flags().BoolVar(&noPrune, "no-prune", noPrune, "If true, do not prune previously applied objects.")
    48  	cmd.Flags().BoolVar(&r.serverSideOptions.ServerSideApply, "server-side", false,
    49  		"If true, preview runs in the server instead of the client.")
    50  	cmd.Flags().BoolVar(&r.serverSideOptions.ForceConflicts, "force-conflicts", false,
    51  		"If true during server-side preview, do not report field conflicts.")
    52  	cmd.Flags().StringVar(&r.serverSideOptions.FieldManager, "field-manager", common.DefaultFieldManager,
    53  		"If true during server-side preview, sets field owner.")
    54  	cmd.Flags().BoolVar(&previewDestroy, "destroy", previewDestroy, "If true, preview of destroy operations will be displayed.")
    55  	cmd.Flags().StringVar(&r.output, "output", printers.DefaultPrinter(),
    56  		fmt.Sprintf("Output format, must be one of %s", strings.Join(printers.SupportedPrinters(), ",")))
    57  	cmd.Flags().StringVar(&r.inventoryPolicy, flagutils.InventoryPolicyFlag, flagutils.InventoryPolicyStrict,
    58  		"It determines the behavior when the resources don't belong to current inventory. Available options "+
    59  			fmt.Sprintf("%q, %q and %q.", flagutils.InventoryPolicyStrict, flagutils.InventoryPolicyAdopt, flagutils.InventoryPolicyForceAdopt))
    60  	cmd.Flags().DurationVar(&r.timeout, "timeout", 0,
    61  		"How long to wait before exiting")
    62  
    63  	r.Command = cmd
    64  	return r
    65  }
    66  
    67  // Command creates the Runner, returning the cobra command associated with it.
    68  func Command(f cmdutil.Factory, invFactory inventory.ClientFactory, loader manifestreader.ManifestLoader,
    69  	ioStreams genericclioptions.IOStreams) *cobra.Command {
    70  	return GetRunner(f, invFactory, loader, ioStreams).Command
    71  }
    72  
    73  // Runner encapsulates data necessary to run the preview command.
    74  type Runner struct {
    75  	Command    *cobra.Command
    76  	factory    cmdutil.Factory
    77  	invFactory inventory.ClientFactory
    78  	loader     manifestreader.ManifestLoader
    79  	ioStreams  genericclioptions.IOStreams
    80  
    81  	serverSideOptions common.ServerSideOptions
    82  	output            string
    83  	inventoryPolicy   string
    84  	timeout           time.Duration
    85  }
    86  
    87  // RunE is the function run from the cobra command.
    88  func (r *Runner) RunE(cmd *cobra.Command, args []string) error {
    89  	ctx := cmd.Context()
    90  	// If specified, cancel with timeout.
    91  	if r.timeout != 0 {
    92  		var cancel context.CancelFunc
    93  		ctx, cancel = context.WithTimeout(ctx, r.timeout)
    94  		defer cancel()
    95  	}
    96  
    97  	var ch <-chan event.Event
    98  
    99  	drs := common.DryRunClient
   100  	if r.serverSideOptions.ServerSideApply {
   101  		drs = common.DryRunServer
   102  	}
   103  
   104  	inventoryPolicy, err := flagutils.ConvertInventoryPolicy(r.inventoryPolicy)
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	reader, err := r.loader.ManifestReader(cmd.InOrStdin(), flagutils.PathFromArgs(args))
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	if found := printers.ValidatePrinterType(r.output); !found {
   115  		return fmt.Errorf("unknown output type %q", r.output)
   116  	}
   117  
   118  	objs, err := reader.Read()
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	invObj, objs, err := inventory.SplitUnstructureds(objs)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	inv := inventory.WrapInventoryInfoObj(invObj)
   128  
   129  	invClient, err := r.invFactory.NewClient(r.factory)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	// if destroy flag is set in preview, transmit it to destroyer DryRunStrategy flag
   135  	// and pivot execution to destroy with dry-run
   136  	if !previewDestroy {
   137  		_, err = common.DemandOneDirectory(args)
   138  		if err != nil {
   139  			return err
   140  		}
   141  		a, err := apply.NewApplierBuilder().
   142  			WithFactory(r.factory).
   143  			WithInventoryClient(invClient).
   144  			Build()
   145  		if err != nil {
   146  			return err
   147  		}
   148  
   149  		// Run the applier. It will return a channel where we can receive updates
   150  		// to keep track of progress and any issues.
   151  		ch = a.Run(ctx, inv, objs, apply.ApplierOptions{
   152  			EmitStatusEvents:  false,
   153  			NoPrune:           noPrune,
   154  			DryRunStrategy:    drs,
   155  			ServerSideOptions: r.serverSideOptions,
   156  			InventoryPolicy:   inventoryPolicy,
   157  		})
   158  	} else {
   159  		d, err := apply.NewDestroyerBuilder().
   160  			WithFactory(r.factory).
   161  			WithInventoryClient(invClient).
   162  			Build()
   163  		if err != nil {
   164  			return err
   165  		}
   166  		ch = d.Run(ctx, inv, apply.DestroyerOptions{
   167  			InventoryPolicy: inventoryPolicy,
   168  			DryRunStrategy:  drs,
   169  		})
   170  	}
   171  
   172  	// Print the preview strategy unless the output format is json.
   173  	if r.output != printers.JSONPrinter {
   174  		if drs.ServerDryRun() {
   175  			fmt.Println("Preview strategy: server")
   176  		} else {
   177  			fmt.Println("Preview strategy: client")
   178  		}
   179  	}
   180  
   181  	// The printer will print updates from the channel. It will block
   182  	// until the channel is closed.
   183  	printer := printers.GetPrinter(r.output, r.ioStreams)
   184  	return printer.Print(ch, drs, false) // Do not print status
   185  }
   186  

View as plain text