Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement tk delete functionality. #313

Merged
merged 4 commits into from
Jul 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/tk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func main() {
showCmd(),
diffCmd(),
pruneCmd(),
deleteCmd(),
)

rootCmd.AddCommand(
Expand Down
29 changes: 29 additions & 0 deletions cmd/tk/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,35 @@ func pruneCmd() *cli.Command {
return cmd
}

func deleteCmd() *cli.Command {
cmd := &cli.Command{
Use: "delete <path>",
Short: "delete the environment from cluster",
Args: workflowArgs,
}

vars := workflowFlags(cmd.Flags())
force := cmd.Flags().Bool("force", false, "force deleting (kubectl delete --force)")
validate := cmd.Flags().Bool("validate", true, "validation of resources (kubectl --validate=false)")
autoApprove := cmd.Flags().Bool("dangerous-auto-approve", false, "skip interactive approval. Only for automation!")
getExtCode := extCodeParser(cmd.Flags())

cmd.Run = func(cmd *cli.Command, args []string) error {
err := tanka.Delete(args[0],
tanka.WithTargets(stringsToRegexps(vars.targets)),
tanka.WithExtCode(getExtCode()),
tanka.WithApplyForce(*force),
tanka.WithApplyValidate(*validate),
tanka.WithApplyAutoApprove(*autoApprove),
)
if err != nil {
return err
}
return nil
}
return cmd
}

func diffCmd() *cli.Command {
cmd := &cli.Command{
Use: "diff <path>",
Expand Down
11 changes: 10 additions & 1 deletion pkg/kubernetes/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,20 @@ package kubernetes
import (
"github.com/grafana/tanka/pkg/kubernetes/client"
"github.com/grafana/tanka/pkg/kubernetes/manifest"
"github.com/grafana/tanka/pkg/process"
)

type DeleteOpts client.DeleteOpts

func (k *Kubernetes) Delete(state manifest.List, opts DeleteOpts) error {
func (k *Kubernetes) Delete(state manifest.List, opts ApplyOpts) error {
// Sort and reverse the manifests to avoid cascading deletions
process.Sort(state)
for i := 0; i < len(state)/2; i++ {
t := state[i]
state[i] = state[len(state)-1-i]
state[len(state)-1-i] = t
}
Comment on lines +14 to +18
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is recommended in SliceTricks, I just discovered there is sort.Reverse which can be used as long as the type implements sort.Interface.

We could consider whether it's worth moving most of the sort logic from process to manifest, and have manifest.List implement the interface, so that you could sort.Sort(manifest.List{}) and also sort.Reverse

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a look at sort.Reverse and it looked like a lot of bureaucracy. If we migrated from sort.SliceStable, it might be a bit more worthwhile, but I'm not sure if it's that intuitive in this case; e.g. the sorting criterion is non-trivial. I'd give it a go in a separate PR, since this is an internal cleanup, not a user-visible feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah thought about doing that. The slice would then know how to sort itself, allowing us to use sort.Stable and sort.Reverse on it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do that, but I agree it's minor internal cleanup


for _, m := range state {
if err := k.ctl.Delete(m.Metadata().Namespace(), m.Kind(), m.Metadata().Name(), client.DeleteOpts(opts)); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion pkg/tanka/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ func Prune(baseDir string, mods ...Modifier) error {
}

// delete resources
return kube.Delete(orphaned, kubernetes.DeleteOpts(opts.apply))
return kube.Delete(orphaned, opts.apply)
}
1 change: 1 addition & 0 deletions pkg/tanka/tanka.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type options struct {

// additional options for diff
diff kubernetes.DiffOpts

// additional options for apply
apply kubernetes.ApplyOpts
}
Expand Down
39 changes: 39 additions & 0 deletions pkg/tanka/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,45 @@ func Diff(baseDir string, mods ...Modifier) (*string, error) {
return kube.Diff(l.Resources, opts.diff)
}

// Delete parses the environment at the given directory (a `baseDir`) and deletes
// the generated objects from the Kubernetes cluster defined in the environment's
// `spec.json`.
func Delete(baseDir string, mods ...Modifier) error {
opts := parseModifiers(mods)

l, err := load(baseDir, opts)
if err != nil {
return err
}
kube, err := l.connect()
if err != nil {
return err
}
defer kube.Close()

// show diff
// static differ will never fail and always return something if input is not nil
diff, err := kubernetes.StaticDiffer(false)(l.Resources)
mplzik marked this conversation as resolved.
Show resolved Hide resolved

if err != nil {
fmt.Println("Error diffing:", err)
}

// in case of non-fatal error diff may be nil
if diff != nil {
b := term.Colordiff(*diff)
fmt.Print(b.String())
}

// prompt for confirmation
if opts.apply.AutoApprove {
} else if err := confirmPrompt("Deleting from", l.Env.Spec.Namespace, kube.Info()); err != nil {
return err
}

return kube.Delete(l.Resources, opts.apply)
}

// Show parses the environment at the given directory (a `baseDir`) and returns
// the list of Kubernetes objects.
// Tip: use the `String()` function on the returned list to get the familiar yaml stream
Expand Down