Use owner references to indicate the objects that refer to the problematic object.
* Change output format to jsonvarsha/versions
parent
0320c5633a
commit
65ba22e8d8
|
@ -34,15 +34,13 @@ func (fq *fullyQualifiedImageCheck) Description() string {
|
|||
// Run runs this check on a set of Kubernetes objects. It can return warnings
|
||||
// (low-priority problems) and errors (high-priority problems) as well as an
|
||||
// error value indicating that the check failed to run.
|
||||
func (fq *fullyQualifiedImageCheck) Run(objects *kube.Objects) ([]kube.Diagnostic, error) {
|
||||
var diagnostics []kube.Diagnostic
|
||||
func (fq *fullyQualifiedImageCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
||||
var diagnostics []checks.Diagnostic
|
||||
|
||||
for _, pod := range objects.Pods.Items {
|
||||
podName := pod.GetName()
|
||||
namespace := pod.GetNamespace()
|
||||
d := checkImage(pod.Spec.Containers, podName, namespace)
|
||||
d := checkImage(pod.Spec.Containers, pod)
|
||||
diagnostics = append(diagnostics, d...)
|
||||
d = checkImage(pod.Spec.InitContainers, podName, namespace)
|
||||
d = checkImage(pod.Spec.InitContainers, pod)
|
||||
diagnostics = append(diagnostics, d...)
|
||||
}
|
||||
|
||||
|
@ -51,17 +49,29 @@ func (fq *fullyQualifiedImageCheck) Run(objects *kube.Objects) ([]kube.Diagnosti
|
|||
|
||||
// checkImage checks if the image name is fully qualified
|
||||
// Adds a warning if the container does not use a fully qualified image name
|
||||
func checkImage(containers []corev1.Container, podName string, namespace string) []kube.Diagnostic {
|
||||
var d []kube.Diagnostic
|
||||
func checkImage(containers []corev1.Container, pod corev1.Pod) []checks.Diagnostic {
|
||||
var diagnostics []checks.Diagnostic
|
||||
for _, container := range containers {
|
||||
value, err := reference.ParseAnyReference(container.Image)
|
||||
if err != nil {
|
||||
d = append(d, kube.Diagnostic{Category: "error", Message: fmt.Sprintf("Malformed image name for container '%s' in pod '%s' in namespace '%s'", container.Name, podName, namespace)})
|
||||
d := checks.Diagnostic{
|
||||
Severity: checks.Error,
|
||||
Message: fmt.Sprintf("Malformed image name for container '%s' in pod '%s'", container.Name, pod.GetName()),
|
||||
Object: kube.Object{TypeInfo: &pod.TypeMeta, ObjectInfo: &pod.ObjectMeta},
|
||||
Owners: pod.ObjectMeta.GetOwnerReferences(),
|
||||
}
|
||||
diagnostics = append(diagnostics, d)
|
||||
} else {
|
||||
if value.String() != container.Image {
|
||||
d = append(d, kube.Diagnostic{Category: "warning", Message: fmt.Sprintf("Use fully qualified image for container '%s' in pod '%s' in namespace '%s'", container.Name, podName, namespace)})
|
||||
d := checks.Diagnostic{
|
||||
Severity: checks.Warning,
|
||||
Message: fmt.Sprintf("Use fully qualified image for container '%s' in pod '%s'", container.Name, pod.GetName()),
|
||||
Object: kube.Object{TypeInfo: &pod.TypeMeta, ObjectInfo: &pod.ObjectMeta},
|
||||
Owners: pod.ObjectMeta.GetOwnerReferences(),
|
||||
}
|
||||
diagnostics = append(diagnostics, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
return d
|
||||
return diagnostics
|
||||
}
|
||||
|
|
|
@ -23,13 +23,13 @@ func TestFullyQualifiedImageCheckRegistration(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFullyQualifiedImageWarning(t *testing.T) {
|
||||
const message string = "Use fully qualified image for container 'bar' in pod 'pod_foo' in namespace 'k8s'"
|
||||
const category string = "warning"
|
||||
const message string = "Use fully qualified image for container 'bar' in pod 'pod_foo'"
|
||||
const severity checks.Severity = checks.Warning
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
arg *kube.Objects
|
||||
expected []kube.Diagnostic
|
||||
expected []checks.Diagnostic
|
||||
}{
|
||||
{
|
||||
name: "no pods",
|
||||
|
@ -44,7 +44,7 @@ func TestFullyQualifiedImageWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container image - busybox:latest",
|
||||
arg: container("busybox:latest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - k8s.gcr.io/busybox",
|
||||
|
@ -54,7 +54,7 @@ func TestFullyQualifiedImageWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container image - busybox",
|
||||
arg: container("busybox"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - test:5000/repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
|
@ -64,7 +64,7 @@ func TestFullyQualifiedImageWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container image - repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: container("repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - test:5000/repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
|
@ -74,7 +74,7 @@ func TestFullyQualifiedImageWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container image - repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: container("repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - k8s.gcr.io/busybox:latest",
|
||||
|
@ -84,7 +84,7 @@ func TestFullyQualifiedImageWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container image - busybox:latest",
|
||||
arg: initContainer("busybox:latest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - k8s.gcr.io/busybox",
|
||||
|
@ -94,7 +94,7 @@ func TestFullyQualifiedImageWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container image - busybox",
|
||||
arg: initContainer("busybox"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - test:5000/repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
|
@ -104,7 +104,7 @@ func TestFullyQualifiedImageWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container image - repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: initContainer("repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - test:5000/repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
|
@ -114,7 +114,7 @@ func TestFullyQualifiedImageWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container image - repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: initContainer("repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -130,23 +130,22 @@ func TestFullyQualifiedImageWarning(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMalformedImageError(t *testing.T) {
|
||||
const message string = "Malformed image name for container 'bar' in pod 'pod_foo' in namespace 'k8s'"
|
||||
const category string = "error"
|
||||
|
||||
const message string = "Malformed image name for container 'bar' in pod 'pod_foo'"
|
||||
const severity checks.Severity = checks.Error
|
||||
scenarios := []struct {
|
||||
name string
|
||||
arg *kube.Objects
|
||||
expected []kube.Diagnostic
|
||||
expected []checks.Diagnostic
|
||||
}{
|
||||
{
|
||||
name: "container with image : test:5000/repo/image@sha256:digest",
|
||||
arg: container("test:5000/repo/image@sha256:digest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "init container with image : test:5000/repo/image@sha256:digest",
|
||||
arg: initContainer("test:5000/repo/image@sha256:digest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
}
|
||||
fullyQualifiedImageCheck := fullyQualifiedImageCheck{}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package basic
|
||||
|
||||
import (
|
||||
"github.com/digitalocean/clusterlint/checks"
|
||||
"github.com/digitalocean/clusterlint/kube"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -11,6 +12,7 @@ func initPod() *kube.Objects {
|
|||
Pods: &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod_foo", Namespace: "k8s"},
|
||||
},
|
||||
},
|
||||
|
@ -19,6 +21,21 @@ func initPod() *kube.Objects {
|
|||
return objs
|
||||
}
|
||||
|
||||
func GetTypeMeta() *metav1.TypeMeta {
|
||||
objs := initPod()
|
||||
return &objs.Pods.Items[0].TypeMeta
|
||||
}
|
||||
|
||||
func GetObjectMeta() *metav1.ObjectMeta {
|
||||
objs := initPod()
|
||||
return &objs.Pods.Items[0].ObjectMeta
|
||||
}
|
||||
|
||||
func GetOwners() []metav1.OwnerReference {
|
||||
objs := initPod()
|
||||
return objs.Pods.Items[0].ObjectMeta.GetOwnerReferences()
|
||||
}
|
||||
|
||||
func container(image string) *kube.Objects {
|
||||
objs := initPod()
|
||||
objs.Pods.Items[0].Spec = corev1.PodSpec{
|
||||
|
@ -43,9 +60,14 @@ func initContainer(image string) *kube.Objects {
|
|||
return objs
|
||||
}
|
||||
|
||||
func issues(category string, message string) []kube.Diagnostic {
|
||||
d := []kube.Diagnostic{
|
||||
{Category: category, Message: message},
|
||||
func issues(severity checks.Severity, message string) []checks.Diagnostic {
|
||||
d := []checks.Diagnostic{
|
||||
{
|
||||
Severity: severity,
|
||||
Message: message,
|
||||
Object: kube.Object{TypeInfo: GetTypeMeta(), ObjectInfo: GetObjectMeta()},
|
||||
Owners: GetOwners(),
|
||||
},
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
|
|
@ -35,13 +35,11 @@ func (l *latestTagCheck) Description() string {
|
|||
// Run runs this check on a set of Kubernetes objects. It can return warnings
|
||||
// (low-priority problems) and errors (high-priority problems) as well as an
|
||||
// error value indicating that the check failed to run.
|
||||
func (l *latestTagCheck) Run(objects *kube.Objects) ([]kube.Diagnostic, error) {
|
||||
var diagnostics []kube.Diagnostic
|
||||
func (l *latestTagCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
||||
var diagnostics []checks.Diagnostic
|
||||
for _, pod := range objects.Pods.Items {
|
||||
podName := pod.GetName()
|
||||
namespace := pod.GetNamespace()
|
||||
diagnostics = append(diagnostics, checkTags(pod.Spec.Containers, podName, namespace)...)
|
||||
diagnostics = append(diagnostics, checkTags(pod.Spec.InitContainers, podName, namespace)...)
|
||||
diagnostics = append(diagnostics, checkTags(pod.Spec.Containers, pod)...)
|
||||
diagnostics = append(diagnostics, checkTags(pod.Spec.InitContainers, pod)...)
|
||||
}
|
||||
|
||||
return diagnostics, nil
|
||||
|
@ -49,14 +47,20 @@ func (l *latestTagCheck) Run(objects *kube.Objects) ([]kube.Diagnostic, error) {
|
|||
|
||||
// checkTags checks if the image name conforms to pattern `image:latest` or `image`
|
||||
// Adds a warning if it finds any image that uses the latest tag
|
||||
func checkTags(containers []corev1.Container, podName string, namespace string) []kube.Diagnostic {
|
||||
var d []kube.Diagnostic
|
||||
func checkTags(containers []corev1.Container, pod corev1.Pod) []checks.Diagnostic {
|
||||
var diagnostics []checks.Diagnostic
|
||||
for _, container := range containers {
|
||||
namedRef, _ := reference.ParseNormalizedNamed(container.Image)
|
||||
tagNameOnly := reference.TagNameOnly(namedRef)
|
||||
if strings.HasSuffix(tagNameOnly.String(), ":latest") {
|
||||
d = append(d, kube.Diagnostic{Category: "warning", Message: fmt.Sprintf("[Best Practice] Use specific tags instead of latest for container '%s' in pod '%s' in namespace '%s'", container.Name, podName, namespace)})
|
||||
d := checks.Diagnostic{
|
||||
Severity: checks.Warning,
|
||||
Message: fmt.Sprintf("Avoid using latest tag for container '%s' in pod '%s'", container.Name, pod.GetName()),
|
||||
Object: kube.Object{TypeInfo: &pod.TypeMeta, ObjectInfo: &pod.ObjectMeta},
|
||||
Owners: pod.ObjectMeta.GetOwnerReferences(),
|
||||
}
|
||||
diagnostics = append(diagnostics, d)
|
||||
}
|
||||
}
|
||||
return d
|
||||
return diagnostics
|
||||
}
|
||||
|
|
|
@ -23,12 +23,13 @@ func TestLatestTagCheckRegistration(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLatestTagWarning(t *testing.T) {
|
||||
const message string = "[Best Practice] Use specific tags instead of latest for container 'bar' in pod 'pod_foo' in namespace 'k8s'"
|
||||
const category string = "warning"
|
||||
const message string = "Avoid using latest tag for container 'bar' in pod 'pod_foo'"
|
||||
const severity checks.Severity = checks.Warning
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
arg *kube.Objects
|
||||
expected []kube.Diagnostic
|
||||
expected []checks.Diagnostic
|
||||
}{
|
||||
{
|
||||
name: "no pods",
|
||||
|
@ -38,46 +39,46 @@ func TestLatestTagWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container image - k8s.gcr.io/busybox:latest",
|
||||
arg: container("k8s.gcr.io/busybox:latest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - busybox:latest",
|
||||
arg: container("busybox:latest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - k8s.gcr.io/busybox",
|
||||
arg: container("k8s.gcr.io/busybox"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - busybox",
|
||||
arg: container("busybox"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - private:5000/repo/busybox",
|
||||
name: "pod with container image - private:5000/busybox",
|
||||
arg: container("private:5000/repo/busybox"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - private:5000/repo/busybox:latest",
|
||||
name: "pod with container image - private:5000/busybox:latest",
|
||||
arg: container("private:5000/repo/busybox:latest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - test:5000/repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
name: "pod with container image - test:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: container("test:5000/repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "pod with container image - repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
name: "pod with container image - repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: container("repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "pod with container image - test:5000/repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: container("test:5000/repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
name: "pod with container image - test:5000/repo:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: container("test:5000/repo:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
|
@ -89,45 +90,45 @@ func TestLatestTagWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with init container image - k8s.gcr.io/busybox:latest",
|
||||
arg: initContainer("k8s.gcr.io/busybox:latest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with init container image - busybox:latest",
|
||||
arg: initContainer("busybox:latest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with init container image - k8s.gcr.io/busybox",
|
||||
arg: initContainer("k8s.gcr.io/busybox"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with init container image - busybox",
|
||||
arg: initContainer("busybox"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - private:5000/repo/busybox",
|
||||
name: "pod with container image - private:5000/busybox",
|
||||
arg: container("private:5000/repo/busybox"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - private:5000/repo/busybox:latest",
|
||||
name: "pod with container image - private:5000/busybox:latest",
|
||||
arg: container("private:5000/repo/busybox:latest"),
|
||||
expected: issues(category, message),
|
||||
expected: issues(severity, message),
|
||||
},
|
||||
{
|
||||
name: "pod with container image - test:5000/repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
name: "pod with container image - test:5000/repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: initContainer("test:5000/repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "pod with container image - test:5000/repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
name: "pod with container image - test:5000/repo:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: initContainer("test:5000/repo/image:ignore-tag@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "pod with container image - repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
name: "pod with container image - repo@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
arg: initContainer("repo/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||
expected: nil,
|
||||
},
|
||||
|
|
|
@ -19,23 +19,28 @@ func init() {
|
|||
type defaultNamespaceCheck struct{}
|
||||
|
||||
type alert struct {
|
||||
diagnostics []kube.Diagnostic
|
||||
diagnostics []checks.Diagnostic
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// GetWarnings returns alert.warnings
|
||||
func (alert *alert) GetDiagnostics() []kube.Diagnostic {
|
||||
func (alert *alert) GetDiagnostics() []checks.Diagnostic {
|
||||
return alert.diagnostics
|
||||
}
|
||||
|
||||
// SetWarnings sets alert.warnings
|
||||
func (alert *alert) SetDiagnostics(d []kube.Diagnostic) {
|
||||
func (alert *alert) SetDiagnostics(d []checks.Diagnostic) {
|
||||
alert.diagnostics = d
|
||||
}
|
||||
|
||||
// warn adds warnings for k8s objects that should not be in the default namespace
|
||||
func (alert *alert) warn(k8stype string, item metav1.ObjectMeta) {
|
||||
d := kube.Diagnostic{Category: "warning", Message: fmt.Sprintf("%s '%s' is in the default namespace.", k8stype, item.GetName())}
|
||||
func (alert *alert) warn(k8stype string, itemMeta metav1.ObjectMeta, itemType metav1.TypeMeta) {
|
||||
d := checks.Diagnostic{
|
||||
Severity: checks.Warning,
|
||||
Message: fmt.Sprintf("Avoid using the default namespace for %s '%s'", k8stype, itemMeta.GetName()),
|
||||
Object: kube.Object{TypeInfo: &itemType, ObjectInfo: &itemMeta},
|
||||
Owners: itemMeta.GetOwnerReferences(),
|
||||
}
|
||||
alert.mu.Lock()
|
||||
alert.diagnostics = append(alert.diagnostics, d)
|
||||
alert.mu.Unlock()
|
||||
|
@ -61,7 +66,7 @@ func (nc *defaultNamespaceCheck) Description() string {
|
|||
func checkPods(items *corev1.PodList, alert *alert) {
|
||||
for _, item := range items.Items {
|
||||
if corev1.NamespaceDefault == item.GetNamespace() {
|
||||
alert.warn("Pod", item.ObjectMeta)
|
||||
alert.warn("pod", item.ObjectMeta, item.TypeMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +75,7 @@ func checkPods(items *corev1.PodList, alert *alert) {
|
|||
func checkPodTemplates(items *corev1.PodTemplateList, alert *alert) {
|
||||
for _, item := range items.Items {
|
||||
if corev1.NamespaceDefault == item.GetNamespace() {
|
||||
alert.warn("Pod template", item.ObjectMeta)
|
||||
alert.warn("pod template", item.ObjectMeta, item.TypeMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +84,7 @@ func checkPodTemplates(items *corev1.PodTemplateList, alert *alert) {
|
|||
func checkPVCs(items *corev1.PersistentVolumeClaimList, alert *alert) {
|
||||
for _, item := range items.Items {
|
||||
if corev1.NamespaceDefault == item.GetNamespace() {
|
||||
alert.warn("Persistent Volume Claim", item.ObjectMeta)
|
||||
alert.warn("persistent volume claim", item.ObjectMeta, item.TypeMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +93,7 @@ func checkPVCs(items *corev1.PersistentVolumeClaimList, alert *alert) {
|
|||
func checkConfigMaps(items *corev1.ConfigMapList, alert *alert) {
|
||||
for _, item := range items.Items {
|
||||
if corev1.NamespaceDefault == item.GetNamespace() {
|
||||
alert.warn("Config Map", item.ObjectMeta)
|
||||
alert.warn("config map", item.ObjectMeta, item.TypeMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +102,7 @@ func checkConfigMaps(items *corev1.ConfigMapList, alert *alert) {
|
|||
func checkServices(items *corev1.ServiceList, alert *alert) {
|
||||
for _, item := range items.Items {
|
||||
if corev1.NamespaceDefault == item.GetNamespace() && item.GetName() != "kubernetes" {
|
||||
alert.warn("Service", item.ObjectMeta)
|
||||
alert.warn("service", item.ObjectMeta, item.TypeMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +111,7 @@ func checkServices(items *corev1.ServiceList, alert *alert) {
|
|||
func checkSecrets(items *corev1.SecretList, alert *alert) {
|
||||
for _, item := range items.Items {
|
||||
if corev1.NamespaceDefault == item.GetNamespace() && !strings.Contains(item.GetName(), "default-token-") {
|
||||
alert.warn("Secret", item.ObjectMeta)
|
||||
alert.warn("secret", item.ObjectMeta, item.TypeMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +120,7 @@ func checkSecrets(items *corev1.SecretList, alert *alert) {
|
|||
func checkSA(items *corev1.ServiceAccountList, alert *alert) {
|
||||
for _, item := range items.Items {
|
||||
if corev1.NamespaceDefault == item.GetNamespace() && item.GetName() != "default" {
|
||||
alert.warn("Service Account", item.ObjectMeta)
|
||||
alert.warn("service account", item.ObjectMeta, item.TypeMeta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +128,7 @@ func checkSA(items *corev1.ServiceAccountList, alert *alert) {
|
|||
// Run runs this check on a set of Kubernetes objects. It can return warnings
|
||||
// (low-priority problems) and errors (high-priority problems) as well as an
|
||||
// error value indicating that the check failed to run.
|
||||
func (nc *defaultNamespaceCheck) Run(objects *kube.Objects) ([]kube.Diagnostic, error) {
|
||||
func (nc *defaultNamespaceCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
||||
alert := &alert{}
|
||||
var g errgroup.Group
|
||||
g.Go(func() error {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package basic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/clusterlint/checks"
|
||||
|
@ -29,7 +28,7 @@ func TestNamespaceWarning(t *testing.T) {
|
|||
scenarios := []struct {
|
||||
name string
|
||||
arg *kube.Objects
|
||||
expected []kube.Diagnostic
|
||||
expected []checks.Diagnostic
|
||||
}{
|
||||
{"no objects in cluster", empty(), nil},
|
||||
{"user created objects in default namespace", userCreatedObjects(), errors()},
|
||||
|
@ -73,16 +72,59 @@ func userCreatedObjects() *kube.Objects {
|
|||
return objs
|
||||
}
|
||||
|
||||
func errors() []kube.Diagnostic {
|
||||
const warning string = "warning"
|
||||
d := []kube.Diagnostic{
|
||||
{Category: warning, Message: fmt.Sprintf("Pod 'pod_foo' is in the default namespace.")},
|
||||
{Category: warning, Message: fmt.Sprintf("Pod template 'template_foo' is in the default namespace.")},
|
||||
{Category: warning, Message: fmt.Sprintf("Persistent Volume Claim 'pvc_foo' is in the default namespace.")},
|
||||
{Category: warning, Message: fmt.Sprintf("Config Map 'cm_foo' is in the default namespace.")},
|
||||
{Category: warning, Message: fmt.Sprintf("Service 'svc_foo' is in the default namespace.")},
|
||||
{Category: warning, Message: fmt.Sprintf("Secret 'secret_foo' is in the default namespace.")},
|
||||
{Category: warning, Message: fmt.Sprintf("Service Account 'sa_foo' is in the default namespace.")},
|
||||
func errors() []checks.Diagnostic {
|
||||
objs := userCreatedObjects()
|
||||
pod := objs.Pods.Items[0]
|
||||
template := objs.PodTemplates.Items[0]
|
||||
pvc := objs.PersistentVolumeClaims.Items[0]
|
||||
cm := objs.ConfigMaps.Items[0]
|
||||
service := objs.Services.Items[0]
|
||||
secret := objs.Secrets.Items[0]
|
||||
sa := objs.ServiceAccounts.Items[0]
|
||||
d := []checks.Diagnostic{
|
||||
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "Avoid using the default namespace for pod 'pod_foo'",
|
||||
Object: kube.Object{TypeInfo: &pod.TypeMeta, ObjectInfo: &pod.ObjectMeta},
|
||||
Owners: pod.ObjectMeta.GetOwnerReferences(),
|
||||
},
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "Avoid using the default namespace for pod template 'template_foo'",
|
||||
Object: kube.Object{TypeInfo: &template.TypeMeta, ObjectInfo: &template.ObjectMeta},
|
||||
Owners: template.ObjectMeta.GetOwnerReferences(),
|
||||
},
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "Avoid using the default namespace for persistent volume claim 'pvc_foo'",
|
||||
Object: kube.Object{TypeInfo: &pvc.TypeMeta, ObjectInfo: &pvc.ObjectMeta},
|
||||
Owners: pvc.ObjectMeta.GetOwnerReferences(),
|
||||
},
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "Avoid using the default namespace for config map 'cm_foo'",
|
||||
Object: kube.Object{TypeInfo: &cm.TypeMeta, ObjectInfo: &cm.ObjectMeta},
|
||||
Owners: cm.ObjectMeta.GetOwnerReferences(),
|
||||
},
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "Avoid using the default namespace for service 'svc_foo'",
|
||||
Object: kube.Object{TypeInfo: &service.TypeMeta, ObjectInfo: &service.ObjectMeta},
|
||||
Owners: service.ObjectMeta.GetOwnerReferences(),
|
||||
},
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "Avoid using the default namespace for secret 'secret_foo'",
|
||||
Object: kube.Object{TypeInfo: &secret.TypeMeta, ObjectInfo: &secret.ObjectMeta},
|
||||
Owners: secret.ObjectMeta.GetOwnerReferences(),
|
||||
},
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "Avoid using the default namespace for service account 'sa_foo'",
|
||||
Object: kube.Object{TypeInfo: &sa.TypeMeta, ObjectInfo: &sa.ObjectMeta},
|
||||
Owners: sa.ObjectMeta.GetOwnerReferences(),
|
||||
},
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
|
|
@ -33,12 +33,18 @@ func (p *podStatusCheck) Description() string {
|
|||
// Run runs this check on a set of Kubernetes objects. It can return warnings
|
||||
// (low-priority problems) and errors (high-priority problems) as well as an
|
||||
// error value indicating that the check failed to run.
|
||||
func (p *podStatusCheck) Run(objects *kube.Objects) ([]kube.Diagnostic, error) {
|
||||
var diagnostics []kube.Diagnostic
|
||||
func (p *podStatusCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
||||
var diagnostics []checks.Diagnostic
|
||||
|
||||
for _, pod := range objects.Pods.Items {
|
||||
if corev1.PodFailed == pod.Status.Phase || corev1.PodUnknown == pod.Status.Phase {
|
||||
diagnostics = append(diagnostics, kube.Diagnostic{Category: "error", Message: fmt.Sprintf("Pod '%s' in namespace '%s' has state: %s. Pod state should be `Running`, `Pending` or `Succeeded`.", pod.GetName(), pod.GetNamespace(), pod.Status.Phase)})
|
||||
d := checks.Diagnostic{
|
||||
Severity: checks.Warning,
|
||||
Message: fmt.Sprintf("Pod '%s' in namespace '%s' has state: %s. Pod state should be `Running`, `Pending` or `Succeeded`.", pod.GetName(), pod.GetNamespace(), pod.Status.Phase),
|
||||
Object: kube.Object{TypeInfo: &pod.TypeMeta, ObjectInfo: &pod.ObjectMeta},
|
||||
Owners: pod.ObjectMeta.GetOwnerReferences(),
|
||||
}
|
||||
diagnostics = append(diagnostics, d)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package basic
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/clusterlint/checks"
|
||||
"github.com/digitalocean/clusterlint/kube"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
@ -19,7 +20,7 @@ func TestPodStateError(t *testing.T) {
|
|||
scenarios := []struct {
|
||||
name string
|
||||
arg *kube.Objects
|
||||
expected []kube.Diagnostic
|
||||
expected []checks.Diagnostic
|
||||
}{
|
||||
{
|
||||
name: "no pods",
|
||||
|
@ -44,15 +45,25 @@ func TestPodStateError(t *testing.T) {
|
|||
{
|
||||
name: "pod with failed status",
|
||||
arg: status(corev1.PodFailed),
|
||||
expected: []kube.Diagnostic{
|
||||
{Category: "error", Message: "Pod 'pod_foo' in namespace 'k8s' has state: Failed. Pod state should be `Running`, `Pending` or `Succeeded`."},
|
||||
expected: []checks.Diagnostic{
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "Pod 'pod_foo' in namespace 'k8s' has state: Failed. Pod state should be `Running`, `Pending` or `Succeeded`.",
|
||||
Object: kube.Object{TypeInfo: GetTypeMeta(), ObjectInfo: GetObjectMeta()},
|
||||
Owners: GetOwners(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pod with unknown status",
|
||||
arg: status(corev1.PodUnknown),
|
||||
expected: []kube.Diagnostic{
|
||||
{Category: "error", Message: "Pod 'pod_foo' in namespace 'k8s' has state: Unknown. Pod state should be `Running`, `Pending` or `Succeeded`."},
|
||||
expected: []checks.Diagnostic{
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "Pod 'pod_foo' in namespace 'k8s' has state: Unknown. Pod state should be `Running`, `Pending` or `Succeeded`.",
|
||||
Object: kube.Object{TypeInfo: GetTypeMeta(), ObjectInfo: GetObjectMeta()},
|
||||
Owners: GetOwners(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -16,5 +16,5 @@ type Check interface {
|
|||
// Run runs this check on a set of Kubernetes objects. It can return
|
||||
// warnings (low-priority problems) and errors (high-priority problems) as
|
||||
// well as an error value indicating that the check failed to run.
|
||||
Run(*kube.Objects) ([]kube.Diagnostic, error)
|
||||
Run(*kube.Objects) ([]Diagnostic, error)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package checks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/digitalocean/clusterlint/kube"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// Diagnostic encapsulates the information each check returns.
|
||||
type Diagnostic struct {
|
||||
Severity Severity
|
||||
Message string
|
||||
Object kube.Object
|
||||
Owners []metav1.OwnerReference
|
||||
}
|
||||
|
||||
func (d *Diagnostic) String() string {
|
||||
return fmt.Sprintf("[%s] %s/%s/%s: %s", d.Severity, d.Object.ObjectInfo.Namespace,
|
||||
d.Object.TypeInfo.Kind, d.Object.ObjectInfo.Name, d.Message)
|
||||
}
|
||||
|
||||
type Severity string
|
||||
|
||||
const (
|
||||
Error Severity = "error"
|
||||
Warning Severity = "warning"
|
||||
Suggestion Severity = "suggestion"
|
||||
)
|
|
@ -33,12 +33,18 @@ func (nc *podSelectorCheck) Description() string {
|
|||
// Run runs this check on a set of Kubernetes objects. It can return warnings
|
||||
// (low-priority problems) and errors (high-priority problems) as well as an
|
||||
// error value indicating that the check failed to run.
|
||||
func (nc *podSelectorCheck) Run(objects *kube.Objects) ([]kube.Diagnostic, error) {
|
||||
var diagnostics []kube.Diagnostic
|
||||
func (nc *podSelectorCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
||||
var diagnostics []checks.Diagnostic
|
||||
for _, pod := range objects.Pods.Items {
|
||||
nodeSelectorMap := pod.Spec.NodeSelector
|
||||
if _, ok := nodeSelectorMap[corev1.LabelHostname]; ok {
|
||||
diagnostics = append(diagnostics, kube.Diagnostic{Category: "error", Message: fmt.Sprintf("pod '%s' in namespace '%s' uses the node name for node selector", pod.GetName(), pod.GetNamespace())})
|
||||
d := checks.Diagnostic{
|
||||
Severity: checks.Error,
|
||||
Message: fmt.Sprintf("Avoid node name label for node selector in pod: %s", pod.GetName()),
|
||||
Object: kube.Object{TypeInfo: &pod.TypeMeta, ObjectInfo: &pod.ObjectMeta},
|
||||
Owners: pod.ObjectMeta.GetOwnerReferences(),
|
||||
}
|
||||
diagnostics = append(diagnostics, d)
|
||||
}
|
||||
}
|
||||
return diagnostics, nil
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package doks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/clusterlint/checks"
|
||||
|
@ -29,7 +28,7 @@ func TestNodeNameError(t *testing.T) {
|
|||
scenarios := []struct {
|
||||
name string
|
||||
arg *kube.Objects
|
||||
expected []kube.Diagnostic
|
||||
expected []checks.Diagnostic
|
||||
}{
|
||||
{
|
||||
name: "no node name selector",
|
||||
|
@ -39,7 +38,7 @@ func TestNodeNameError(t *testing.T) {
|
|||
{
|
||||
name: "node name used in node selector",
|
||||
arg: invalidPod(),
|
||||
expected: errors(),
|
||||
expected: errors(invalidPod()),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -74,9 +73,15 @@ func invalidPod() *kube.Objects {
|
|||
return objs
|
||||
}
|
||||
|
||||
func errors() []kube.Diagnostic {
|
||||
diagnostics := []kube.Diagnostic{
|
||||
{Category: "error", Message: fmt.Sprintf("pod 'pod_foo' in namespace 'k8s' uses the node name for node selector")},
|
||||
func errors(objs *kube.Objects) []checks.Diagnostic {
|
||||
pod := objs.Pods.Items[0]
|
||||
diagnostics := []checks.Diagnostic{
|
||||
{
|
||||
Severity: checks.Error,
|
||||
Message: "Avoid node name label for node selector in pod: pod_foo",
|
||||
Object: kube.Object{TypeInfo: &pod.TypeMeta, ObjectInfo: &pod.ObjectMeta},
|
||||
Owners: pod.ObjectMeta.GetOwnerReferences(),
|
||||
},
|
||||
}
|
||||
return diagnostics
|
||||
}
|
||||
|
|
|
@ -30,6 +30,6 @@ func (nc *check) Description() string {
|
|||
// Run runs this check on a set of Kubernetes objects. It can return warnings
|
||||
// (low-priority problems) and errors (high-priority problems) as well as an
|
||||
// error value indicating that the check failed to run.
|
||||
func (nc *check) Run(*kube.Objects) ([]kube.Diagnostic, error) {
|
||||
func (nc *check) Run(*kube.Objects) ([]checks.Diagnostic, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -33,14 +33,12 @@ func (pc *privilegedContainerCheck) Description() string {
|
|||
// Run runs this check on a set of Kubernetes objects. It can return warnings
|
||||
// (low-priority problems) and errors (high-priority problems) as well as an
|
||||
// error value indicating that the check failed to run.
|
||||
func (pc *privilegedContainerCheck) Run(objects *kube.Objects) ([]kube.Diagnostic, error) {
|
||||
var diagnostics []kube.Diagnostic
|
||||
func (pc *privilegedContainerCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
||||
var diagnostics []checks.Diagnostic
|
||||
|
||||
for _, pod := range objects.Pods.Items {
|
||||
podName := pod.GetName()
|
||||
namespace := pod.GetNamespace()
|
||||
diagnostics = append(diagnostics, checkPrivileged(pod.Spec.Containers, podName, namespace)...)
|
||||
diagnostics = append(diagnostics, checkPrivileged(pod.Spec.InitContainers, podName, namespace)...)
|
||||
diagnostics = append(diagnostics, checkPrivileged(pod.Spec.Containers, pod)...)
|
||||
diagnostics = append(diagnostics, checkPrivileged(pod.Spec.InitContainers, pod)...)
|
||||
}
|
||||
|
||||
return diagnostics, nil
|
||||
|
@ -48,12 +46,18 @@ func (pc *privilegedContainerCheck) Run(objects *kube.Objects) ([]kube.Diagnosti
|
|||
|
||||
// checkPrivileged checks if the container is running in privileged mode
|
||||
// Adds a warning if it finds any privileged container
|
||||
func checkPrivileged(containers []corev1.Container, podName string, namespace string) []kube.Diagnostic {
|
||||
var d []kube.Diagnostic
|
||||
func checkPrivileged(containers []corev1.Container, pod corev1.Pod) []checks.Diagnostic {
|
||||
var diagnostics []checks.Diagnostic
|
||||
for _, container := range containers {
|
||||
if container.SecurityContext != nil && container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {
|
||||
d = append(d, kube.Diagnostic{Category: "warning", Message: fmt.Sprintf("[Best Practice] Privileged container '%s' found in pod '%s', namespace '%s'.", container.Name, podName, namespace)})
|
||||
d := checks.Diagnostic{
|
||||
Severity: checks.Warning,
|
||||
Message: fmt.Sprintf("[Best Practice] Privileged container '%s' found in pod '%s'", container.Name, pod.GetName()),
|
||||
Object: kube.Object{TypeInfo: &pod.TypeMeta, ObjectInfo: &pod.ObjectMeta},
|
||||
Owners: pod.ObjectMeta.GetOwnerReferences(),
|
||||
}
|
||||
diagnostics = append(diagnostics, d)
|
||||
}
|
||||
}
|
||||
return d
|
||||
return diagnostics
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package security
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/digitalocean/clusterlint/checks"
|
||||
|
@ -29,7 +28,7 @@ func TestPrivilegedContainerWarning(t *testing.T) {
|
|||
scenarios := []struct {
|
||||
name string
|
||||
arg *kube.Objects
|
||||
expected []kube.Diagnostic
|
||||
expected []checks.Diagnostic
|
||||
}{
|
||||
{
|
||||
name: "no pods",
|
||||
|
@ -39,7 +38,7 @@ func TestPrivilegedContainerWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with container in privileged mode",
|
||||
arg: container(true),
|
||||
expected: warnings(),
|
||||
expected: warnings(container(true)),
|
||||
},
|
||||
{
|
||||
name: "pod with container.SecurityContext = nil",
|
||||
|
@ -59,7 +58,7 @@ func TestPrivilegedContainerWarning(t *testing.T) {
|
|||
{
|
||||
name: "pod with init container in privileged mode",
|
||||
arg: initContainer(true),
|
||||
expected: warnings(),
|
||||
expected: warnings(initContainer(true)),
|
||||
},
|
||||
{
|
||||
name: "pod with initContainer.SecurityContext = nil",
|
||||
|
@ -94,6 +93,7 @@ func initPod() *kube.Objects {
|
|||
Pods: &corev1.PodList{
|
||||
Items: []corev1.Pod{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod_foo", Namespace: "k8s"},
|
||||
},
|
||||
},
|
||||
|
@ -172,9 +172,15 @@ func initContainerPrivilegedNil() *kube.Objects {
|
|||
return objs
|
||||
}
|
||||
|
||||
func warnings() []kube.Diagnostic {
|
||||
d := []kube.Diagnostic{
|
||||
{Category: "warning", Message: fmt.Sprintf("[Best Practice] Privileged container 'bar' found in pod 'pod_foo', namespace 'k8s'.")},
|
||||
func warnings(objs *kube.Objects) []checks.Diagnostic {
|
||||
pod := objs.Pods.Items[0]
|
||||
d := []checks.Diagnostic{
|
||||
{
|
||||
Severity: checks.Warning,
|
||||
Message: "[Best Practice] Privileged container 'bar' found in pod 'pod_foo'",
|
||||
Object: kube.Object{TypeInfo: &pod.TypeMeta, ObjectInfo: &pod.ObjectMeta},
|
||||
Owners: pod.ObjectMeta.GetOwnerReferences(),
|
||||
},
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -61,7 +61,7 @@ func main() {
|
|||
}
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
fmt.Printf("failed: %v", err)
|
||||
log.Printf("failed: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ func listChecks(c *cli.Context) error {
|
|||
group := c.String("group")
|
||||
allChecks := getChecks(group)
|
||||
for _, check := range allChecks {
|
||||
fmt.Printf("%s : %s\n", check.Name(), check.Description())
|
||||
log.Printf("%s : %s\n", check.Name(), check.Description())
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -102,7 +102,7 @@ func runChecks(c *cli.Context) error {
|
|||
// runs all checks in the registry if group is not specified
|
||||
func runChecksForGroup(group string, objects *kube.Objects) error {
|
||||
allChecks := getChecks(group)
|
||||
var diagnostics []kube.Diagnostic
|
||||
var diagnostics []checks.Diagnostic
|
||||
var mu sync.Mutex
|
||||
var g errgroup.Group
|
||||
|
||||
|
@ -136,22 +136,20 @@ func runCheck(name string, objects *kube.Objects) error {
|
|||
|
||||
log.Println("Running check: ", name)
|
||||
diagnostics, err := check.Run(objects)
|
||||
showDiagnostics(diagnostics)
|
||||
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return showDiagnostics(diagnostics)
|
||||
}
|
||||
|
||||
// showErrorsAndWarnings displays all the errors and warnings returned by checks
|
||||
func showDiagnostics(diagnostics []kube.Diagnostic) {
|
||||
for _, diagnostic := range diagnostics {
|
||||
log.Printf("[%s] %s\n", diagnostic.Category, diagnostic.Message)
|
||||
if len(diagnostic.Metadata) > 0 {
|
||||
log.Println("Object Meta Information: ")
|
||||
for key, value := range diagnostic.Metadata {
|
||||
log.Printf("%s: %s\n", key, value)
|
||||
}
|
||||
}
|
||||
func showDiagnostics(diagnostics []checks.Diagnostic) error {
|
||||
resp, err := json.Marshal(diagnostics)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println(string(resp))
|
||||
return nil
|
||||
}
|
||||
|
||||
// getChecks retrieves all checks within given group
|
||||
|
|
|
@ -25,19 +25,16 @@ type Objects struct {
|
|||
LimitRanges *corev1.LimitRangeList
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
TypeInfo *metav1.TypeMeta
|
||||
ObjectInfo *metav1.ObjectMeta
|
||||
}
|
||||
|
||||
// Client encapsulates a client for a Kubernetes cluster.
|
||||
type Client struct {
|
||||
kubeClient kubernetes.Interface
|
||||
}
|
||||
|
||||
// Diagnostic encapsulates the information each check returns.
|
||||
type Diagnostic struct {
|
||||
Category string
|
||||
Message string
|
||||
|
||||
Metadata map[string]string
|
||||
}
|
||||
|
||||
// FetchObjects returns the objects from a Kubernetes cluster.
|
||||
func (c *Client) FetchObjects() (*Objects, error) {
|
||||
const all = ""
|
||||
|
|
Loading…
Reference in New Issue