Skip to content

Commit

Permalink
Cut out logic specific to controller types
Browse files Browse the repository at this point in the history
  • Loading branch information
baderbuddy committed Mar 17, 2020
1 parent 7fdebfc commit 3c68527
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 200 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ require (
google.golang.org/grpc v1.20.1
gopkg.in/inf.v0 v0.9.1
gopkg.in/yaml.v2 v2.2.2
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
k8s.io/api v0.0.0-20181213150558-05914d821849
k8s.io/apimachinery v0.0.0-20181127025237-2b1284ed4c93
k8s.io/client-go v0.0.0-20181213151034-8d9ed539ba31
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
Expand Down
232 changes: 48 additions & 184 deletions pkg/kube/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package kube

import (
"bytes"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -11,9 +10,7 @@ import (
"time"

"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
"gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -27,21 +24,15 @@ import (

// ResourceProvider contains k8s resources to be audited
type ResourceProvider struct {
ServerVersion string
CreationTime time.Time
SourceName string
SourceType string
Nodes []corev1.Node
Deployments []appsv1.Deployment
StatefulSets []appsv1.StatefulSet
DaemonSets []appsv1.DaemonSet
Jobs []batchv1.Job
CronJobs []batchv1beta1.CronJob
ReplicationControllers []corev1.ReplicationController
Namespaces []corev1.Namespace
Pods []corev1.Pod
DynamicClient *dynamic.Interface
RestMapper *meta.RESTMapper
ServerVersion string
CreationTime time.Time
SourceName string
SourceType string
Nodes []corev1.Node
Namespaces []corev1.Namespace
Pods []corev1.Pod
DynamicClient *dynamic.Interface
RestMapper *meta.RESTMapper
}

type k8sResource struct {
Expand All @@ -59,18 +50,12 @@ func CreateResourceProvider(directory string) (*ResourceProvider, error) {
// CreateResourceProviderFromPath returns a new ResourceProvider using the YAML files in a directory
func CreateResourceProviderFromPath(directory string) (*ResourceProvider, error) {
resources := ResourceProvider{
ServerVersion: "unknown",
SourceType: "Path",
SourceName: directory,
Nodes: []corev1.Node{},
Deployments: []appsv1.Deployment{},
StatefulSets: []appsv1.StatefulSet{},
DaemonSets: []appsv1.DaemonSet{},
Jobs: []batchv1.Job{},
CronJobs: []batchv1beta1.CronJob{},
ReplicationControllers: []corev1.ReplicationController{},
Namespaces: []corev1.Namespace{},
Pods: []corev1.Pod{},
ServerVersion: "unknown",
SourceType: "Path",
SourceName: directory,
Nodes: []corev1.Node{},
Namespaces: []corev1.Namespace{},
Pods: []corev1.Pod{},
}

addYaml := func(contents string) error {
Expand Down Expand Up @@ -135,11 +120,7 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string
logrus.Errorf("Error fetching Cluster API version: %v", err)
return nil, err
}
jobs, err := kube.BatchV1().Jobs("").List(listOpts)
if err != nil {
logrus.Errorf("Error fetching Jobs: %v", err)
return nil, err
}

nodes, err := kube.CoreV1().Nodes().List(listOpts)
if err != nil {
logrus.Errorf("Error fetching Nodes: %v", err)
Expand Down Expand Up @@ -168,7 +149,6 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string
SourceType: "Cluster",
SourceName: clusterName,
CreationTime: time.Now(),
Jobs: jobs.Items,
Nodes: nodes.Items,
Namespaces: namespaces.Items,
Pods: pods.Items,
Expand All @@ -178,172 +158,56 @@ func CreateResourceProviderFromAPI(kube kubernetes.Interface, clusterName string
return &api, nil
}

func getPodSpec(yaml map[string]interface{}) interface{} {
if childYaml, ok := yaml["spec"]; ok {
return getPodSpec(childYaml.(map[string]interface{}))
}
if childYaml, ok := yaml["template"]; ok {
return getPodSpec(childYaml.(map[string]interface{}))
}
return yaml
}

func addResourceFromString(contents string, resources *ResourceProvider) error {
contentBytes := []byte(contents)
decoder := k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
resource := k8sResource{}
err := decoder.Decode(&resource)
decoder = k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)

if err != nil {
logrus.Errorf("Invalid YAML: %s", string(contents))
return err
}
decoder = k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(contentBytes), 1000)
if resource.Kind == "Deployment" {
controller := appsv1.Deployment{}
err = decoder.Decode(&controller)
resources.Deployments = append(resources.Deployments, controller)
} else if resource.Kind == "StatefulSet" {
controller := appsv1.StatefulSet{}
err = decoder.Decode(&controller)
resources.StatefulSets = append(resources.StatefulSets, controller)
} else if resource.Kind == "DaemonSet" {
controller := appsv1.DaemonSet{}
err = decoder.Decode(&controller)
resources.DaemonSets = append(resources.DaemonSets, controller)
} else if resource.Kind == "Job" {
controller := batchv1.Job{}
err = decoder.Decode(&controller)
resources.Jobs = append(resources.Jobs, controller)
} else if resource.Kind == "CronJob" {
controller := batchv1beta1.CronJob{}
err = decoder.Decode(&controller)
resources.CronJobs = append(resources.CronJobs, controller)
} else if resource.Kind == "ReplicationController" {
controller := corev1.ReplicationController{}
err = decoder.Decode(&controller)
resources.ReplicationControllers = append(resources.ReplicationControllers, controller)
} else if resource.Kind == "Namespace" {
if resource.Kind == "Namespace" {
ns := corev1.Namespace{}
err = decoder.Decode(&ns)
resources.Namespaces = append(resources.Namespaces, ns)
} else if resource.Kind == "Pod" {
pod := corev1.Pod{}
err = decoder.Decode(&pod)
resources.Pods = append(resources.Pods, pod)
}
if err != nil {
logrus.Errorf("Error parsing %s: %v", resource.Kind, err)
return err
}
return nil
}

func getDeployments(kube kubernetes.Interface) ([]appsv1.Deployment, error) {
listOpts := metav1.ListOptions{}
deployList, err := kube.AppsV1().Deployments("").List(listOpts)
if err != nil {
logrus.Errorf("Error fetching Deployments: %v", err)
return nil, err
}
deploys := deployList.Items

oldDeploys := make([]interface{}, 0)
deploysV1B1, err := kube.AppsV1beta1().Deployments("").List(listOpts)
if err != nil {
logrus.Errorf("Error fetching Deployments v1beta1: %v", err)
return nil, err
}
for _, oldDeploy := range deploysV1B1.Items {
oldDeploys = append(oldDeploys, oldDeploy)
}
deploysV1B2, err := kube.AppsV1beta2().Deployments("").List(listOpts)
if err != nil {
logrus.Errorf("Error fetching Deployments v1beta2: %v", err)
return nil, err
}
for _, oldDeploy := range deploysV1B2.Items {
oldDeploys = append(oldDeploys, oldDeploy)
}

for _, oldDeploy := range oldDeploys {
str, err := json.Marshal(oldDeploy)
if err != nil {
logrus.Errorf("Error marshaling old deployment version: %v", err)
return nil, err
}
deploy := appsv1.Deployment{}
err = json.Unmarshal(str, &deploy)
} else {
yamlNode := make(map[string]interface{})
err = yaml.Unmarshal(contentBytes, &yamlNode)
if err != nil {
logrus.Errorf("Error unmarshaling old deployment version: %v", err)
return nil, err
}
deploys = append(deploys, deploy)
}
return deploys, nil
}

func getStatefulSets(kube kubernetes.Interface) ([]appsv1.StatefulSet, error) {
listOpts := metav1.ListOptions{}
controllerList, err := kube.AppsV1().StatefulSets("").List(listOpts)
if err != nil {
logrus.Errorf("Error fetching StatefulSets: %v", err)
return nil, err
}
controllers := controllerList.Items

oldControllers := make([]interface{}, 0)
controllersV1B1, err := kube.AppsV1beta1().StatefulSets("").List(listOpts)
if err != nil {
logrus.Errorf("Error fetching StatefulSets v1beta1: %v", err)
return nil, err
}
for _, oldController := range controllersV1B1.Items {
oldControllers = append(oldControllers, oldController)
}
controllersV1B2, err := kube.AppsV1beta2().StatefulSets("").List(listOpts)
if err != nil {
logrus.Errorf("Error fetching StatefulSets v1beta2: %v", err)
return nil, err
}
for _, oldController := range controllersV1B2.Items {
oldControllers = append(oldControllers, oldController)
}

for _, oldController := range oldControllers {
str, err := json.Marshal(oldController)
if err != nil {
logrus.Errorf("Error marshaling old StatefulSet version: %v", err)
return nil, err
}
controller := appsv1.StatefulSet{}
err = json.Unmarshal(str, &controller)
if err != nil {
logrus.Errorf("Error unmarshaling old StatefulSet version: %v", err)
return nil, err
}
controllers = append(controllers, controller)
}
return controllers, nil
}

func getDaemonSets(kube kubernetes.Interface) ([]appsv1.DaemonSet, error) {
listOpts := metav1.ListOptions{}
controllerList, err := kube.AppsV1().DaemonSets("").List(listOpts)
if err != nil {
logrus.Errorf("Error fetching DaemonSets: %v", err)
return nil, err
}
controllers := controllerList.Items

controllersV1B2, err := kube.AppsV1beta2().DaemonSets("").List(listOpts)
if err != nil {
logrus.Errorf("Error fetching DaemonSets v1beta2: %v", err)
return nil, err
}

for _, oldController := range controllersV1B2.Items {
str, err := json.Marshal(oldController)
if err != nil {
logrus.Errorf("Error marshaling old DaemonSet version: %v", err)
return nil, err
logrus.Errorf("Invalid YAML: %s", string(contents))
return err
}
controller := appsv1.DaemonSet{}
err = json.Unmarshal(str, &controller)
finalDoc := make(map[string]interface{})
finalDoc["metadata"] = yamlNode["metadata"]
finalDoc["apiVersion"] = "v1"
finalDoc["kind"] = "Pod"
finalDoc["spec"] = getPodSpec(yamlNode)
marshelledYaml, err := yaml.Marshal(finalDoc)
if err != nil {
logrus.Errorf("Error unmarshaling old DaemonSet version: %v", err)
return nil, err
logrus.Errorf("Could not marshell yaml: %v", err)
return err
}
controllers = append(controllers, controller)
decoder := k8sYaml.NewYAMLOrJSONDecoder(bytes.NewReader(marshelledYaml), 1000)
pod := corev1.Pod{}
err = decoder.Decode(&pod)
resources.Pods = append(resources.Pods, pod)
}
return controllers, nil
return err
}
20 changes: 4 additions & 16 deletions pkg/kube/resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,10 @@ func TestGetResourcesFromPath(t *testing.T) {

assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")

assert.Equal(t, 1, len(resources.Deployments), "Should have a deployment")
assert.Equal(t, "ubuntu", resources.Deployments[0].Spec.Template.Spec.Containers[0].Name)

assert.Equal(t, 1, len(resources.StatefulSets), "Should have a stateful set")
assert.Equal(t, "nginx", resources.StatefulSets[0].Spec.Template.Spec.Containers[0].Name)

assert.Equal(t, 1, len(resources.Namespaces), "Should have a namespace")
assert.Equal(t, "two", resources.Namespaces[0].ObjectMeta.Name)

assert.Equal(t, 2, len(resources.Pods), "Should have two pods")
assert.Equal(t, 8, len(resources.Pods), "Should have two pods")
assert.Equal(t, "", resources.Pods[0].ObjectMeta.Namespace, "Should have one pod in default namespace")
assert.Equal(t, "two", resources.Pods[1].ObjectMeta.Namespace, "Should have one pod in namespace 'two'")

assert.Equal(t, "two", resources.Pods[5].ObjectMeta.Namespace, "Should have one pod in namespace 'two'")
}

func TestGetMultipleResourceFromSingleFile(t *testing.T) {
Expand All @@ -46,10 +38,6 @@ func TestGetMultipleResourceFromSingleFile(t *testing.T) {

assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")

assert.Equal(t, 1, len(resources.Deployments), "Should have a deployment")
assert.Equal(t, "dashboard", resources.Deployments[0].Spec.Template.Spec.Containers[0].Name)

assert.Equal(t, 2, len(resources.Namespaces), "Should have a namespace")
assert.Equal(t, "polaris", resources.Namespaces[0].ObjectMeta.Name)
assert.Equal(t, "polaris-2", resources.Namespaces[1].ObjectMeta.Name)
}
Expand All @@ -70,6 +58,6 @@ func TestGetResourceFromAPI(t *testing.T) {
assert.IsType(t, time.Now(), resources.CreationTime, "Creation time should be set")

assert.Equal(t, 0, len(resources.Nodes), "Should not have any nodes")
assert.Equal(t, 0, len(resources.Pods), "Should have a pod")
assert.Equal(t, 1, len(resources.Pods), "Should have a pod")

}

0 comments on commit 3c68527

Please sign in to comment.