mirror of https://github.com/fluxcd/cli-utils.git
170 lines
5.1 KiB
Go
170 lines
5.1 KiB
Go
// Copyright 2020 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package preview
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/spf13/cobra"
|
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
|
"k8s.io/kubectl/pkg/util/i18n"
|
|
"sigs.k8s.io/cli-utils/pkg/apply"
|
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
|
"sigs.k8s.io/cli-utils/pkg/common"
|
|
"sigs.k8s.io/cli-utils/pkg/inventory"
|
|
"sigs.k8s.io/cli-utils/pkg/manifestreader"
|
|
"sigs.k8s.io/cli-utils/pkg/provider"
|
|
"sigs.k8s.io/kustomize/kyaml/setters2"
|
|
)
|
|
|
|
var (
|
|
noPrune = false
|
|
serverDryRun = false
|
|
previewDestroy = false
|
|
)
|
|
|
|
// GetPreviewRunner creates and returns the PreviewRunner which stores the cobra command.
|
|
func GetPreviewRunner(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *PreviewRunner {
|
|
provider := provider.NewProvider(f, inventory.WrapInventoryObj)
|
|
r := &PreviewRunner{
|
|
Applier: apply.NewApplier(provider, ioStreams),
|
|
Destroyer: apply.NewDestroyer(provider, ioStreams),
|
|
ioStreams: ioStreams,
|
|
provider: provider,
|
|
}
|
|
cmd := &cobra.Command{
|
|
Use: "preview (DIRECTORY | STDIN)",
|
|
DisableFlagsInUseLine: true,
|
|
Short: i18n.T("Preview the apply of a configuration"),
|
|
Args: cobra.MaximumNArgs(1),
|
|
RunE: r.RunE,
|
|
}
|
|
|
|
r.Applier.SetFlags(cmd)
|
|
|
|
cmd.Flags().BoolVar(&noPrune, "no-prune", noPrune, "If true, do not prune previously applied objects.")
|
|
cmd.Flags().BoolVar(&serverDryRun, "server-side", serverDryRun, "If true, preview runs in the server instead of the client.")
|
|
|
|
// The following flags are added, but hidden because other code
|
|
// dependend on them when parsing flags. These flags are hidden and unused.
|
|
var unusedBool bool
|
|
cmd.Flags().BoolVar(&unusedBool, "dry-run", unusedBool, "NOT USED")
|
|
cmd.Flags().BoolVar(&previewDestroy, "destroy", previewDestroy, "If true, preview of destroy operations will be displayed.")
|
|
_ = cmd.Flags().MarkHidden("dry-run")
|
|
cmdutil.AddValidateFlags(cmd)
|
|
_ = cmd.Flags().MarkHidden("validate")
|
|
cmd.Flags().Bool("force-conflicts", false, "If true, server-side apply will force the changes against conflicts.")
|
|
cmd.Flags().String("field-manager", "kubectl", "Name of the manager used to track field ownership.")
|
|
// hide unwanted server-side flags
|
|
_ = cmd.Flags().MarkHidden("force-conflicts")
|
|
_ = cmd.Flags().MarkHidden("field-manager")
|
|
|
|
r.Command = cmd
|
|
return r
|
|
}
|
|
|
|
// PreviewCommand creates the PreviewRunner, returning the cobra command associated with it.
|
|
func PreviewCommand(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
|
|
return GetPreviewRunner(f, ioStreams).Command
|
|
}
|
|
|
|
// PreviewRunner encapsulates data necessary to run the preview command.
|
|
type PreviewRunner struct {
|
|
Command *cobra.Command
|
|
ioStreams genericclioptions.IOStreams
|
|
Applier *apply.Applier
|
|
Destroyer *apply.Destroyer
|
|
provider provider.Provider
|
|
}
|
|
|
|
// RunE is the function run from the cobra command.
|
|
func (r *PreviewRunner) RunE(cmd *cobra.Command, args []string) error {
|
|
err := setters2.CheckRequiredSettersSet()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var ch <-chan event.Event
|
|
err = r.Destroyer.Initialize(cmd, args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
drs := common.DryRunClient
|
|
if serverDryRun {
|
|
drs = common.DryRunServer
|
|
}
|
|
|
|
if previewDestroy {
|
|
r.Destroyer.DryRunStrategy = drs
|
|
}
|
|
|
|
// if destroy flag is set in preview, transmit it to destroyer DryRunStrategy flag
|
|
// and pivot execution to destroy with dry-run
|
|
if !r.Destroyer.DryRunStrategy.ClientOrServerDryRun() {
|
|
err = r.Applier.Initialize(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create a context
|
|
ctx := context.Background()
|
|
|
|
_, err := common.DemandOneDirectory(args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Fetch the namespace from the configloader. The source of this
|
|
// either the namespace flag or the context. If the namespace is provided
|
|
// with the flag, enforceNamespace will be true. In this case, it is
|
|
// an error if any of the resources in the package has a different
|
|
// namespace set.
|
|
namespace, enforceNamespace, err := r.provider.Factory().ToRawKubeConfigLoader().Namespace()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var reader manifestreader.ManifestReader
|
|
readerOptions := manifestreader.ReaderOptions{
|
|
Factory: r.provider.Factory(),
|
|
Namespace: namespace,
|
|
EnforceNamespace: enforceNamespace,
|
|
}
|
|
if len(args) == 0 {
|
|
reader = &manifestreader.StreamManifestReader{
|
|
ReaderName: "stdin",
|
|
Reader: cmd.InOrStdin(),
|
|
ReaderOptions: readerOptions,
|
|
}
|
|
} else {
|
|
reader = &manifestreader.PathManifestReader{
|
|
Path: args[0],
|
|
ReaderOptions: readerOptions,
|
|
}
|
|
}
|
|
infos, err := reader.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Run the applier. It will return a channel where we can receive updates
|
|
// to keep track of progress and any issues.
|
|
ch = r.Applier.Run(ctx, infos, apply.Options{
|
|
EmitStatusEvents: false,
|
|
NoPrune: noPrune,
|
|
DryRunStrategy: drs,
|
|
})
|
|
} else {
|
|
ch = r.Destroyer.Run()
|
|
}
|
|
|
|
// The printer will print updates from the channel. It will block
|
|
// until the channel is closed.
|
|
printer := &apply.BasicPrinter{
|
|
IOStreams: r.ioStreams,
|
|
}
|
|
return printer.Print(ch, drs)
|
|
}
|