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

Add kube-score/skip annotation #624

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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 domain/kube-score.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type NamedReader interface {

type FileLocation struct {
Name string
Skip bool
Line int
}

Expand Down
51 changes: 51 additions & 0 deletions parser/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
networkingv1beta1 "k8s.io/api/networking/v1beta1"
policyv1 "k8s.io/api/policy/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
Expand Down Expand Up @@ -247,9 +248,31 @@ func detectFileLocation(fileName string, fileOffset int, fileContents []byte) ks
return ks.FileLocation{
Name: fileName,
Line: fileOffset,
Skip: false,
}
}

const (
SkippedResourceAnnotation = "kube-score/skip"
)

func IsSkipped(errs []error, annotations ...map[string]string) bool {
skip := false
for _, annotations := range annotations {
if skipAnnotation, ok := annotations[SkippedResourceAnnotation]; ok {
if err := yaml.Unmarshal([]byte(skipAnnotation), &skip); err != nil {
errs = append(errs, fmt.Errorf("invalid skip annotation %q, must be boolean", skipAnnotation))
}
}
}
return skip
}

func (p *Parser) isSkipped(res metav1.ObjectMetaAccessor, errs parseErrors) bool {
annotations := res.GetObjectMeta().GetAnnotations()
return IsSkipped(errs, annotations)
}

