Skip to content

Commit

Permalink
feat(cli): autoApprove, forceApply (grafana#35)
Browse files Browse the repository at this point in the history
autoApprove: Skip interactive change approval before apply, useful for
automation

forceApply: Force kubectl to apply, ignoring resource ownership
  • Loading branch information
sh0rez authored Aug 12, 2019
1 parent 32bc8a4 commit 626b097
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 17 deletions.
7 changes: 6 additions & 1 deletion cmd/tk/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
}
}
Expand Down
40 changes: 26 additions & 14 deletions pkg/kubernetes/kubectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pkg/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 626b097

Please sign in to comment.