Skip to content
This repository has been archived by the owner on Nov 17, 2021. It is now read-only.

Commit

Permalink
Refactor cmd/ application logic to pkg/
Browse files Browse the repository at this point in the history
This commit will separate the application level logic for the 'show', 'delete',
'validate', and 'diff' commands from the Cobra logic in the cmd/ package.  The
application level logic will be placed in pkg/kubecfg/.
  • Loading branch information
jessicayuen committed Sep 1, 2017
1 parent 2f33558 commit f7d6436
Show file tree
Hide file tree
Showing 14 changed files with 414 additions and 311 deletions.
67 changes: 8 additions & 59 deletions cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,9 @@
package cmd

import (
"fmt"
"sort"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/ksonnet/kubecfg/utils"
"github.com/ksonnet/kubecfg/pkg/kubecfg"
)

const (
Expand All @@ -42,75 +36,30 @@ var deleteCmd = &cobra.Command{
Short: "Delete Kubernetes resources described in local config",
RunE: func(cmd *cobra.Command, args []string) error {
flags := cmd.Flags()
var err error

gracePeriod, err := flags.GetInt64(flagGracePeriod)
if err != nil {
return err
}
c := kubecfg.DeleteCmd{}

files, err := getFiles(cmd, args)
c.GracePeriod, err = flags.GetInt64(flagGracePeriod)
if err != nil {
return err
}

vm, err := newExpander(cmd)
c.ClientPool, c.Discovery, err = restClientPool(cmd)
if err != nil {
return err
}

objs, err := vm.Expand(files)
c.DefaultNamespace, _, err = clientConfig.Namespace()
if err != nil {
return err
}

clientpool, disco, err := restClientPool(cmd)
objs, err := readObjs(cmd, args)
if err != nil {
return err
}

defaultNs, _, err := clientConfig.Namespace()
if err != nil {
return err
}

version, err := utils.FetchVersion(disco)
if err != nil {
return err
}

sort.Sort(sort.Reverse(utils.DependencyOrder(objs)))

deleteOpts := metav1.DeleteOptions{}
if version.Compare(1, 6) < 0 {
// 1.5.x option
boolFalse := false
deleteOpts.OrphanDependents = &boolFalse
} else {
// 1.6.x option (NB: Background is broken)
fg := metav1.DeletePropagationForeground
deleteOpts.PropagationPolicy = &fg
}
if gracePeriod >= 0 {
deleteOpts.GracePeriodSeconds = &gracePeriod
}

for _, obj := range objs {
desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(disco, obj), utils.FqName(obj))
log.Info("Deleting ", desc)

c, err := utils.ClientForResource(clientpool, disco, obj, defaultNs)
if err != nil {
return err
}

err = c.Delete(obj.GetName(), &deleteOpts)
if err != nil && !errors.IsNotFound(err) {
return fmt.Errorf("Error deleting %s: %s", desc, err)
}

log.Debug("Deleted object: ", obj)
}

return nil
return c.Run(objs)
},
}
132 changes: 8 additions & 124 deletions cmd/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,13 @@
package cmd

import (
"fmt"
"io"
"os"
"sort"

"github.com/mattn/go-isatty"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/yudai/gojsondiff"
"github.com/yudai/gojsondiff/formatter"
"k8s.io/apimachinery/pkg/api/errors"

"github.com/ksonnet/kubecfg/utils"
"github.com/ksonnet/kubecfg/pkg/kubecfg"
)

const flagDiffStrategy = "diff-strategy"

var ErrDiffFound = fmt.Errorf("Differences found.")