func (p *Parser) decodeItem(s *parsedObjects, detectedVersion schema.GroupVersionKind, fileName string, fileOffset int, fileContents []byte) error {
addPodSpeccer := func(ps ks.PodSpecer) {
s.podspecers = append(s.podspecers, ps)
Expand All @@ -268,32 +291,37 @@ func (p *Parser) decodeItem(s *parsedObjects, detectedVersion schema.GroupVersio
case corev1.SchemeGroupVersion.WithKind("Pod"):
var pod corev1.Pod
errs.AddIfErr(p.decode(fileContents, &pod))
fileLocation.Skip = p.isSkipped(&pod, errs)
p := internalpod.Pod{Obj: pod, Location: fileLocation}
s.pods = append(s.pods, p)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: pod.TypeMeta, ObjectMeta: pod.ObjectMeta, FileLocationer: p})

case batchv1.SchemeGroupVersion.WithKind("Job"):
var job batchv1.Job
errs.AddIfErr(p.decode(fileContents, &job))
fileLocation.Skip = p.isSkipped(&job, errs)
addPodSpeccer(internal.Batchv1Job{Job: job, Location: fileLocation})

case batchv1beta1.SchemeGroupVersion.WithKind("CronJob"):
var cronjob batchv1beta1.CronJob
errs.AddIfErr(p.decode(fileContents, &cronjob))
fileLocation.Skip = p.isSkipped(&cronjob, errs)
cjob := internalcronjob.CronJobV1beta1{Obj: cronjob, Location: fileLocation}
addPodSpeccer(cjob)
s.cronjobs = append(s.cronjobs, cjob)

case batchv1.SchemeGroupVersion.WithKind("CronJob"):
var cronjob batchv1.CronJob
errs.AddIfErr(p.decode(fileContents, &cronjob))
fileLocation.Skip = p.isSkipped(&cronjob, errs)
cjob := internalcronjob.CronJobV1{Obj: cronjob, Location: fileLocation}
addPodSpeccer(cjob)
s.cronjobs = append(s.cronjobs, cjob)

case appsv1.SchemeGroupVersion.WithKind("Deployment"):
var deployment appsv1.Deployment
errs.AddIfErr(p.decode(fileContents, &deployment))
fileLocation.Skip = p.isSkipped(&deployment, errs)
deploy := internal.Appsv1Deployment{Obj: deployment, Location: fileLocation}
addPodSpeccer(deploy)

Expand All @@ -302,19 +330,24 @@ func (p *Parser) decodeItem(s *parsedObjects, detectedVersion schema.GroupVersio
case appsv1beta1.SchemeGroupVersion.WithKind("Deployment"):
var deployment appsv1beta1.Deployment
errs.AddIfErr(p.decode(fileContents, &deployment))
fileLocation.Skip = p.isSkipped(&deployment, errs)
addPodSpeccer(internal.Appsv1beta1Deployment{Deployment: deployment, Location: fileLocation})
case appsv1beta2.SchemeGroupVersion.WithKind("Deployment"):
var deployment appsv1beta2.Deployment
errs.AddIfErr(p.decode(fileContents, &deployment))
fileLocation.Skip = p.isSkipped(&deployment, errs)
addPodSpeccer(internal.Appsv1beta2Deployment{Deployment: deployment, Location: fileLocation})
case extensionsv1beta1.SchemeGroupVersion.WithKind("Deployment"):
var deployment extensionsv1beta1.Deployment
errs.AddIfErr(p.decode(fileContents, &deployment))
fileLocation.Skip = p.isSkipped(&deployment, errs)
addPodSpeccer(internal.Extensionsv1beta1Deployment{Deployment: deployment, Location: fileLocation})

case appsv1.SchemeGroupVersion.WithKind("StatefulSet"):
var statefulSet appsv1.StatefulSet
errs.AddIfErr(p.decode(fileContents, &statefulSet))
fileLocation.Skip = p.isSkipped(&statefulSet, errs)

sset := internal.Appsv1StatefulSet{Obj: statefulSet, Location: fileLocation}
addPodSpeccer(sset)

Expand All @@ -323,48 +356,59 @@ func (p *Parser) decodeItem(s *parsedObjects, detectedVersion schema.GroupVersio
case appsv1beta1.SchemeGroupVersion.WithKind("StatefulSet"):
var statefulSet appsv1beta1.StatefulSet
errs.AddIfErr(p.decode(fileContents, &statefulSet))
fileLocation.Skip = p.isSkipped(&statefulSet, errs)

addPodSpeccer(internal.Appsv1beta1StatefulSet{StatefulSet: statefulSet, Location: fileLocation})
case appsv1beta2.SchemeGroupVersion.WithKind("StatefulSet"):
var statefulSet appsv1beta2.StatefulSet
errs.AddIfErr(p.decode(fileContents, &statefulSet))
fileLocation.Skip = p.isSkipped(&statefulSet, errs)

addPodSpeccer(internal.Appsv1beta2StatefulSet{StatefulSet: statefulSet, Location: fileLocation})

case appsv1.SchemeGroupVersion.WithKind("DaemonSet"):
var daemonset appsv1.DaemonSet
errs.AddIfErr(p.decode(fileContents, &daemonset))
fileLocation.Skip = p.isSkipped(&daemonset, errs)
addPodSpeccer(internal.Appsv1DaemonSet{DaemonSet: daemonset, Location: fileLocation})
case appsv1beta2.SchemeGroupVersion.WithKind("DaemonSet"):
var daemonset appsv1beta2.DaemonSet
errs.AddIfErr(p.decode(fileContents, &daemonset))
fileLocation.Skip = p.isSkipped(&daemonset, errs)
addPodSpeccer(internal.Appsv1beta2DaemonSet{DaemonSet: daemonset, Location: fileLocation})
case extensionsv1beta1.SchemeGroupVersion.WithKind("DaemonSet"):
var daemonset extensionsv1beta1.DaemonSet
errs.AddIfErr(p.decode(fileContents, &daemonset))
fileLocation.Skip = p.isSkipped(&daemonset, errs)
addPodSpeccer(internal.Extensionsv1beta1DaemonSet{DaemonSet: daemonset, Location: fileLocation})

case networkingv1.SchemeGroupVersion.WithKind("NetworkPolicy"):
var netpol networkingv1.NetworkPolicy
errs.AddIfErr(p.decode(fileContents, &netpol))
fileLocation.Skip = p.isSkipped(&netpol, errs)
np := internalnetpol.NetworkPolicy{Obj: netpol, Location: fileLocation}
s.networkPolicies = append(s.networkPolicies, np)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: netpol.TypeMeta, ObjectMeta: netpol.ObjectMeta, FileLocationer: np})

case corev1.SchemeGroupVersion.WithKind("Service"):
var service corev1.Service
errs.AddIfErr(p.decode(fileContents, &service))
fileLocation.Skip = p.isSkipped(&service, errs)
serv := internalservice.Service{Obj: service, Location: fileLocation}
s.services = append(s.services, serv)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: service.TypeMeta, ObjectMeta: service.ObjectMeta, FileLocationer: serv})

