From 15b85ecb5223db2193738e27c64f7dcb66f21b13 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Wed, 11 Sep 2019 16:36:10 -0700 Subject: [PATCH 1/2] Duck-type k8s manifests instead of actually parsing when running skaffold init --- pkg/skaffold/initializer/kubectl/kubectl.go | 22 +++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/pkg/skaffold/initializer/kubectl/kubectl.go b/pkg/skaffold/initializer/kubectl/kubectl.go index 9784cf2be00..d288610bb47 100644 --- a/pkg/skaffold/initializer/kubectl/kubectl.go +++ b/pkg/skaffold/initializer/kubectl/kubectl.go @@ -25,9 +25,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" yaml "gopkg.in/yaml.v2" - "k8s.io/apimachinery/pkg/runtime" k8syaml "k8s.io/apimachinery/pkg/util/yaml" - "k8s.io/client-go/kubernetes/scheme" ) // ValidSuffixes are the supported file formats for kubernetes manifests @@ -89,7 +87,7 @@ func parseImagesFromKubernetesYaml(filepath string) ([]string, error) { } r := k8syaml.NewYAMLReader(bufio.NewReader(f)) - objects := []runtime.Object{} + yamlsFound := 0 images := []string{} for { @@ -100,21 +98,25 @@ func parseImagesFromKubernetesYaml(filepath string) ([]string, error) { if err != nil { return nil, errors.Wrap(err, "reading config file") } - d := scheme.Codecs.UniversalDeserializer() - obj, _, err := d.Decode(doc, nil, nil) - if err != nil { - return nil, errors.Wrap(err, "decoding kubernetes yaml") - } m := make(map[interface{}]interface{}) if err := yaml.Unmarshal(doc, &m); err != nil { return nil, errors.Wrap(err, "reading kubernetes YAML") } + if _, ok := m["apiVersion"]; !ok { + logrus.Debugf("'apiVersion' not present in yaml, continuing") + continue + } + if _, ok := m["kind"]; !ok { + logrus.Debugf("'kind' not present in yaml, continuing") + continue + } + yamlsFound++ + images = append(images, parseImagesFromYaml(m)...) - objects = append(objects, obj) } - if len(objects) == 0 { + if yamlsFound == 0 { return nil, errors.New("no valid kubernetes objects decoded") } return images, nil From e7b2603ad7deccfe66d2d67a532e7f0050dc2019 Mon Sep 17 00:00:00 2001 From: Nick Kubala Date: Thu, 12 Sep 2019 10:59:11 -0700 Subject: [PATCH 2/2] check for metadata field and add test --- pkg/skaffold/initializer/kubectl/kubectl.go | 26 +++++++++++++------ .../initializer/kubectl/kubectl_test.go | 13 ++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/pkg/skaffold/initializer/kubectl/kubectl.go b/pkg/skaffold/initializer/kubectl/kubectl.go index d288610bb47..e4804938e5a 100644 --- a/pkg/skaffold/initializer/kubectl/kubectl.go +++ b/pkg/skaffold/initializer/kubectl/kubectl.go @@ -31,6 +31,8 @@ import ( // ValidSuffixes are the supported file formats for kubernetes manifests var ValidSuffixes = []string{".yml", ".yaml", ".json"} +var requiredFields = []string{"apiVersion", "kind", "metadata"} + // Kubectl holds parameters to run kubectl. type Kubectl struct { configs []string @@ -77,8 +79,10 @@ func (k *Kubectl) GetImages() []string { return k.images } -// parseImagesFromKubernetesYaml attempts to parse k8s objects from a yaml file -// if successful, it will return the images referenced in the k8s config +// parseImagesFromKubernetesYaml uses required fields from the k8s spec +// to determine if a provided yaml file is a valid k8s manifest, as detailed in +// https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields. +// if so, it will return the images referenced in the k8s config // so they can be built by the generated skaffold yaml func parseImagesFromKubernetesYaml(filepath string) ([]string, error) { f, err := os.Open(filepath) @@ -104,14 +108,10 @@ func parseImagesFromKubernetesYaml(filepath string) ([]string, error) { return nil, errors.Wrap(err, "reading kubernetes YAML") } - if _, ok := m["apiVersion"]; !ok { - logrus.Debugf("'apiVersion' not present in yaml, continuing") - continue - } - if _, ok := m["kind"]; !ok { - logrus.Debugf("'kind' not present in yaml, continuing") + if !isKubernetesYaml(m) { continue } + yamlsFound++ images = append(images, parseImagesFromYaml(m)...) @@ -122,6 +122,16 @@ func parseImagesFromKubernetesYaml(filepath string) ([]string, error) { return images, nil } +func isKubernetesYaml(doc map[interface{}]interface{}) bool { + for _, field := range requiredFields { + if _, ok := doc[field]; !ok { + logrus.Debugf("%s not present in yaml, continuing", field) + return false + } + } + return true +} + // adapted from pkg/skaffold/deploy/kubectl/recursiveReplaceImage() func parseImagesFromYaml(doc interface{}) []string { images := []string{} diff --git a/pkg/skaffold/initializer/kubectl/kubectl_test.go b/pkg/skaffold/initializer/kubectl/kubectl_test.go index 02f907c35ae..53f2be89aed 100644 --- a/pkg/skaffold/initializer/kubectl/kubectl_test.go +++ b/pkg/skaffold/initializer/kubectl/kubectl_test.go @@ -98,6 +98,19 @@ subjects: images: []string{}, shouldErr: false, }, + { + description: "crd", + contents: `apiVersion: my.crd.io/v1 +kind: CustomType +metadata: + name: test crd +spec: + containers: + - name: container + image: gcr.io/my/image`, + images: []string{"gcr.io/my/image"}, + shouldErr: false, + }, } for _, test := range tests { testutil.Run(t, test.description, func(t *testutil.T) {