From cc9be4ee1ac28aadc54de903586895f02fcafdc6 Mon Sep 17 00:00:00 2001 From: Michael Hammer Date: Mon, 4 Mar 2019 14:09:23 -0500 Subject: [PATCH 1/2] initial core files for Secret --- pkg/core/secret/from_kube.go | 68 ++++++++++++ pkg/core/secret/secret.go | 29 +++++ pkg/core/secret/secret_test.go | 188 +++++++++++++++++++++++++++++++++ pkg/core/secret/to_kube.go | 70 ++++++++++++ 4 files changed, 355 insertions(+) create mode 100644 pkg/core/secret/from_kube.go create mode 100644 pkg/core/secret/secret.go create mode 100644 pkg/core/secret/secret_test.go create mode 100644 pkg/core/secret/to_kube.go diff --git a/pkg/core/secret/from_kube.go b/pkg/core/secret/from_kube.go new file mode 100644 index 0000000..dd6ab56 --- /dev/null +++ b/pkg/core/secret/from_kube.go @@ -0,0 +1,68 @@ +package secret + +import ( + "fmt" + "reflect" + + serrors "github.com/koki/structurederrors" + v1 "k8s.io/api/core/v1" +) + +// NewSecretFromKubeSecret will create a new Secret object with +// the data from a provided kubernetes Secret object +func NewSecretFromKubeSecret(s interface{}) (*Secret, error) { + switch reflect.TypeOf(s) { + case reflect.TypeOf(v1.Secret{}): + obj := s.(v1.Secret) + return fromKubeSecretV1(&obj) + case reflect.TypeOf(&v1.Secret{}): + return fromKubeSecretV1(s.(*v1.Secret)) + default: + return nil, fmt.Errorf("unknown Secret version: %s", reflect.TypeOf(s)) + } +} + +func fromKubeSecretV1(kubeSecret *v1.Secret) (*Secret, error) { + sType, err := convertSecretType(kubeSecret.Type) + if err != nil { + return nil, err + } + s := &Secret{ + Name: kubeSecret.Name, + Namespace: kubeSecret.Namespace, + Version: kubeSecret.APIVersion, + Cluster: kubeSecret.ClusterName, + Labels: kubeSecret.Labels, + Annotations: kubeSecret.Annotations, + Data: kubeSecret.Data, + StringData: kubeSecret.StringData, + SecretType: sType, + } + + return s, nil +} + +// convertSecretType converts from a kubernetes SecretType +func convertSecretType(secret v1.SecretType) (SecretType, error) { + if secret == "" { + return "", nil + } + switch secret { + case v1.SecretTypeOpaque: + return SecretTypeOpaque, nil + case v1.SecretTypeServiceAccountToken: + return SecretTypeServiceAccountToken, nil + case v1.SecretTypeDockercfg: + return SecretTypeDockercfg, nil + case v1.SecretTypeDockerConfigJson: + return SecretTypeDockerConfigJson, nil + case v1.SecretTypeBasicAuth: + return SecretTypeBasicAuth, nil + case v1.SecretTypeSSHAuth: + return SecretTypeSSHAuth, nil + case v1.SecretTypeTLS: + return SecretTypeTLS, nil + default: + return "", serrors.InvalidValueErrorf(secret, "unrecognized Secret type") + } +} diff --git a/pkg/core/secret/secret.go b/pkg/core/secret/secret.go new file mode 100644 index 0000000..59ae6a0 --- /dev/null +++ b/pkg/core/secret/secret.go @@ -0,0 +1,29 @@ +package secret + +type SecretWrapper struct { + Secret Secret `json:"secret,omitempty"` +} + +type Secret struct { + Version string `json:"version,omitempty"` + Cluster string `json:"cluster,omitempty"` + Name string `json:"name,omitempty"` + Namespace string `json:"namespace,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` + StringData map[string]string `json:"string_data,omitempty"` + Data map[string][]byte `json:"data,omitempty"` + SecretType SecretType `json:"type,omitempty"` +} + +type SecretType string + +const ( + SecretTypeOpaque SecretType = "Opaque" + SecretTypeServiceAccountToken SecretType = "kubernetes.io/service-account-token" + SecretTypeDockercfg SecretType = "kubernetes.io/dockercfg" + SecretTypeDockerConfigJson SecretType = "kubernetes.io/dockerconfigjson" + SecretTypeBasicAuth SecretType = "kubernetes.io/basic-auth" + SecretTypeSSHAuth SecretType = "kubernetes.io/ssh-auth" + SecretTypeTLS SecretType = "kubernetes.io/tls" +) diff --git a/pkg/core/secret/secret_test.go b/pkg/core/secret/secret_test.go new file mode 100644 index 0000000..dbb807a --- /dev/null +++ b/pkg/core/secret/secret_test.go @@ -0,0 +1,188 @@ +package secret + +import ( + "reflect" + "testing" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// map[SecretName]V1SecretName +var mappings = map[string]string{ + "Name": "Name", + "Namespace": "Namespace", + "Version": "APIVersion", + "Cluster": "ClusterName", + "Labels": "Labels", + "Annotations": "Annotations", + "Data": "Data", + "StringData": "StringData", + "SecretType": "Type", +} + +// TestNewSecretFromKubeSecret verifies that NewSecretFromKubeSecret() +// successfully creates a Secret from a kubernetes Secret +func TestNewSecretFromKubeSecret(t *testing.T) { + testcases := []struct { + description string + obj interface{} + }{ + { + description: "v1 secret object", + obj: v1.Secret{}, + }, + { + description: "v1 secret pointer", + obj: &v1.Secret{}, + }, + } + + for _, tc := range testcases { + obj, _ := NewSecretFromKubeSecret(tc.obj) + expectedObj := reflect.TypeOf(&Secret{}) + objType := reflect.TypeOf(obj) + if expectedObj != objType { + t.Errorf("expected %s got %s", expectedObj, objType) + } + } +} + +// TestFromKubeSecretV1 verifies that fromKubeSecretV1() correctly populates +// the v1.Secret{} fields +func TestFromKubeSecretV1(t *testing.T) { + v1S := v1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "testS", + Namespace: "testNS", + ClusterName: "testCluster", + Labels: map[string]string{"label1": "test1", "label2": "test2"}, + Annotations: map[string]string{"ann1": "test1", "ann2": "test2"}, + }, + StringData: map[string]string{"field1": "data1", "field2": "data2"}, + Data: map[string][]byte{"bfield1": []byte("bdata1")}, + Type: v1.SecretTypeOpaque, + } + + s, _ := fromKubeSecretV1(&v1S) + if s.Name != v1S.Name { + t.Errorf("incorrect name, expected %s got %s", v1S.Name, s.Name) + } + + for name, v1Name := range mappings { + value := reflect.ValueOf(s).Elem().FieldByName(name).Interface() + v1Value := reflect.ValueOf(v1S).FieldByName(v1Name).Interface() + if !reflect.DeepEqual(value, v1Value) { + t.Errorf("incorrect %s, expected %s, got %s", name, v1Value, value) + } + } +} + +// TestToKube verifies that ToKube() successfully returns +// a v1.Secret{} +func TestToKube(t *testing.T) { + testcases := []struct { + description string + version string + expectedObj interface{} + }{ + { + description: "v1 api version", + version: "v1", + expectedObj: &v1.Secret{}, + }, + { + description: "empty api version", + version: "", + expectedObj: &v1.Secret{}, + }, + { + description: "unknown api version", + version: "unknown", + expectedObj: nil, + }, + } + + for _, tc := range testcases { + s := Secret{ + Version: tc.version, + } + kubeObj, err := s.ToKube() + kubeType := reflect.TypeOf(kubeObj) + expectedType := reflect.TypeOf(tc.expectedObj) + if kubeType != expectedType { + t.Errorf("wrong api version, got %s expected %s", kubeType, expectedType) + } + if tc.expectedObj == nil && err == nil { + t.Errorf("no error returned") + } + } +} + +// TestToKubeV1 verifies that toKubev1() correctly populates +// the v1.Secret{} fields +func TestToKubeV1(t *testing.T) { + s := Secret{ + Version: "v1", + Name: "testS", + Namespace: "testNS", + Cluster: "testCluster", + Labels: map[string]string{"label1": "test1", "label2": "test2"}, + Annotations: map[string]string{"ann1": "test1", "ann2": "test2"}, + StringData: map[string]string{"field1": "data1", "field2": "data2"}, + Data: map[string][]byte{"bfield1": []byte("bdata1")}, + SecretType: v1.SecretTypeOpaque, + } + + kubeObj, _ := s.toKubeV1() + for name, v1Name := range mappings { + value := reflect.ValueOf(s).FieldByName(name).Interface() + v1Value := reflect.ValueOf(kubeObj).Elem().FieldByName(v1Name).Interface() + if !reflect.DeepEqual(value, v1Value) { + t.Errorf("incorrect %s, expected %s, got %s", v1Name, value, v1Value) + } + } +} + +var mapToSecretType = map[v1.SecretType]SecretType{ + v1.SecretTypeOpaque: SecretTypeOpaque, + v1.SecretTypeServiceAccountToken: SecretTypeServiceAccountToken, + v1.SecretTypeDockercfg: SecretTypeDockercfg, + v1.SecretTypeDockerConfigJson: SecretTypeDockerConfigJson, + v1.SecretTypeBasicAuth: SecretTypeBasicAuth, + v1.SecretTypeSSHAuth: SecretTypeSSHAuth, + v1.SecretTypeTLS: SecretTypeTLS, +} + +// TestConvertSecretType verifies converting to Secret Types from Kubernetes +func TestConvertSecretType(t *testing.T) { + for kubeSecretType, secretType := range mapToSecretType { + gotType := convertSecretType(kubeSecretType) + if gotType != secretType { + t.Errorf("incorrect conversion of %s, expected %s, got %s", kubeSecretType, secretType, gotType) + } + } +} + +var mapToKubeSecretType = map[SecretType]v1.SecretType{ + SecretTypeOpaque: v1.SecretTypeOpaque, + SecretTypeServiceAccountToken: v1.SecretTypeServiceAccountToken, + SecretTypeDockercfg: v1.SecretTypeDockercfg, + SecretTypeDockerConfigJson: v1.SecretTypeDockerConfigJson, + SecretTypeBasicAuth: v1.SecretTypeBasicAuth, + SecretTypeSSHAuth: v1.SecretTypeSSHAuth, + SecretTypeTLS: v1.SecretTypeTLS, +} + +// TestRevertSecretType verifies converting to Kubernetes Secret Types +func TestRevertSecretType(t *testing.T) { + for secretType, KubeSecretType := range mapToKubeSecretType { + gotType := revertSecretType(secretType) + if gotType != secretType { + t.Errorf("incorrect conversion of %s, expected %s, got %s", secretType, KubeSecretType, gotType) + } + } +} diff --git a/pkg/core/secret/to_kube.go b/pkg/core/secret/to_kube.go new file mode 100644 index 0000000..1c0e6f0 --- /dev/null +++ b/pkg/core/secret/to_kube.go @@ -0,0 +1,70 @@ +package secret + +import ( + "fmt" + "strings" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + serrors "github.com/koki/structurederrors" +) + +// ToKube will return a kubernetes secret object of the +// api version type defined in the object +func (s *Secret) ToKube() (runtime.Object, error) { + switch strings.ToLower(s.Version) { + case "v1": + return s.toKubeV1() + case "": + return s.toKubeV1() + default: + return nil, fmt.Errorf("unsupported api version for secret: %s", cm.Version) + } +} + +func (s *Secret) toKubeV1() (*v1.Secret, error) { + kubeSecret := &v1.Secret{} + + kubeSecret.Name = s.Name + kubeSecret.Namespace = s.Namespace + kubeSecret.APIVersion = s.Version + kubeSecret.ClusterName = s.Cluster + kubeSecret.Kind = "Secret" + kubeSecret.Labels = s.Labels + kubeSecret.Annotations = s.Annotations + kubeSecret.Data = s.Data + kubeSecret.StringData = s.StringData + type, err := revertSecretType(s.SecretType) + if err != nil { + return nil, err + } + kubeSecret.Type = type + + return kubeSecret, nil +} + +// revertSecretType converts to a kubernetes SecretType +func revertSecretType(secret SecretType) (v1.SecretType, error) { + if secret == "" { + return "", nil + } + + switch secret { + case SecretTypeOpaque: + return v1.SecretTypeOpaque, nil + case SecretTypeServiceAccountToken: + return v1.SecretTypeServiceAccountToken, nil + case SecretTypeDockercfg: + return v1.SecretTypeDockercfg, nil + case SecretTypeDockerConfigJson: + return v1.SecretTypeDockerConfigJson, nil + case SecretTypeBasicAuth: + return v1.SecretTypeBasicAuth, nil + case SecretTypeSSHAuth: + return v1.SecretTypeSSHAuth, nil + case SecretTypeTLS: + return v1.SecretTypeTLS, nil + default: + return "", serrors.InvalidValueErrorf(secret, "unrecognized Secret type") + } +} From e4fdb7f5e0e96f2a1a47811884b7783b10c5e59e Mon Sep 17 00:00:00 2001 From: Vasiliy Sharapov Date: Tue, 5 Mar 2019 09:24:22 -0500 Subject: [PATCH 2/2] More tests for core/secret and a couple of typo fixes --- pkg/core/secret/secret.go | 32 ++++++++++++ pkg/core/secret/secret_test.go | 93 +++++++++++++++++++++++++++------- pkg/core/secret/to_kube.go | 6 +-- 3 files changed, 111 insertions(+), 20 deletions(-) diff --git a/pkg/core/secret/secret.go b/pkg/core/secret/secret.go index 59ae6a0..6bf5820 100644 --- a/pkg/core/secret/secret.go +++ b/pkg/core/secret/secret.go @@ -1,5 +1,10 @@ package secret +import ( + "fmt" + v1 "k8s.io/api/core/v1" +) + type SecretWrapper struct { Secret Secret `json:"secret,omitempty"` } @@ -27,3 +32,30 @@ const ( SecretTypeSSHAuth SecretType = "kubernetes.io/ssh-auth" SecretTypeTLS SecretType = "kubernetes.io/tls" ) + +func (s SecretType) ToString() string { + return string(s) +} + +func CompareSecretTypes(secret1, secret2 interface{}) (bool, error) { + s1str, s2str := "", "" + sV1, ok1 := secret1.(v1.SecretType) + if ok1 { s1str = string(sV1) } + sMantle, ok2 := secret1.(SecretType) + if ok2 { s1str = string(sMantle) } + if !ok1 && !ok2 { + return false, fmt.Errorf("%s is not a k8s.v1 secretType nor a mantle SecretType", secret1) + } + + sV1, ok1 = secret2.(v1.SecretType) + if ok1 { s2str = string(sV1) } + sMantle, ok2 = secret2.(SecretType) + if ok2 { s2str = string(sMantle) } + if !ok1 && !ok2 { + return false, fmt.Errorf("%s is not a k8s.v1 secretType nor a mantle SecretType", secret2) + } + + return s1str == s2str, nil +} + + diff --git a/pkg/core/secret/secret_test.go b/pkg/core/secret/secret_test.go index dbb807a..785c9db 100644 --- a/pkg/core/secret/secret_test.go +++ b/pkg/core/secret/secret_test.go @@ -21,6 +21,46 @@ var mappings = map[string]string{ "SecretType": "Type", } +var mapToSecretType = map[v1.SecretType]SecretType{ + v1.SecretTypeOpaque: SecretTypeOpaque, + v1.SecretTypeServiceAccountToken: SecretTypeServiceAccountToken, + v1.SecretTypeDockercfg: SecretTypeDockercfg, + v1.SecretTypeDockerConfigJson: SecretTypeDockerConfigJson, + v1.SecretTypeBasicAuth: SecretTypeBasicAuth, + v1.SecretTypeSSHAuth: SecretTypeSSHAuth, + v1.SecretTypeTLS: SecretTypeTLS, +} + +func TestCompareSecretTypes(t *testing.T) { + // Compare mantle to v1 + for key, val := range mapToSecretType { + out, err := CompareSecretTypes(key, val) + if !out || err != nil { + t.Errorf("Failed TestCompareSecretTypes on %s and %s", key, val) + } + } + // Compare v1 to v1 + for key, _ := range mapToSecretType { + out, err := CompareSecretTypes(key, key) + if !out || err != nil { + t.Errorf("Failed TestCompareSecretTypes on %s and %s", key, key) + } + } + // Compare mantle to mantle + for _, val := range mapToSecretType { + out, err := CompareSecretTypes(val, val) + if !out || err != nil { + t.Errorf("Failed TestCompareSecretTypes on %s and %s", val, val) + } + } + // Compare "Opaque" to k8s v1.SecretTypeOpaque + out, err := CompareSecretTypes("Opaque", v1.SecretTypeOpaque) + if err == nil { + t.Errorf("Compare \"Opaque\" to k8s v1.SecretTypeOpaque should have failed but returned %v", out) + } +} + + // TestNewSecretFromKubeSecret verifies that NewSecretFromKubeSecret() // successfully creates a Secret from a kubernetes Secret func TestNewSecretFromKubeSecret(t *testing.T) { @@ -75,9 +115,20 @@ func TestFromKubeSecretV1(t *testing.T) { for name, v1Name := range mappings { value := reflect.ValueOf(s).Elem().FieldByName(name).Interface() v1Value := reflect.ValueOf(v1S).FieldByName(v1Name).Interface() - if !reflect.DeepEqual(value, v1Value) { + //fmt.Printf("(%v, %T)\n", value, value) + //fmt.Printf("(%v, %T)\n", v1Value, v1Value) + if !reflect.DeepEqual(value, v1Value) && name != "SecretType" { t.Errorf("incorrect %s, expected %s, got %s", name, v1Value, value) } + if name == "SecretType" { + areEqual, err := CompareSecretTypes(value, v1Value) + if err != nil { + t.Errorf("%s", err) + } + if !areEqual { + t.Errorf("incorrect %s, expected %s, got %s", name, v1Value, value) + } + } } } @@ -134,33 +185,36 @@ func TestToKubeV1(t *testing.T) { Annotations: map[string]string{"ann1": "test1", "ann2": "test2"}, StringData: map[string]string{"field1": "data1", "field2": "data2"}, Data: map[string][]byte{"bfield1": []byte("bdata1")}, - SecretType: v1.SecretTypeOpaque, + SecretType: SecretTypeOpaque, } kubeObj, _ := s.toKubeV1() for name, v1Name := range mappings { value := reflect.ValueOf(s).FieldByName(name).Interface() v1Value := reflect.ValueOf(kubeObj).Elem().FieldByName(v1Name).Interface() - if !reflect.DeepEqual(value, v1Value) { - t.Errorf("incorrect %s, expected %s, got %s", v1Name, value, v1Value) + if !reflect.DeepEqual(value, v1Value) && name != "SecretType" { + t.Errorf("incorrect %s, expected %s, got %s", name, v1Value, value) } + if name == "SecretType" { + areEqual, err := CompareSecretTypes(value, v1Value) + if err != nil { + t.Errorf("%s", err) + } + if !areEqual { + t.Errorf("incorrect %s, expected %s, got %s", name, v1Value, value) + } + } } } -var mapToSecretType = map[v1.SecretType]SecretType{ - v1.SecretTypeOpaque: SecretTypeOpaque, - v1.SecretTypeServiceAccountToken: SecretTypeServiceAccountToken, - v1.SecretTypeDockercfg: SecretTypeDockercfg, - v1.SecretTypeDockerConfigJson: SecretTypeDockerConfigJson, - v1.SecretTypeBasicAuth: SecretTypeBasicAuth, - v1.SecretTypeSSHAuth: SecretTypeSSHAuth, - v1.SecretTypeTLS: SecretTypeTLS, -} - // TestConvertSecretType verifies converting to Secret Types from Kubernetes func TestConvertSecretType(t *testing.T) { for kubeSecretType, secretType := range mapToSecretType { - gotType := convertSecretType(kubeSecretType) + gotType, err := convertSecretType(kubeSecretType) + if err != nil { + t.Errorf("convertSecretType() returned an error: %s", err) + } + if gotType != secretType { t.Errorf("incorrect conversion of %s, expected %s, got %s", kubeSecretType, secretType, gotType) } @@ -180,8 +234,13 @@ var mapToKubeSecretType = map[SecretType]v1.SecretType{ // TestRevertSecretType verifies converting to Kubernetes Secret Types func TestRevertSecretType(t *testing.T) { for secretType, KubeSecretType := range mapToKubeSecretType { - gotType := revertSecretType(secretType) - if gotType != secretType { + gotType, err := revertSecretType(secretType) + if err != nil { + t.Errorf("revertSecretType() returned an error: %s", err) + } + + // t.Errorf("gotType: %s\nsecretType: %s\nKubeSecretType: %s", gotType, secretType, KubeSecretType) + if string(gotType) != secretType.ToString() { t.Errorf("incorrect conversion of %s, expected %s, got %s", secretType, KubeSecretType, gotType) } } diff --git a/pkg/core/secret/to_kube.go b/pkg/core/secret/to_kube.go index 1c0e6f0..b5b6363 100644 --- a/pkg/core/secret/to_kube.go +++ b/pkg/core/secret/to_kube.go @@ -18,7 +18,7 @@ func (s *Secret) ToKube() (runtime.Object, error) { case "": return s.toKubeV1() default: - return nil, fmt.Errorf("unsupported api version for secret: %s", cm.Version) + return nil, fmt.Errorf("unsupported api version for secret: %s", s.Version) } } @@ -34,11 +34,11 @@ func (s *Secret) toKubeV1() (*v1.Secret, error) { kubeSecret.Annotations = s.Annotations kubeSecret.Data = s.Data kubeSecret.StringData = s.StringData - type, err := revertSecretType(s.SecretType) + secretType, err := revertSecretType(s.SecretType) if err != nil { return nil, err } - kubeSecret.Type = type + kubeSecret.Type = secretType return kubeSecret, nil }