case policyv1beta1.SchemeGroupVersion.WithKind("PodDisruptionBudget"):
var disruptBudget policyv1beta1.PodDisruptionBudget
errs.AddIfErr(p.decode(fileContents, &disruptBudget))
fileLocation.Skip = p.isSkipped(&disruptBudget, errs)
dbug := internalpdb.PodDisruptionBudgetV1beta1{Obj: disruptBudget, Location: fileLocation}
s.podDisruptionBudgets = append(s.podDisruptionBudgets, dbug)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: disruptBudget.TypeMeta, ObjectMeta: disruptBudget.ObjectMeta, FileLocationer: dbug})
case policyv1.SchemeGroupVersion.WithKind("PodDisruptionBudget"):
var disruptBudget policyv1.PodDisruptionBudget
errs.AddIfErr(p.decode(fileContents, &disruptBudget))
fileLocation.Skip = p.isSkipped(&disruptBudget, errs)
dbug := internalpdb.PodDisruptionBudgetV1{Obj: disruptBudget, Location: fileLocation}
s.podDisruptionBudgets = append(s.podDisruptionBudgets, dbug)
s.bothMetas = append(s.bothMetas, ks.BothMeta{
Expand All @@ -376,41 +420,47 @@ func (p *Parser) decodeItem(s *parsedObjects, detectedVersion schema.GroupVersio
case extensionsv1beta1.SchemeGroupVersion.WithKind("Ingress"):
var ingress extensionsv1beta1.Ingress
errs.AddIfErr(p.decode(fileContents, &ingress))
fileLocation.Skip = p.isSkipped(&ingress, errs)
ing := internal.ExtensionsIngressV1beta1{Ingress: ingress, Location: fileLocation}
s.ingresses = append(s.ingresses, ing)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: ingress.TypeMeta, ObjectMeta: ingress.ObjectMeta, FileLocationer: ing})

case networkingv1beta1.SchemeGroupVersion.WithKind("Ingress"):
var ingress networkingv1beta1.Ingress
errs.AddIfErr(p.decode(fileContents, &ingress))
fileLocation.Skip = p.isSkipped(&ingress, errs)
ing := internal.IngressV1beta1{Ingress: ingress, Location: fileLocation}
s.ingresses = append(s.ingresses, ing)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: ingress.TypeMeta, ObjectMeta: ingress.ObjectMeta, FileLocationer: ing})

case networkingv1.SchemeGroupVersion.WithKind("Ingress"):
var ingress networkingv1.Ingress
errs.AddIfErr(p.decode(fileContents, &ingress))
fileLocation.Skip = p.isSkipped(&ingress, errs)
ing := internal.IngressV1{Ingress: ingress, Location: fileLocation}
s.ingresses = append(s.ingresses, ing)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: ingress.TypeMeta, ObjectMeta: ingress.ObjectMeta, FileLocationer: ing})

case autoscalingv1.SchemeGroupVersion.WithKind("HorizontalPodAutoscaler"):
var hpa autoscalingv1.HorizontalPodAutoscaler
errs.AddIfErr(p.decode(fileContents, &hpa))
fileLocation.Skip = p.isSkipped(&hpa, errs)
h := internal.HPAv1{HorizontalPodAutoscaler: hpa, Location: fileLocation}
s.hpaTargeters = append(s.hpaTargeters, h)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: hpa.TypeMeta, ObjectMeta: hpa.ObjectMeta, FileLocationer: h})

case autoscalingv2beta1.SchemeGroupVersion.WithKind("HorizontalPodAutoscaler"):
var hpa autoscalingv2beta1.HorizontalPodAutoscaler
errs.AddIfErr(p.decode(fileContents, &hpa))
fileLocation.Skip = p.isSkipped(&hpa, errs)
h := internal.HPAv2beta1{HorizontalPodAutoscaler: hpa, Location: fileLocation}
s.hpaTargeters = append(s.hpaTargeters, h)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: hpa.TypeMeta, ObjectMeta: hpa.ObjectMeta, FileLocationer: h})

