...

Source file src/sigs.k8s.io/cli-utils/cmd/apply/cmdapply.go

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

     1  // Copyright 2020 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package apply
     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/common"
    19  	"sigs.k8s.io/cli-utils/pkg/inventory"
    20  	"sigs.k8s.io/cli-utils/pkg/manifestreader"
    21  	"sigs.k8s.io/cli-utils/pkg/printers"
    22  )
    23  
    24  func GetRunner(factory cmdutil.Factory, invFactory inventory.ClientFactory,
    25  	loader manifestreader.ManifestLoader, ioStreams genericclioptions.IOStreams) *Runner {
    26  	r := &Runner{
    27  		ioStreams:  ioStreams,
    28  		factory:    factory,
    29  		invFactory: invFactory,
    30  		loader:     loader,
    31  	}
    32  	cmd := &cobra.Command{
    33  		Use:                   "apply (DIRECTORY | STDIN)",
    34  		DisableFlagsInUseLine: true,
    35  		Short:                 i18n.T("Apply a configuration to a resource by package directory or stdin"),
    36  		RunE:                  r.RunE,
    37  	}
    38  
    39  	cmd.Flags().BoolVar(&r.serverSideOptions.ServerSideApply, "server-side", false,
    40  		"If true, apply merge patch is calculated on API server instead of client.")
    41  	cmd.Flags().BoolVar(&r.serverSideOptions.ForceConflicts, "force-conflicts", false,
    42  		"If true, overwrite applied fields on server if field manager conflict.")
    43  	cmd.Flags().StringVar(&r.serverSideOptions.FieldManager, "field-manager", common.DefaultFieldManager,
    44  		"The client owner of the fields being applied on the server-side.")
    45  
    46  	cmd.Flags().StringVar(&r.output, "output", printers.DefaultPrinter(),
    47  		fmt.Sprintf("Output format, must be one of %s", strings.Join(printers.SupportedPrinters(), ",")))
    48  	cmd.Flags().DurationVar(&r.reconcileTimeout, "reconcile-timeout", time.Duration(0),
    49  		"Timeout threshold for waiting for all resources to reach the Current status.")
    50  	cmd.Flags().BoolVar(&r.noPrune, "no-prune", r.noPrune,
    51  		"If true, do not prune previously applied objects.")
    52  	cmd.Flags().StringVar(&r.prunePropagationPolicy, "prune-propagation-policy",
    53  		"Background", "Propagation policy for pruning")
    54  	cmd.Flags().DurationVar(&r.pruneTimeout, "prune-timeout", time.Duration(0),
    55  		"Timeout threshold for waiting for all pruned resources to be deleted")
    56  	cmd.Flags().StringVar(&r.inventoryPolicy, flagutils.InventoryPolicyFlag, flagutils.InventoryPolicyStrict,
    57  		"It determines the behavior when the resources don't belong to current inventory. Available options "+
    58  			fmt.Sprintf("%q, %q and %q.", flagutils.InventoryPolicyStrict, flagutils.InventoryPolicyAdopt, flagutils.InventoryPolicyForceAdopt))
    59  	cmd.Flags().DurationVar(&r.timeout, "timeout", 0,
    60  		"How long to wait before exiting")
    61  	cmd.Flags().BoolVar(&r.printStatusEvents, "status-events", false,
    62  		"Print status events (always enabled for table output)")
    63  
    64  	r.Command = cmd
    65  	return r
    66  }
    67  
    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  type Runner struct {
    74  	Command    *cobra.Command
    75  	ioStreams  genericclioptions.IOStreams
    76  	factory    cmdutil.Factory
    77  	invFactory inventory.ClientFactory
    78  	loader     manifestreader.ManifestLoader
    79  
    80  	serverSideOptions      common.ServerSideOptions
    81  	output                 string
    82  	reconcileTimeout       time.Duration
    83  	noPrune                bool
    84  	prunePropagationPolicy string
    85  	pruneTimeout           time.Duration
    86  	inventoryPolicy        string
    87  	timeout                time.Duration
    88  	printStatusEvents      bool
    89  }
    90  
    91  func (r *Runner) RunE(cmd *cobra.Command, args []string) error {
    92  	ctx := cmd.Context()
    93  	// If specified, cancel with timeout.
    94  	if r.timeout != 0 {
    95  		var cancel context.CancelFunc
    96  		ctx, cancel = context.WithTimeout(ctx, r.timeout)
    97  		defer cancel()
    98  	}
    99  
   100  	prunePropPolicy, err := flagutils.ConvertPropagationPolicy(r.prunePropagationPolicy)
   101  	if err != nil {
   102  		return err
   103  	}
   104  	inventoryPolicy, err := flagutils.ConvertInventoryPolicy(r.inventoryPolicy)
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	if found := printers.ValidatePrinterType(r.output); !found {
   110  		return fmt.Errorf("unknown output type %q", r.output)
   111  	}
   112  
   113  	// TODO: Fix DemandOneDirectory to no longer return FileNameFlags
   114  	// since we are no longer using them.
   115  	_, err = common.DemandOneDirectory(args)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	reader, err := r.loader.ManifestReader(cmd.InOrStdin(), flagutils.PathFromArgs(args))
   120  	if err != nil {
   121  		return err
   122  	}
   123  	objs, err := reader.Read()
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	invObj, objs, err := inventory.SplitUnstructureds(objs)
   129  	if err != nil {
   130  		return err
   131  	}
   132  	inv := inventory.WrapInventoryInfoObj(invObj)
   133  
   134  	invClient, err := r.invFactory.NewClient(r.factory)
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	// Run the applier. It will return a channel where we can receive updates
   140  	// to keep track of progress and any issues.
   141  	a, err := apply.NewApplierBuilder().
   142  		WithFactory(r.factory).
   143  		WithInventoryClient(invClient).
   144  		Build()
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	// Always enable status events for the table printer
   150  	if r.output == printers.TablePrinter {
   151  		r.printStatusEvents = true
   152  	}
   153  
   154  	ch := a.Run(ctx, inv, objs, apply.ApplierOptions{
   155  		ServerSideOptions: r.serverSideOptions,
   156  		ReconcileTimeout:  r.reconcileTimeout,
   157  		// If we are not waiting for status, tell the applier to not
   158  		// emit the events.
   159  		EmitStatusEvents:       r.printStatusEvents,
   160  		NoPrune:                r.noPrune,
   161  		DryRunStrategy:         common.DryRunNone,
   162  		PrunePropagationPolicy: prunePropPolicy,
   163  		PruneTimeout:           r.pruneTimeout,
   164  		InventoryPolicy:        inventoryPolicy,
   165  	})
   166  
   167  	// The printer will print updates from the channel. It will block
   168  	// until the channel is closed.
   169  	printer := printers.GetPrinter(r.output, r.ioStreams)
   170  	return printer.Print(ch, common.DryRunNone, r.printStatusEvents)
   171  }
   172  

View as plain text