Skip to content

Commit

Permalink
AB#676 Add cli unittests for k8s resources (#111)
Browse files Browse the repository at this point in the history
* AB#676 Add cli unittests for k8s resources and fix some command descriptions

* Update csr_test.go to no longer use rsa.PrivateKey.Equal method

* Update root command description
  • Loading branch information
daniel-weisse authored Mar 12, 2021
1 parent c3d4c8c commit 04c1b2e
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 13 deletions.
4 changes: 2 additions & 2 deletions cli/cmd/certificateChain.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ func newCertificateChain() *cobra.Command {

cmd := &cobra.Command{
Use: "chain <IP:PORT>",
Short: "returns the certificate chain of the Marblerun coordinator",
Long: `returns the certificate chain of the Marblerun coordinator`,
Short: "Returns the certificate chain of the Marblerun coordinator",
Long: `Returns the certificate chain of the Marblerun coordinator`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
hostName := args[0]
Expand Down
4 changes: 2 additions & 2 deletions cli/cmd/certificateIntermediate.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ func newCertificateIntermediate() *cobra.Command {

cmd := &cobra.Command{
Use: "intermediate <IP:PORT>",
Short: "returns the intermediate certificate of the Marblerun coordinator",
Long: `returns the intermediate certificate of the Marblerun coordinator`,
Short: "Returns the intermediate certificate of the Marblerun coordinator",
Long: `Returns the intermediate certificate of the Marblerun coordinator`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
hostName := args[0]
Expand Down
4 changes: 2 additions & 2 deletions cli/cmd/certificateRoot.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ func newCertificateRoot() *cobra.Command {

cmd := &cobra.Command{
Use: "root <IP:PORT>",
Short: "returns the root certificate of the Marblerun coordinator",
Long: `returns the root certificate of the Marblerun coordinator`,
Short: "Returns the root certificate of the Marblerun coordinator",
Long: `Returns the root certificate of the Marblerun coordinator`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
hostName := args[0]
Expand Down
90 changes: 90 additions & 0 deletions cli/cmd/csr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package cmd

import (
"crypto/rand"
"crypto/rsa"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/client-go/kubernetes/fake"
)

func TestCreateCSR(t *testing.T) {
require := require.New(t)
assert := assert.New(t)

testKey, err := rsa.GenerateKey(rand.Reader, 1024)
require.NoError(err)

pem, err := createCSR(testKey)
require.NoError(err)
assert.Equal("CERTIFICATE REQUEST", pem.Type)
}

func TestCertificateV1(t *testing.T) {
require := require.New(t)
assert := assert.New(t)

testClient := fake.NewSimpleClientset()

testHandler, err := newCertificateV1(testClient)
require.NoError(err)

testKey := testHandler.getKey()
assert.Equal(testKey, testHandler.privKey, "private key of the handler and private key returned by its method were not equal")

testHandler.timeout = 5
// this should error with a timeout since the fakeClient does not keep upated resources, but only returns them once on API call
err = testHandler.signRequest()
require.Error(err)
assert.Contains(err.Error(), "certificate signing request was not updated", fmt.Sprintf("failed with unexpected error: %s", err.Error()))

// we use a different timeout function here, so this should not return an error, but the certificate will be empty
testCrt, err := testHandler.get()
require.NoError(err)
assert.True((len(testCrt) == 0))

testValues := map[string]interface{}{
"marbleInjector": map[string]interface{}{
"start": false,
"CABundle": "string",
},
}

testHandler.setCaBundle(testValues)
assert.Equal(nil, testValues["marbleInjector"].(map[string]interface{})["CABundle"], "failed to remove CABundle")
assert.Equal(true, testValues["marbleInjector"].(map[string]interface{})["start"], "failed to set start to true")
}

func TestCertificateLegacy(t *testing.T) {
require := require.New(t)
assert := assert.New(t)

testHandler, err := newCertificateLegacy()
require.NoError(err)
assert.True(len(testHandler.caCert.Bytes) > 0, "failed creating caCert")

err = testHandler.signRequest()
require.NoError(err)
assert.True(len(testHandler.serverCert.Bytes) > 0, "failed creating serverCert")

testKey := testHandler.getKey()
assert.Equal(testKey, testHandler.serverPrivKey, "private key of the handler and private key returned by its method were not equal")

testCrt, err := testHandler.get()
require.NoError(err)
assert.True(len(testCrt) > 0, "failed to retrieve server certificate")

testValues := map[string]interface{}{
"marbleInjector": map[string]interface{}{
"start": false,
"CABundle": "string",
},
}

testHandler.setCaBundle(testValues)
assert.Equal(true, testValues["marbleInjector"].(map[string]interface{})["start"], "failed to set start to true")
assert.NotEqual("string", testValues["marbleInjector"].(map[string]interface{})["CABundle"], "failed to set CABundle")
}
10 changes: 5 additions & 5 deletions cli/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func installWebhook(vals map[string]interface{}) error {
}

// createSecret creates a secret containing the signed certificate and private key for the webhook server
func createSecret(privKey *rsa.PrivateKey, crt []byte, kubeClient *kubernetes.Clientset) error {
func createSecret(privKey *rsa.PrivateKey, crt []byte, kubeClient kubernetes.Interface) error {
rsaPEM := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Expand Down Expand Up @@ -285,14 +285,14 @@ func getCertificateHandler(kubeClient kubernetes.Interface) (certificateInterfac
return newCertificateV1(kubeClient)
}

func verifyNamespace(namespace string, kubeClient *kubernetes.Clientset) error {
_, err := kubeClient.CoreV1().Namespaces().Get(context.TODO(), "marblerun", metav1.GetOptions{})
func verifyNamespace(namespace string, kubeClient kubernetes.Interface) error {
_, err := kubeClient.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{})
if err != nil {
// if the namespace does not exist we create it
if err.Error() == "namespaces \"marblerun\" not found" {
if err.Error() == fmt.Sprintf("namespaces \"%s\" not found", namespace) {
marbleNamespace := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "marblerun",
Name: namespace,
},
}
if _, err := kubeClient.CoreV1().Namespaces().Create(context.TODO(), marbleNamespace, metav1.CreateOptions{}); err != nil {
Expand Down
84 changes: 84 additions & 0 deletions cli/cmd/install_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package cmd

import (
"context"
"crypto/rand"
"crypto/rsa"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/version"
fakediscovery "k8s.io/client-go/discovery/fake"
"k8s.io/client-go/kubernetes/fake"
)

func TestCreateSecret(t *testing.T) {
require := require.New(t)
testClient := fake.NewSimpleClientset()

testKey, err := rsa.GenerateKey(rand.Reader, 1024)
require.NoError(err)
crt := []byte{0xAA, 0xAA, 0xAA}

newNamespace1 := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "marblerun",
},
}
_, err = testClient.CoreV1().Namespaces().Create(context.TODO(), newNamespace1, metav1.CreateOptions{})
require.NoError(err)

err = createSecret(testKey, crt, testClient)
require.NoError(err)
_, err = testClient.CoreV1().Secrets("marblerun").Get(context.TODO(), "marble-injector-webhook-certs", metav1.GetOptions{})
require.NoError(err)

// we should get an error since the secret was already created in the previous step
err = createSecret(testKey, crt, testClient)
require.Error(err)
}

func TestGetCertificateHandler(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
testClient := fake.NewSimpleClientset()

testClient.Discovery().(*fakediscovery.FakeDiscovery).FakedServerVersion = &version.Info{
Major: "1",
Minor: "19",
}
testHandler, err := getCertificateHandler(testClient)
require.NoError(err)
assert.Equal(reflect.TypeOf(testHandler).String(), "*cmd.certificateV1")

testClient.Discovery().(*fakediscovery.FakeDiscovery).FakedServerVersion = &version.Info{
Major: "1",
Minor: "18",
}
testHandler, err = getCertificateHandler(testClient)
require.NoError(err)
assert.Equal(reflect.TypeOf(testHandler).String(), "*cmd.certificateLegacy")
}

func TestVerifyNamespace(t *testing.T) {
require := require.New(t)
testClient := fake.NewSimpleClientset()

_, err := testClient.CoreV1().Namespaces().Get(context.TODO(), "test-space", metav1.GetOptions{})
require.Error(err)

// namespace does not exist, it should be created here
err = verifyNamespace("test-space", testClient)
require.NoError(err)

_, err = testClient.CoreV1().Namespaces().Get(context.TODO(), "test-space", metav1.GetOptions{})
require.NoError(err)

// namespace exists, should return nil
err = verifyNamespace("test-space", testClient)
require.NoError(err)
}
161 changes: 161 additions & 0 deletions cli/cmd/namespace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package cmd

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
)

func TestNameSpaceAdd(t *testing.T) {
assert := assert.New(t)
require := require.New(t)

testClient := fake.NewSimpleClientset()

// Test adding non existant namespace
err := cliNameSpaceAdd([]string{"test-space-1"}, testClient, true)
require.Error(err)

// Test adding multiple non existant namespaces
err = cliNameSpaceAdd([]string{"test-space-1", "test-space-2", "test-space-3"}, testClient, true)
require.Error(err)

// Create namespace to add to mesh
newNamespace1 := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-space-1",
},
}
_, err = testClient.CoreV1().Namespaces().Create(context.TODO(), newNamespace1, metav1.CreateOptions{})
require.NoError(err)
err = cliNameSpaceAdd([]string{"test-space-1"}, testClient, true)
require.NoError(err)

injectedNamespace, err := testClient.CoreV1().Namespaces().Get(context.TODO(), "test-space-1", metav1.GetOptions{})
require.NoError(err)
assert.Equal(injectedNamespace.Labels["marblerun/inject"], "enabled", "failed to inject marblerun label")
assert.Equal(injectedNamespace.Labels["marblerun/inject-sgx"], "disabled", "injected sgx label when it shouldnt have")

// Create two more namespaces
newNamespace2 := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-space-2",
},
}
newNamespace3 := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-space-3",
},
}
_, err = testClient.CoreV1().Namespaces().Create(context.TODO(), newNamespace2, metav1.CreateOptions{})
require.NoError(err)
_, err = testClient.CoreV1().Namespaces().Create(context.TODO(), newNamespace3, metav1.CreateOptions{})
require.NoError(err)
err = cliNameSpaceAdd([]string{"test-space-2", "test-space-3"}, testClient, false)
require.NoError(err)

injectedNamespace, err = testClient.CoreV1().Namespaces().Get(context.TODO(), "test-space-2", metav1.GetOptions{})
require.NoError(err)
assert.Equal(injectedNamespace.Labels["marblerun/inject"], "enabled", "failed to inject marblerun label")
assert.Equal(injectedNamespace.Labels["marblerun/inject-sgx"], "enabled", "failed to inject marblerun inject-sgx label")

injectedNamespace, err = testClient.CoreV1().Namespaces().Get(context.TODO(), "test-space-3", metav1.GetOptions{})
require.NoError(err)
assert.Equal(injectedNamespace.Labels["marblerun/inject"], "enabled", "failed to inject marblerun label")
assert.Equal(injectedNamespace.Labels["marblerun/inject-sgx"], "enabled", "failed to inject marblerun inject-sgx label")
}

func TestNameSpaceList(t *testing.T) {
require := require.New(t)
assert := assert.New(t)

testClient := fake.NewSimpleClientset()

// Test listing on empty
err := cliNameSpaceList(testClient)
require.NoError(err)

// Create and add two namespaces
newNamespace1 := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-space-1",
Labels: map[string]string{
"marblerun/inject": "enabled",
"marblerun/inject-sgx": "enabled",
},
},
}
newNamespace2 := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-space-2",
Labels: map[string]string{
"marblerun/inject": "enabled",
"marblerun/inject-sgx": "disabled",
},
},
}
_, err = testClient.CoreV1().Namespaces().Create(context.TODO(), newNamespace1, metav1.CreateOptions{})
require.NoError(err)
_, err = testClient.CoreV1().Namespaces().Create(context.TODO(), newNamespace2, metav1.CreateOptions{})
require.NoError(err)