func init() {
addEnvCmdFlags(diffCmd)
diffCmd.PersistentFlags().String(flagDiffStrategy, "all", "Diff strategy, all or subset.")
Expand All @@ -45,92 +33,32 @@ var diffCmd = &cobra.Command{
Use: "diff [<env>|-f <file-or-dir>]",
Short: "Display differences between server and local config",
RunE: func(cmd *cobra.Command, args []string) error {
out := cmd.OutOrStdout()
flags := cmd.Flags()
diffStrategy, err := flags.GetString(flagDiffStrategy)
if err != nil {
return err
}
var err error

files, err := getFiles(cmd, args)
if err != nil {
return err
}
c := kubecfg.DiffCmd{}

vm, err := newExpander(cmd)
c.DiffStrategy, err = flags.GetString(flagDiffStrategy)
if err != nil {
return err
}

objs, err := vm.Expand(files)
c.ClientPool, c.Discovery, err = restClientPool(cmd)
if err != nil {
return err
}

clientpool, disco, err := restClientPool(cmd)
c.DefaultNamespace, _, err = clientConfig.Namespace()
if err != nil {
return err
}

defaultNs, _, err := clientConfig.Namespace()
objs, err := readObjs(cmd, args)
if err != nil {
return err
}

sort.Sort(utils.AlphabeticalOrder(objs))

diffFound := false
for _, obj := range objs {
desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(disco, obj), utils.FqName(obj))
log.Debug("Fetching ", desc)

c, err := utils.ClientForResource(clientpool, disco, obj, defaultNs)
if err != nil {
return err
}

liveObj, err := c.Get(obj.GetName())
if err != nil && errors.IsNotFound(err) {
log.Debugf("%s doesn't exist on the server", desc)
liveObj = nil
} else if err != nil {
return fmt.Errorf("Error fetching %s: %v", desc, err)
}

fmt.Fprintln(out, "---")
fmt.Fprintf(out, "- live %s\n+ config %s\n", desc, desc)
if liveObj == nil {
fmt.Fprintf(out, "%s doesn't exist on server\n", desc)
diffFound = true
continue
}

liveObjObject := liveObj.Object
if diffStrategy == "subset" {
liveObjObject = removeMapFields(obj.Object, liveObjObject)
}
diff := gojsondiff.New().CompareObjects(liveObjObject, obj.Object)

if diff.Modified() {
diffFound = true
fcfg := formatter.AsciiFormatterConfig{
Coloring: istty(out),
}
formatter := formatter.NewAsciiFormatter(liveObjObject, fcfg)
text, err := formatter.Format(diff)
if err != nil {
return err
}
fmt.Fprintf(out, "%s", text)
} else {
fmt.Fprintf(out, "%s unchanged\n", desc)
}
}

if diffFound {
return ErrDiffFound
}
return nil
return c.Run(objs, cmd.OutOrStdout())
},
Long: `Display differences between server and local configuration.
Expand All @@ -149,47 +77,3 @@ files.`,
# referred to by './kubeconfig'.
ksonnet diff --kubeconfig=./kubeconfig -f ./pod.yaml`,
}

func removeFields(config, live interface{}) interface{} {
switch c := config.(type) {
case map[string]interface{}:
return removeMapFields(c, live.(map[string]interface{}))
case []interface{}:
return removeListFields(c, live.([]interface{}))
default:
return live
}
}

func removeMapFields(config, live map[string]interface{}) map[string]interface{} {
result := map[string]interface{}{}
for k, v1 := range config {
v2, ok := live[k]
if !ok {
continue
}
result[k] = removeFields(v1, v2)
}
return result
}

func removeListFields(config, live []interface{}) []interface{} {
// If live is longer than config, then the extra elements at the end of the
// list will be returned as is so they appear in the diff.
result := make([]interface{}, 0, len(live))
for i, v2 := range live {
if len(config) > i {
result = append(result, removeFields(config[i], v2))
} else {
result = append(result, v2)
}
}
return result
}

func istty(w io.Writer) bool {
if f, ok := w.(*os.File); ok {
return isatty.IsTerminal(f.Fd())
}
return false
}
20 changes: 15 additions & 5 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
"path/filepath"
"strings"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
Expand Down Expand Up @@ -254,10 +256,8 @@ func parseEnvCmd(cmd *cobra.Command, args []string) (*string, []string, error) {
return env, files, nil
}

// TODO: Remove this and use `kubecfg.GetFiles` when we move commands into
// `pkg`.
func getFiles(cmd *cobra.Command, args []string) ([]string, error) {
env, files, err := parseEnvCmd(cmd, args)
func readObjs(cmd *cobra.Command, args []string) ([]*unstructured.Unstructured, error) {
env, f, err := parseEnvCmd(cmd, args)
if err != nil {
return nil, err
}
Expand All @@ -267,5 +267,15 @@ func getFiles(cmd *cobra.Command, args []string) ([]string, error) {
return nil, err
}

return kubecfg.GetFiles(metadata.AbsPath(cwd), env, files)
expander, err := newExpander(cmd)
if err != nil {
return nil, err
}

files, err := kubecfg.GetFiles(metadata.AbsPath(cwd), env, f)
if err != nil {
return nil, err
}

return expander.Expand(files)
}
60 changes: 7 additions & 53 deletions cmd/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@
package cmd

import (
"encoding/json"
"fmt"

"github.com/spf13/cobra"
"gopkg.in/yaml.v2"

"github.com/ksonnet/kubecfg/pkg/kubecfg"
)

const (
Expand All @@ -38,64 +36,20 @@ var showCmd = &cobra.Command{
Short: "Show expanded resource definitions",
RunE: func(cmd *cobra.Command, args []string) error {
flags := cmd.Flags()
out := cmd.OutOrStdout()

files, err := getFiles(cmd, args)
if err != nil {
return err
}
var err error

vm, err := newExpander(cmd)
if err != nil {
return err
}
c := kubecfg.ShowCmd{}

objs, err := vm.Expand(files)
c.Format, err = flags.GetString(flagFormat)
if err != nil {
return err
}

format, err := flags.GetString(flagFormat)
objs, err := readObjs(cmd, args)
if err != nil {
return err
}
switch format {
case "yaml":
for _, obj := range objs {
fmt.Fprintln(out, "---")
// Urgh. Go via json because we need
// to trigger the custom scheme
// encoding.
buf, err := json.Marshal(obj)
if err != nil {
return err
}
o := map[string]interface{}{}
if err := json.Unmarshal(buf, &o); err != nil {
return err
}
buf, err = yaml.Marshal(o)
if err != nil {
return err
}
out.Write(buf)
}
case "json":
enc := json.NewEncoder(out)
enc.SetIndent("", " ")
for _, obj := range objs {
// TODO: this is not valid framing for JSON
if len(objs) > 1 {
fmt.Fprintln(out, "---")
}
if err := enc.Encode(obj); err != nil {
return err
}
}
default:
return fmt.Errorf("Unknown --format: %s", format)
}

return nil
return c.Run(objs, cmd.OutOrStdout())
},
}
Loading

0 comments on commit f7d6436

Please sign in to comment.