diff --git a/cmd/tk/workflow.go b/cmd/tk/workflow.go index 8e46d8b5d..e1db49385 100644 --- a/cmd/tk/workflow.go +++ b/cmd/tk/workflow.go @@ -33,6 +33,8 @@ func applyCmd() *cobra.Command { }, } vars := workflowFlags(cmd.Flags()) + force := cmd.Flags().Bool("force", false, "force applying (kubectl apply --force)") + autoApprove := cmd.Flags().Bool("dangerous-auto-approve", false, "skip interactive approval. Only for automation!") cmd.Run = func(cmd *cobra.Command, args []string) { raw, err := evalDict(args[0]) if err != nil { @@ -48,7 +50,10 @@ func applyCmd() *cobra.Command { log.Println("Warning: There are no differences. Your apply may not do anything at all.") } - if err := kube.Apply(desired); err != nil { + if err := kube.Apply(desired, kubernetes.ApplyOpts{ + Force: *force, + AutoApprove: *autoApprove, + }); err != nil { log.Fatalln("Applying:", err) } } diff --git a/pkg/kubernetes/kubectl.go b/pkg/kubernetes/kubectl.go index a02643e4f..16c690e8f 100644 --- a/pkg/kubernetes/kubectl.go +++ b/pkg/kubernetes/kubectl.go @@ -127,32 +127,44 @@ func (k Kubectl) Get(namespace, kind, name string) (map[string]interface{}, erro return obj, nil } +// ApplyOpts allow to specify additional parameter for apply operations +type ApplyOpts struct { + Force bool + AutoApprove bool +} + // Apply applies the given yaml to the cluster -func (k Kubectl) Apply(yaml, namespace string) error { +func (k Kubectl) Apply(yaml, namespace string, opts ApplyOpts) error { if err := k.setupContext(); err != nil { return err } - reader := bufio.NewReader(os.Stdin) - fmt.Printf(`Applying to namespace '%s' of cluster '%s' at '%s' using context '%s'. + if !opts.AutoApprove { + reader := bufio.NewReader(os.Stdin) + fmt.Printf(`Applying to namespace '%s' of cluster '%s' at '%s' using context '%s'. Please type 'yes' to perform: `, - alert(namespace), - alert(k.cluster.Get("name").MustStr()), - alert(k.cluster.Get("cluster.server").MustStr()), - alert(k.context.Get("name").MustStr()), - ) - approve, err := reader.ReadString('\n') - if err != nil { - return err - } - if approve != "yes\n" { - return errors.New("aborted by user") + alert(namespace), + alert(k.cluster.Get("name").MustStr()), + alert(k.cluster.Get("cluster.server").MustStr()), + alert(k.context.Get("name").MustStr()), + ) + approve, err := reader.ReadString('\n') + if err != nil { + return err + } + if approve != "yes\n" { + return errors.New("aborted by user") + } } argv := []string{"apply", "--context", k.context.Get("name").MustStr(), "-f", "-", } + if !opts.Force { + argv = append(argv, "--force") + } + cmd := exec.Command("kubectl", argv...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/pkg/kubernetes/kubernetes.go b/pkg/kubernetes/kubernetes.go index 0854a8e4b..9da96b303 100644 --- a/pkg/kubernetes/kubernetes.go +++ b/pkg/kubernetes/kubernetes.go @@ -105,12 +105,12 @@ func (k *Kubernetes) Fmt(state []Manifest) (string, error) { } // Apply receives a state object generated using `Reconcile()` and may apply it to the target system -func (k *Kubernetes) Apply(state []Manifest) error { +func (k *Kubernetes) Apply(state []Manifest, opts ApplyOpts) error { yaml, err := k.Fmt(state) if err != nil { return err } - return k.client.Apply(yaml, k.Spec.Namespace) + return k.client.Apply(yaml, k.Spec.Namespace, opts) } // Diff takes the desired state and returns the differences from the cluster