list, err := selectNamespaces(testClient)
require.NoError(err)
assert.Equal(len(list.Items), 2, fmt.Sprintf("expected 2 items but got %d", len(list.Items)))

err = cliNameSpaceList(testClient)
require.NoError(err)
}

func TestNameSpaceRemove(t *testing.T) {
require := require.New(t)
//assert := assert.New(t)

testClient := fake.NewSimpleClientset()

// Try removing non existant namespace
err := cliNameSpaceRemove("test-space-1", testClient)
require.Error(err)

newNamespace1 := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-space-1",
Labels: map[string]string{
"marblerun/inject": "enabled",
"marblerun/inject-sgx": "enabled",
},
},
}
_, err = testClient.CoreV1().Namespaces().Create(context.TODO(), newNamespace1, metav1.CreateOptions{})
require.NoError(err)

// Remove namespace from mesh
err = cliNameSpaceRemove("test-space-1", testClient)
require.NoError(err)

// Try removing namespace that is not labeled
err = cliNameSpaceRemove("test-space-1", testClient)
require.Error(err)

newNamespace2 := &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "test-space-2",
Labels: map[string]string{
"marblerun/inject": "wrong-value",
"marblerun/inject-sgx": "enabled",
},
},
}
_, err = testClient.CoreV1().Namespaces().Create(context.TODO(), newNamespace2, metav1.CreateOptions{})
require.NoError(err)

// Try removing namespace with incorrect label
err = cliNameSpaceRemove("test-space-2", testClient)
require.Error(err)
}
Loading

0 comments on commit 04c1b2e

Please sign in to comment.