Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions pkg/lintcontext/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lintcontext

import (
"golang.stackrox.io/kube-linter/internal/k8sutil"
"k8s.io/apimachinery/pkg/runtime"
)

// ObjectMetadata is metadata about an object.
Expand Down Expand Up @@ -31,6 +32,8 @@ type LintContext interface {
type lintContextImpl struct {
objects []Object
invalidObjects []InvalidObject

customDecoder runtime.Decoder
}

// Objects returns the (valid) objects loaded from this LintContext.
Expand Down
16 changes: 16 additions & 0 deletions pkg/lintcontext/create_contexts.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,31 @@ import (
"github.com/pkg/errors"
"golang.stackrox.io/kube-linter/internal/set"
"helm.sh/helm/v3/pkg/chartutil"
"k8s.io/apimachinery/pkg/runtime"
)

var (
knownYAMLExtensions = set.NewFrozenStringSet(".yaml", ".yml")
)

// Options represent values that can be provided to modify how objects are parsed to create lint contexts
type Options struct {
// CustomDecoder allows users to supply a non-default decoder to parse k8s objects. This can be used
// to allow the linter to create contexts for k8s custom resources
CustomDecoder runtime.Decoder
}

// CreateContexts creates a context. Each context contains a set of files that should be linted
// as a group.
// Currently, each directory of Kube YAML files (or Helm charts) are treated as a separate context.
// TODO: Figure out if it's useful to allow people to specify that files spanning different directories
// should be treated as being in the same context.
func CreateContexts(filesOrDirs ...string) ([]LintContext, error) {
return CreateContextsWithOptions(Options{}, filesOrDirs...)
}

// CreateContextsWithOptions creates a context with additional Options
func CreateContextsWithOptions(options Options, filesOrDirs ...string) ([]LintContext, error) {
contextsByDir := make(map[string]*lintContextImpl)
for _, fileOrDir := range filesOrDirs {
// Stdin
Expand All @@ -30,6 +42,7 @@ func CreateContexts(filesOrDirs ...string) ([]LintContext, error) {
continue
}
ctx := new()
ctx.customDecoder = options.CustomDecoder
if err := ctx.loadObjectsFromReader("<standard input>", os.Stdin); err != nil {
return nil, err
}
Expand All @@ -49,6 +62,7 @@ func CreateContexts(filesOrDirs ...string) ([]LintContext, error) {
if !info.IsDir() {
if strings.HasSuffix(strings.ToLower(currentPath), ".tgz") {
ctx := new()
ctx.customDecoder = options.CustomDecoder
if err := ctx.loadObjectsFromTgzHelmChart(currentPath); err != nil {
return err
}
Expand All @@ -63,6 +77,7 @@ func CreateContexts(filesOrDirs ...string) ([]LintContext, error) {
ctx := contextsByDir[dirName]
if ctx == nil {
ctx = new()
ctx.customDecoder = options.CustomDecoder
contextsByDir[dirName] = ctx
}
if err := ctx.loadObjectsFromYAMLFile(currentPath, info); err != nil {
Expand All @@ -77,6 +92,7 @@ func CreateContexts(filesOrDirs ...string) ([]LintContext, error) {
return nil
}
ctx := new()
ctx.customDecoder = options.CustomDecoder
contextsByDir[currentPath] = ctx
if err := ctx.loadObjectsFromHelmChart(currentPath); err != nil {
return err
Expand Down
12 changes: 8 additions & 4 deletions pkg/lintcontext/parse_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"helm.sh/helm/v3/pkg/cli/values"
"helm.sh/helm/v3/pkg/engine"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/kubernetes/scheme"
Expand All @@ -34,15 +35,18 @@ var (
decoder = serializer.NewCodecFactory(clientSchema).UniversalDeserializer()
)

func parseObjects(data []byte) ([]k8sutil.Object, error) {
obj, _, err := decoder.Decode(data, nil, nil)
func parseObjects(data []byte, d runtime.Decoder) ([]k8sutil.Object, error) {
if d == nil {
d = decoder
}
obj, _, err := d.Decode(data, nil, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to decode")
}
if list, ok := obj.(*v1.List); ok {
objs := make([]k8sutil.Object, 0, len(list.Items))
for i, item := range list.Items {
obj, _, err := decoder.Decode(item.Raw, nil, nil)
obj, _, err := d.Decode(item.Raw, nil, nil)
if err != nil {
return nil, errors.Wrapf(err, "decoding item %d in the list", i)
}
Expand Down Expand Up @@ -197,7 +201,7 @@ func (l *lintContextImpl) loadObjectFromYAMLReader(filePath string, r *yaml.YAML
Raw: doc,
}

objs, err := parseObjects(doc)
objs, err := parseObjects(doc, l.customDecoder)
if err != nil {
l.addInvalidObjects(InvalidObject{
Metadata: metadata,
Expand Down