Check for config map references in nodes and projected volumes
parent
2d097ba31a
commit
d0eb5a4b0a
|
@ -277,7 +277,6 @@ Description: This check reports all the persistent volume claims in the cluster
|
||||||
How to fix:
|
How to fix:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
<<<<<<< HEAD
|
|
||||||
kubectl delete pvc <unused pvc>
|
kubectl delete pvc <unused pvc>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -27,28 +27,23 @@ func (c *unusedClaimCheck) Description() string {
|
||||||
return "Check if there are unused persistent volume claims in the cluster"
|
return "Check if there are unused persistent volume claims in the cluster"
|
||||||
}
|
}
|
||||||
|
|
||||||
type identifier struct {
|
|
||||||
Name string
|
|
||||||
Namespace string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run runs this check on a set of Kubernetes objects. It can return warnings
|
// 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
|
// (low-priority problems) and errors (high-priority problems) as well as an
|
||||||
// error value indicating that the check failed to run.
|
// error value indicating that the check failed to run.
|
||||||
func (c *unusedClaimCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
func (c *unusedClaimCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
||||||
var diagnostics []checks.Diagnostic
|
var diagnostics []checks.Diagnostic
|
||||||
used := make(map[identifier]bool)
|
used := make(map[kube.Identifier]bool)
|
||||||
for _, pod := range objects.Pods.Items {
|
for _, pod := range objects.Pods.Items {
|
||||||
for _, volume := range pod.Spec.Volumes {
|
for _, volume := range pod.Spec.Volumes {
|
||||||
claim := volume.VolumeSource.PersistentVolumeClaim
|
claim := volume.VolumeSource.PersistentVolumeClaim
|
||||||
if claim != nil {
|
if claim != nil {
|
||||||
used[identifier{Name: claim.ClaimName, Namespace: pod.GetNamespace()}] = true
|
used[kube.Identifier{Name: claim.ClaimName, Namespace: pod.GetNamespace()}] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, claim := range objects.PersistentVolumeClaims.Items {
|
for _, claim := range objects.PersistentVolumeClaims.Items {
|
||||||
if _, ok := used[identifier{Name: claim.GetName(), Namespace: claim.GetNamespace()}]; !ok {
|
if _, ok := used[kube.Identifier{Name: claim.GetName(), Namespace: claim.GetNamespace()}]; !ok {
|
||||||
d := checks.Diagnostic{
|
d := checks.Diagnostic{
|
||||||
Severity: checks.Warning,
|
Severity: checks.Warning,
|
||||||
Message: "Unused persistent volume claim",
|
Message: "Unused persistent volume claim",
|
||||||
|
|
|
@ -15,11 +15,6 @@ func init() {
|
||||||
|
|
||||||
type unusedCMCheck struct{}
|
type unusedCMCheck struct{}
|
||||||
|
|
||||||
type identifier struct {
|
|
||||||
Name string
|
|
||||||
Namespace string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns a unique name for this check.
|
// Name returns a unique name for this check.
|
||||||
func (c *unusedCMCheck) Name() string {
|
func (c *unusedCMCheck) Name() string {
|
||||||
return "unused-config-map"
|
return "unused-config-map"
|
||||||
|
@ -42,13 +37,22 @@ func (c *unusedCMCheck) Description() string {
|
||||||
func (c *unusedCMCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
func (c *unusedCMCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error) {
|
||||||
var diagnostics []checks.Diagnostic
|
var diagnostics []checks.Diagnostic
|
||||||
|
|
||||||
used, err := checkReferences(objects)
|
used, err := checkPodReferences(objects)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodeRefs, err := checkNodeReferences(objects)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range nodeRefs {
|
||||||
|
used[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
for _, cm := range objects.ConfigMaps.Items {
|
for _, cm := range objects.ConfigMaps.Items {
|
||||||
if _, ok := used[identifier{Name: cm.GetName(), Namespace: cm.GetNamespace()}]; !ok {
|
if _, ok := used[kube.Identifier{Name: cm.GetName(), Namespace: cm.GetNamespace()}]; !ok {
|
||||||
cm := cm
|
cm := cm
|
||||||
d := checks.Diagnostic{
|
d := checks.Diagnostic{
|
||||||
Severity: checks.Warning,
|
Severity: checks.Warning,
|
||||||
|
@ -63,9 +67,28 @@ func (c *unusedCMCheck) Run(objects *kube.Objects) ([]checks.Diagnostic, error)
|
||||||
return diagnostics, nil
|
return diagnostics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//checkReferences checks each pod for config map references in volumes and environment variables
|
func checkNodeReferences(objects *kube.Objects) (map[kube.Identifier]bool, error) {
|
||||||
func checkReferences(objects *kube.Objects) (map[identifier]bool, error) {
|
used := make(map[kube.Identifier]bool)
|
||||||
used := make(map[identifier]bool)
|
var mu sync.Mutex
|
||||||
|
var g errgroup.Group
|
||||||
|
for _, node := range objects.Nodes.Items {
|
||||||
|
node := node
|
||||||
|
g.Go(func() error {
|
||||||
|
source := node.Spec.ConfigSource
|
||||||
|
if source != nil {
|
||||||
|
mu.Lock()
|
||||||
|
used[kube.Identifier{Name: source.ConfigMap.Name, Namespace: source.ConfigMap.Namespace}] = true
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return used, g.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
//checkPodReferences checks each pod for config map references in volumes and environment variables
|
||||||
|
func checkPodReferences(objects *kube.Objects) (map[kube.Identifier]bool, error) {
|
||||||
|
used := make(map[kube.Identifier]bool)
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
var g errgroup.Group
|
var g errgroup.Group
|
||||||
for _, pod := range objects.Pods.Items {
|
for _, pod := range objects.Pods.Items {
|
||||||
|
@ -76,9 +99,19 @@ func checkReferences(objects *kube.Objects) (map[identifier]bool, error) {
|
||||||
cm := volume.VolumeSource.ConfigMap
|
cm := volume.VolumeSource.ConfigMap
|
||||||
if cm != nil {
|
if cm != nil {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
used[identifier{Name: cm.LocalObjectReference.Name, Namespace: namespace}] = true
|
used[kube.Identifier{Name: cm.LocalObjectReference.Name, Namespace: namespace}] = true
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
}
|
}
|
||||||
|
if volume.VolumeSource.Projected != nil {
|
||||||
|
for _, source := range volume.VolumeSource.Projected.Sources {
|
||||||
|
cm := source.ConfigMap
|
||||||
|
if cm != nil {
|
||||||
|
mu.Lock()
|
||||||
|
used[kube.Identifier{Name: cm.LocalObjectReference.Name, Namespace: namespace}] = true
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
identifiers := checkEnvVars(pod.Spec.Containers, namespace)
|
identifiers := checkEnvVars(pod.Spec.Containers, namespace)
|
||||||
identifiers = append(identifiers, checkEnvVars(pod.Spec.InitContainers, namespace)...)
|
identifiers = append(identifiers, checkEnvVars(pod.Spec.InitContainers, namespace)...)
|
||||||
|
@ -96,12 +129,12 @@ func checkReferences(objects *kube.Objects) (map[identifier]bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkEnvVars checks for config map references in container environment variables
|
// checkEnvVars checks for config map references in container environment variables
|
||||||
func checkEnvVars(containers []corev1.Container, namespace string) []identifier {
|
func checkEnvVars(containers []corev1.Container, namespace string) []kube.Identifier {
|
||||||
var refs []identifier
|
var refs []kube.Identifier
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
for _, env := range container.EnvFrom {
|
for _, env := range container.EnvFrom {
|
||||||
if env.ConfigMapRef != nil {
|
if env.ConfigMapRef != nil {
|
||||||
refs = append(refs, identifier{Name: env.ConfigMapRef.LocalObjectReference.Name, Namespace: namespace})
|
refs = append(refs, kube.Identifier{Name: env.ConfigMapRef.LocalObjectReference.Name, Namespace: namespace})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ func TestUnusedConfigMapWarning(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no config maps",
|
name: "no config maps",
|
||||||
objs: &kube.Objects{Pods: &corev1.PodList{}, ConfigMaps: &corev1.ConfigMapList{}},
|
objs: &kube.Objects{Nodes: &corev1.NodeList{}, Pods: &corev1.PodList{}, ConfigMaps: &corev1.ConfigMapList{}},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -43,10 +43,20 @@ func TestUnusedConfigMapWarning(t *testing.T) {
|
||||||
expected: nil,
|
expected: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "environment variable references to config map",
|
name: "environment variable references config map",
|
||||||
objs: configMapEnvSource(),
|
objs: configMapEnvSource(),
|
||||||
expected: nil,
|
expected: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "projected volume references config map",
|
||||||
|
objs: projectedVolume(),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "node config source references config map",
|
||||||
|
objs: nodeConfigSource(),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "unused config map",
|
name: "unused config map",
|
||||||
objs: initConfigMap(),
|
objs: initConfigMap(),
|
||||||
|
@ -75,6 +85,14 @@ func TestUnusedConfigMapWarning(t *testing.T) {
|
||||||
|
|
||||||
func initConfigMap() *kube.Objects {
|
func initConfigMap() *kube.Objects {
|
||||||
objs := &kube.Objects{
|
objs := &kube.Objects{
|
||||||
|
Nodes: &corev1.NodeList{
|
||||||
|
Items: []corev1.Node{
|
||||||
|
{
|
||||||
|
TypeMeta: metav1.TypeMeta{Kind: "Node", APIVersion: "v1"},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "node_foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
Pods: &corev1.PodList{
|
Pods: &corev1.PodList{
|
||||||
Items: []corev1.Pod{
|
Items: []corev1.Pod{
|
||||||
{
|
{
|
||||||
|
@ -95,6 +113,19 @@ func initConfigMap() *kube.Objects {
|
||||||
return objs
|
return objs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nodeConfigSource() *kube.Objects {
|
||||||
|
objs := initConfigMap()
|
||||||
|
objs.Nodes.Items[0].Spec = corev1.NodeSpec{
|
||||||
|
ConfigSource: &corev1.NodeConfigSource{
|
||||||
|
ConfigMap: &corev1.ConfigMapNodeConfigSource{
|
||||||
|
Name: "cm_foo",
|
||||||
|
Namespace: cmNamespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return objs
|
||||||
|
}
|
||||||
|
|
||||||
func configMapVolume() *kube.Objects {
|
func configMapVolume() *kube.Objects {
|
||||||
objs := initConfigMap()
|
objs := initConfigMap()
|
||||||
objs.Pods.Items[0].Spec = corev1.PodSpec{
|
objs.Pods.Items[0].Spec = corev1.PodSpec{
|
||||||
|
@ -111,6 +142,28 @@ func configMapVolume() *kube.Objects {
|
||||||
return objs
|
return objs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func projectedVolume() *kube.Objects {
|
||||||
|
objs := initConfigMap()
|
||||||
|
objs.Pods.Items[0].Spec = corev1.PodSpec{
|
||||||
|
Volumes: []corev1.Volume{
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
VolumeSource: corev1.VolumeSource{
|
||||||
|
Projected: &corev1.ProjectedVolumeSource{
|
||||||
|
Sources: []corev1.VolumeProjection{
|
||||||
|
{
|
||||||
|
ConfigMap: &corev1.ConfigMapProjection{
|
||||||
|
LocalObjectReference: corev1.LocalObjectReference{Name: "cm_foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
return objs
|
||||||
|
}
|
||||||
|
|
||||||
func configMapEnvSource() *kube.Objects {
|
func configMapEnvSource() *kube.Objects {
|
||||||
objs := initConfigMap()
|
objs := initConfigMap()
|
||||||
objs.Pods.Items[0].Spec = corev1.PodSpec{
|
objs.Pods.Items[0].Spec = corev1.PodSpec{
|
||||||
|
|
|
@ -9,6 +9,12 @@ import (
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//Identifier is used to identify a specific namspace scoped object.
|
||||||
|
type Identifier struct {
|
||||||
|
Name string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
// Objects encapsulates all the objects from a Kubernetes cluster.
|
// Objects encapsulates all the objects from a Kubernetes cluster.
|
||||||
type Objects struct {
|
type Objects struct {
|
||||||
Nodes *corev1.NodeList
|
Nodes *corev1.NodeList
|
||||||
|
|
Loading…
Reference in New Issue