From 55ef5fa645b76d2ffef090233d7da0eb75232eef Mon Sep 17 00:00:00 2001 From: Genevieve Luyt <11131143+genevieveluyt@users.noreply.github.com> Date: Thu, 25 Nov 2021 10:21:22 -0500 Subject: [PATCH] Test IncludeGenerated (#381) --- cmd/commands/VERSION | 2 +- internal/k8sinternal/client_test.go | 76 ++++++++++++++++--- .../fixtures/include-generated.yml | 16 ++++ internal/test/fixtures/service.yml | 4 +- internal/test/test.go | 23 +++--- pkg/k8s/type_checks.go | 5 ++ 6 files changed, 102 insertions(+), 24 deletions(-) create mode 100644 internal/k8sinternal/fixtures/include-generated.yml diff --git a/cmd/commands/VERSION b/cmd/commands/VERSION index a5510516..04a373ef 100644 --- a/cmd/commands/VERSION +++ b/cmd/commands/VERSION @@ -1 +1 @@ -0.15.0 +0.16.0 diff --git a/internal/k8sinternal/client_test.go b/internal/k8sinternal/client_test.go index 99267a25..8ac1629a 100644 --- a/internal/k8sinternal/client_test.go +++ b/internal/k8sinternal/client_test.go @@ -1,12 +1,15 @@ -package k8sinternal +package k8sinternal_test import ( "errors" "testing" + "github.com/Shopify/kubeaudit/internal/k8sinternal" + "github.com/Shopify/kubeaudit/internal/test" "github.com/Shopify/kubeaudit/pkg/k8s" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" runtime "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/version" fakediscovery "k8s.io/client-go/discovery/fake" @@ -29,11 +32,11 @@ func (kc *MockK8sClient) InClusterConfig() (*rest.Config, error) { func TestKubeClientConfigLocal(t *testing.T) { assert := assert.New(t) - _, err := NewKubeClientLocal("/notarealfile") - assert.Equal(ErrNoReadableKubeConfig, err) + _, err := k8sinternal.NewKubeClientLocal("/notarealfile") + assert.Equal(k8sinternal.ErrNoReadableKubeConfig, err) - _, err = NewKubeClientLocal("client.go") - assert.NotEqual(ErrNoReadableKubeConfig, err) + _, err = k8sinternal.NewKubeClientLocal("client.go") + assert.NotEqual(k8sinternal.ErrNoReadableKubeConfig, err) assert.NotNil(err) } @@ -43,13 +46,13 @@ func TestKubeClientConfigCluster(t *testing.T) { client := &MockK8sClient{} var config *rest.Config = nil client.On("InClusterConfig").Return(config, errors.New("mock error")) - clientset, err := NewKubeClientCluster(client) + clientset, err := k8sinternal.NewKubeClientCluster(client) assert.Nil(clientset) assert.NotNil(err) client = &MockK8sClient{} client.On("InClusterConfig").Return(&rest.Config{}, nil) - clientset, err = NewKubeClientCluster(client) + clientset, err = k8sinternal.NewKubeClientCluster(client) assert.NotNil(clientset) assert.NoError(err) } @@ -60,11 +63,11 @@ func TestIsRunningInCluster(t *testing.T) { client := &MockK8sClient{} var config *rest.Config = nil client.On("InClusterConfig").Return(config, errors.New("mock error")) - assert.False(IsRunningInCluster(client)) + assert.False(k8sinternal.IsRunningInCluster(client)) client = &MockK8sClient{} client.On("InClusterConfig").Return(&rest.Config{}, nil) - assert.True(IsRunningInCluster(client)) + assert.True(k8sinternal.IsRunningInCluster(client)) } func TestGetAllResources(t *testing.T) { @@ -94,13 +97,17 @@ func TestGetAllResources(t *testing.T) { } clientset := fakeclientset.NewSimpleClientset(resources...) - assert.Len(t, GetAllResources(clientset, ClientOptions{}), len(resourceTemplates)*len(namespaces)) + assert.Len(t, k8sinternal.GetAllResources(clientset, k8sinternal.ClientOptions{}), len(resourceTemplates)*len(namespaces)) // Because field selectors are handled server-side, the fake clientset does not support them // which means the Namespace resources don't get filtered (this is not a problem when using // a real clientset) // See https://github.com/kubernetes/client-go/issues/326 - assert.Len(t, GetAllResources(clientset, ClientOptions{Namespace: namespaces[0]}), len(resourceTemplates)+(len(namespaces)-1)) + assert.Len( + t, + k8sinternal.GetAllResources(clientset, k8sinternal.ClientOptions{Namespace: namespaces[0]}), + len(resourceTemplates)+(len(namespaces)-1), + ) } func setNamespace(resource k8s.Resource, namespace string) { @@ -125,7 +132,52 @@ func TestGetKubernetesVersion(t *testing.T) { Platform: "ACME 8-bit", } - r, err := GetKubernetesVersion(client) + r, err := k8sinternal.GetKubernetesVersion(client) assert.Nil(t, err) assert.EqualValues(t, *fakeDiscovery.FakedServerVersion, *r) } + +func TestIncludeGenerated(t *testing.T) { + // The "IncludeGenerated" option only applies to local and cluster mode + if !test.UseKind() { + return + } + + namespace := "include-generated" + defer test.DeleteNamespace(t, namespace) + test.CreateNamespace(t, namespace) + test.ApplyManifest(t, "./fixtures/include-generated.yml", namespace) + + clientset, err := k8sinternal.NewKubeClientLocal("") + require.NoError(t, err) + + // Test IncludeGenerated = false + resources := k8sinternal.GetAllResources( + clientset, + k8sinternal.ClientOptions{Namespace: namespace, IncludeGenerated: false}, + ) + assert.False(t, hasPod(resources), "Expected no pods for IncludeGenerated=false") + + // Test IncludeGenerated unspecified defaults to false + resources = k8sinternal.GetAllResources( + clientset, + k8sinternal.ClientOptions{Namespace: namespace}, + ) + assert.False(t, hasPod(resources), "Expected no pods if IncludeGenerated is unspecified (ie. default to false)") + + // Test IncludeGenerated = true + resources = k8sinternal.GetAllResources( + clientset, + k8sinternal.ClientOptions{Namespace: namespace, IncludeGenerated: true}, + ) + assert.True(t, hasPod(resources), "Expected pods for IncludeGenerated=true") +} + +func hasPod(resources []k8s.Resource) bool { + for _, resource := range resources { + if k8s.IsPodV1(resource) { + return true + } + } + return false +} diff --git a/internal/k8sinternal/fixtures/include-generated.yml b/internal/k8sinternal/fixtures/include-generated.yml new file mode 100644 index 00000000..b13e340b --- /dev/null +++ b/internal/k8sinternal/fixtures/include-generated.yml @@ -0,0 +1,16 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment +spec: + selector: + matchLabels: + name: deployment + template: + metadata: + labels: + name: deployment + spec: + containers: + - name: container + image: scratch diff --git a/internal/test/fixtures/service.yml b/internal/test/fixtures/service.yml index c700dd39..1340e366 100644 --- a/internal/test/fixtures/service.yml +++ b/internal/test/fixtures/service.yml @@ -9,5 +9,5 @@ spec: - protocol: TCP port: 80 targetPort: 9376 - clusterIP: 10.0.171.239 - type: LoadBalancer \ No newline at end of file + clusterIP: 10.96.0.1 + type: LoadBalancer diff --git a/internal/test/test.go b/internal/test/test.go index 0bcf9d09..60d7aae2 100644 --- a/internal/test/test.go +++ b/internal/test/test.go @@ -31,7 +31,7 @@ func AuditLocal(t *testing.T, fixtureDir, fixture string, auditable kubeaudit.Au } func AuditMultiple(t *testing.T, fixtureDir, fixture string, auditables []kubeaudit.Auditable, expectedErrors []string, namespace string, mode string) { - if mode == LOCAL_MODE && os.Getenv("USE_KIND") == "false" { + if mode == LOCAL_MODE && !UseKind() { return } @@ -104,13 +104,13 @@ func GetReport(t *testing.T, fixtureDir, fixture string, auditables []kubeaudit. var report *kubeaudit.Report switch mode { case MANIFEST_MODE: - manifest, err := os.Open(fixture) - require.NoError(err) + manifest, openErr := os.Open(fixture) + require.NoError(openErr) report, err = auditor.AuditManifest(manifest) case LOCAL_MODE: - defer deleteNamespace(t, namespace) - createNamespace(t, namespace) - applyManifest(t, fixture, namespace) + defer DeleteNamespace(t, namespace) + CreateNamespace(t, namespace) + ApplyManifest(t, fixture, namespace) report, err = auditor.AuditLocal("", k8sinternal.ClientOptions{Namespace: namespace}) } @@ -133,17 +133,22 @@ func GetAllFileNames(t *testing.T, directory string) []string { return fileNames } -func applyManifest(t *testing.T, manifestPath, namespace string) { +// UseKind returns true if tests which utilize Kind should run +func UseKind() bool { + return os.Getenv("USE_KIND") != "false" +} + +func ApplyManifest(t *testing.T, manifestPath, namespace string) { t.Helper() runCmd(t, exec.Command("kubectl", "apply", "-f", manifestPath, "-n", namespace)) } -func createNamespace(t *testing.T, namespace string) { +func CreateNamespace(t *testing.T, namespace string) { t.Helper() runCmd(t, exec.Command("kubectl", "create", "namespace", namespace)) } -func deleteNamespace(t *testing.T, namespace string) { +func DeleteNamespace(t *testing.T, namespace string) { t.Helper() runCmd(t, exec.Command("kubectl", "delete", "namespace", namespace)) } diff --git a/pkg/k8s/type_checks.go b/pkg/k8s/type_checks.go index 79b93c6c..bf2b71c3 100644 --- a/pkg/k8s/type_checks.go +++ b/pkg/k8s/type_checks.go @@ -4,3 +4,8 @@ func IsNamespaceV1(resource Resource) bool { _, ok := resource.(*NamespaceV1) return ok } + +func IsPodV1(resource Resource) bool { + _, ok := resource.(*PodV1) + return ok +}