case autoscalingv2beta2.SchemeGroupVersion.WithKind("HorizontalPodAutoscaler"):
var hpa autoscalingv2beta2.HorizontalPodAutoscaler
errs.AddIfErr(p.decode(fileContents, &hpa))
fileLocation.Skip = p.isSkipped(&hpa, errs)
h := internal.HPAv2beta2{HorizontalPodAutoscaler: hpa, Location: fileLocation}
s.hpaTargeters = append(s.hpaTargeters, h)
s.bothMetas = append(s.bothMetas, ks.BothMeta{
Expand All @@ -422,6 +472,7 @@ func (p *Parser) decodeItem(s *parsedObjects, detectedVersion schema.GroupVersio
case autoscalingv2.SchemeGroupVersion.WithKind("HorizontalPodAutoscaler"):
var hpa autoscalingv2.HorizontalPodAutoscaler
errs.AddIfErr(p.decode(fileContents, &hpa))
fileLocation.Skip = p.isSkipped(&hpa, errs)
h := internal.HPAv2{HorizontalPodAutoscaler: hpa, Location: fileLocation}
s.hpaTargeters = append(s.hpaTargeters, h)
s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: hpa.TypeMeta, ObjectMeta: hpa.ObjectMeta, FileLocationer: h})
Expand Down
116 changes: 116 additions & 0 deletions parser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package parser

import (
"fmt"
"io"
"os"
"strings"
"testing"

ks "github.com/zegl/kube-score/domain"
Expand Down Expand Up @@ -73,3 +75,117 @@ spec:
assert.Equal(t, "someName", fl.Name)
assert.Equal(t, 123, fl.Line)
}

type namedReader struct {
io.Reader
name string
}

func (n namedReader) Name() string {
return n.name
}

func parse(t *testing.T, doc, name string) ks.AllTypes {
p, err := New(nil)
assert.NoError(t, err)
parsedFiles, err := p.ParseFiles([]ks.NamedReader{
namedReader{Reader: strings.NewReader(doc), name: name},
})
assert.NoError(t, err)
return parsedFiles
}

func TestSkipNo(t *testing.T) {
t.Parallel()
doc := `kind: Deployment
apiVersion: apps/v1
metadata:
name: foo
annotations:
kube-score/skip: "No"
spec:
template:
metadata:
labels:
foo: bar`

location := parse(t, doc, "skip-yes.yaml").Deployments()[0].FileLocation()
assert.Equal(t, "skip-yes.yaml", location.Name)
assert.Equal(t, false, location.Skip)
}

func TestSkipYes(t *testing.T) {
t.Parallel()
doc := `kind: Deployment
apiVersion: apps/v1
metadata:
name: foo
annotations:
kube-score/skip: " yes "
spec:
template:
metadata:
labels:
foo: bar`

location := parse(t, doc, "skip-yes.yaml").Deployments()[0].FileLocation()
assert.Equal(t, "skip-yes.yaml", location.Name)
assert.Equal(t, true, location.Skip)
}

func TestSkipTrueUppercase(t *testing.T) {
t.Parallel()
doc := `kind: Deployment
apiVersion: apps/v1
metadata:
name: foo
annotations:
"kube-score/skip": "True"
spec:
template:
metadata:
labels:
foo: bar`

location := parse(t, doc, "skip-true-uppercase.yaml").Deployments()[0].FileLocation()
assert.Equal(t, "skip-true-uppercase.yaml", location.Name)
assert.Equal(t, true, location.Skip)
}

func TestSkipTrue(t *testing.T) {
t.Parallel()
doc := `kind: Deployment
apiVersion: apps/v1
metadata:
name: foo
annotations:
"kube-score/skip": "true"
spec:
template:
metadata:
labels:
foo: bar`

location := parse(t, doc, "skip-true.yaml").Deployments()[0].FileLocation()
assert.Equal(t, "skip-true.yaml", location.Name)
assert.Equal(t, true, location.Skip)
}

func TestSkipFalse(t *testing.T) {
t.Parallel()
doc := `kind: Deployment
apiVersion: apps/v1
metadata:
name: foo
annotations:
"kube-score/skip": "false"
spec:
template:
metadata:
labels:
foo: bar`

location := parse(t, doc, "skip-false.yaml").Deployments()[0].FileLocation()
assert.Equal(t, "skip-false.yaml", location.Name)
assert.Equal(t, false, location.Skip)
}
20 changes: 16 additions & 4 deletions renderer/human/human.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,22 @@ func Human(scoreCard *scorecard.Scorecard, verboseOutput int, termWidth int, use
}
}

for _, card := range scoredObject.Checks {
r := outputHumanStep(card, verboseOutput, termWidth)
if _, err := io.Copy(w, r); err != nil {
return nil, fmt.Errorf("failed to copy output: %w", err)
if scoredObject.FileLocation.Skip {
if verboseOutput >= 2 {
// Only print skipped files if verbosity is at least 2
color.New(color.FgGreen).Fprintf(
w,
" [SKIPPED] %s#L%d\n",
scoredObject.FileLocation.Name,
scoredObject.FileLocation.Line,
)
}
} else {
for _, card := range scoredObject.Checks {
r := outputHumanStep(card, verboseOutput, termWidth)
if _, err := io.Copy(w, r); err != nil {
return nil, fmt.Errorf("failed to copy output: %w", err)
}
}
}
}
Expand Down
Loading