Skip to content
Snippets Groups Projects
Commit 3d5b174a authored by Alex Clemmer's avatar Alex Clemmer Committed by GitHub
Browse files

Merge pull request #93 from ksonnet/jyuen/cmd-to-pkg

Refactor cmd/ application logic to pkg/
parents 2f335586 f7d6436d
No related branches found
No related tags found
No related merge requests found
......@@ -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 (
......@@ -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)
},
}
......@@ -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.")
......@@ -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.
......@@ -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
}
......@@ -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"
......@@ -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
}
......@@ -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)
}
......@@ -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 (
......@@ -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())
},
}
......@@ -66,11 +66,6 @@ local configuration. Accepts JSON, YAML, or Jsonnet.`,
c := kubecfg.UpdateCmd{}
c.Environment, c.Files, err = parseEnvCmd(cmd, args)
if err != nil {
return err
}
c.Create, err = flags.GetBool(flagCreate)
if err != nil {
return err
......@@ -101,17 +96,17 @@ local configuration. Accepts JSON, YAML, or Jsonnet.`,
return err
}
c.Expander, err = newExpander(cmd)
cwd, err := os.Getwd()
if err != nil {
return err
}
cwd, err := os.Getwd()
objs, err := readObjs(cmd, args)
if err != nil {
return err
}
return c.Run(metadata.AbsPath(cwd))
return c.Run(objs, metadata.AbsPath(cwd))
},
Long: `Update (or optionally create) Kubernetes resources on the cluster using the
local configuration. Use the '--create' flag to control whether we create them
......
......@@ -16,12 +16,9 @@
package cmd
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/ksonnet/kubecfg/utils"
"github.com/ksonnet/kubecfg/pkg/kubecfg"
)
func init() {
......@@ -33,53 +30,21 @@ var validateCmd = &cobra.Command{
Use: "validate [<env>|-f <file-or-dir>]",
Short: "Compare generated manifest against server OpenAPI spec",
RunE: func(cmd *cobra.Command, args []string) error {
files, err := getFiles(cmd, args)
if err != nil {
return err
}
var err error
vm, err := newExpander(cmd)
if err != nil {
return err
}
c := kubecfg.ValidateCmd{}
objs, err := vm.Expand(files)
_, c.Discovery, err = restClientPool(cmd)
if err != nil {
return err
}
_, disco, err := restClientPool(cmd)
objs, err := readObjs(cmd, args)
if err != nil {
return err
}
hasError := false
for _, obj := range objs {
desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(disco, obj), utils.FqName(obj))
log.Info("Validating ", desc)
var allErrs []error
schema, err := utils.NewSwaggerSchemaFor(disco, obj.GroupVersionKind().GroupVersion())
if err != nil {
allErrs = append(allErrs, fmt.Errorf("Unable to fetch schema: %v", err))
} else {
// Validate obj
allErrs = append(allErrs, schema.Validate(obj)...)
}
for _, err := range allErrs {
log.Errorf("Error in %s: %v", desc, err)
hasError = true
}
}
if hasError {
return fmt.Errorf("Validation failed")
}
return nil
return c.Run(objs, cmd.OutOrStdout())
},
Long: `Validate that an application or file is compliant with the Kubernetes
specification.
......
......@@ -21,6 +21,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/ksonnet/kubecfg/cmd"
"github.com/ksonnet/kubecfg/pkg/kubecfg"
)
// Version is overridden using `-X main.version` during release builds
......@@ -37,7 +38,7 @@ func main() {
log.Error(err.Error())
switch err {
case cmd.ErrDiffFound:
case kubecfg.ErrDiffFound:
os.Exit(10)
default:
os.Exit(1)
......
......@@ -6,7 +6,6 @@ import (
"github.com/ksonnet/kubecfg/metadata"
)
// TODO: Make this private when we move more commands into `pkg`.
func GetFiles(wd metadata.AbsPath, env *string, files []string) ([]string, error) {
envPresent := env != nil
filesPresent := len(files) > 0
......
// Copyright 2017 The kubecfg authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kubecfg
import (
"fmt"
"sort"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"github.com/ksonnet/kubecfg/utils"
)
// DeleteCmd represents the delete subcommand
type DeleteCmd struct {
ClientPool dynamic.ClientPool
Discovery discovery.DiscoveryInterface
DefaultNamespace string
GracePeriod int64
}
func (c DeleteCmd) Run(apiObjects []*unstructured.Unstructured) error {
version, err := utils.FetchVersion(c.Discovery)
if err != nil {
return err
}
sort.Sort(sort.Reverse(utils.DependencyOrder(apiObjects)))
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 c.GracePeriod >= 0 {
deleteOpts.GracePeriodSeconds = &c.GracePeriod
}
for _, obj := range apiObjects {
desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(c.Discovery, obj), utils.FqName(obj))
log.Info("Deleting ", desc)
client, err := utils.ClientForResource(c.ClientPool, c.Discovery, obj, c.DefaultNamespace)
if err != nil {
return err
}
err = client.Delete(obj.GetName(), &deleteOpts)
if err != nil && !errors.IsNotFound(err) {
return fmt.Errorf("Error deleting %s: %s", desc, err)
}
log.Debugf("Deleted object: ", obj)
}
return nil
}
// Copyright 2017 The kubecfg authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kubecfg
import (
"fmt"
"io"
"os"
"sort"
isatty "github.com/mattn/go-isatty"
log "github.com/sirupsen/logrus"
"github.com/yudai/gojsondiff"
"github.com/yudai/gojsondiff/formatter"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"github.com/ksonnet/kubecfg/utils"
)
var ErrDiffFound = fmt.Errorf("Differences found.")
// DiffCmd represents the diff subcommand
type DiffCmd struct {
ClientPool dynamic.ClientPool
Discovery discovery.DiscoveryInterface
DefaultNamespace string
DiffStrategy string
}
func (c DiffCmd) Run(apiObjects []*unstructured.Unstructured, out io.Writer) error {
sort.Sort(utils.AlphabeticalOrder(apiObjects))
diffFound := false
for _, obj := range apiObjects {
desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(c.Discovery, obj), utils.FqName(obj))
log.Debugf("Fetching ", desc)
client, err := utils.ClientForResource(c.ClientPool, c.Discovery, obj, c.DefaultNamespace)
if err != nil {
return err
}
liveObj, err := client.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 c.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
}
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
}
......@@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
package kubecfg
import (
"testing"
......
// Copyright 2017 The kubecfg authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kubecfg
import (
"encoding/json"
"fmt"
"io"
yaml "gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// ShowCmd represents the show subcommand
type ShowCmd struct {
Format string
}
func (c ShowCmd) Run(apiObjects []*unstructured.Unstructured, out io.Writer) error {
switch c.Format {
case "yaml":
for _, obj := range apiObjects {
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 apiObjects {
// TODO: this is not valid framing for JSON
if len(apiObjects) > 1 {
fmt.Fprintln(out, "---")
}
if err := enc.Encode(obj); err != nil {
return err
}
}
default:
return fmt.Errorf("Unknown --format: %s", c.Format)
}
return nil
}
......@@ -9,6 +9,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
......@@ -18,7 +19,6 @@ import (
"k8s.io/client-go/dynamic"
"github.com/ksonnet/kubecfg/metadata"
"github.com/ksonnet/kubecfg/template"
"github.com/ksonnet/kubecfg/utils"
)
......@@ -45,37 +45,23 @@ type UpdateCmd struct {
Discovery discovery.DiscoveryInterface
DefaultNamespace string
Expander *template.Expander
Environment *string
Files []string
Create bool
GcTag string
SkipGc bool
DryRun bool
}
func (c UpdateCmd) Run(wd metadata.AbsPath) error {
files, err := GetFiles(wd, c.Environment, c.Files)
if err != nil {
return err
}
objs, err := c.Expander.Expand(files)
if err != nil {
return err
}
func (c UpdateCmd) Run(apiObjects []*unstructured.Unstructured, wd metadata.AbsPath) error {
dryRunText := ""
if c.DryRun {
dryRunText = " (dry-run)"
}
sort.Sort(utils.DependencyOrder(objs))
sort.Sort(utils.DependencyOrder(apiObjects))
seenUids := sets.NewString()
for _, obj := range objs {
for _, obj := range apiObjects {
if c.GcTag != "" {
utils.SetMetaDataAnnotation(obj, AnnotationGcTag, c.GcTag)
}
......
// Copyright 2017 The kubecfg authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kubecfg
import (
"fmt"
"io"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/discovery"
"github.com/ksonnet/kubecfg/utils"
)
// ValidateCmd represents the validate subcommand
type ValidateCmd struct {
Discovery discovery.DiscoveryInterface
}
func (c ValidateCmd) Run(apiObjects []*unstructured.Unstructured, out io.Writer) error {
hasError := false
for _, obj := range apiObjects {
desc := fmt.Sprintf("%s %s", utils.ResourceNameFor(c.Discovery, obj), utils.FqName(obj))
log.Info("Validating ", desc)
var allErrs []error
schema, err := utils.NewSwaggerSchemaFor(c.Discovery, obj.GroupVersionKind().GroupVersion())
if err != nil {
allErrs = append(allErrs, fmt.Errorf("Unable to fetch schema: %v", err))
} else {
// Validate obj
allErrs = append(allErrs, schema.Validate(obj)...)
}
for _, err := range allErrs {
log.Errorf("Error in %s: %v", desc, err)
hasError = true
}
}
if hasError {
return fmt.Errorf("Validation failed")
}
return nil
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment