github.skymusic.top/operator-framework/operator-sdk@v0.8.2/test/e2e/tls_util_test.go (about)

     1  // Copyright 2018 The Operator-SDK Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package e2e
    16  
    17  import (
    18  	"io/ioutil"
    19  	"reflect"
    20  	"testing"
    21  
    22  	framework "github.com/operator-framework/operator-sdk/pkg/test"
    23  	tlsutil "github.com/operator-framework/operator-sdk/pkg/tls"
    24  
    25  	"k8s.io/api/core/v1"
    26  	apiErrors "k8s.io/apimachinery/pkg/api/errors"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  )
    30  
    31  var (
    32  	// TLS test variables.
    33  	crKind                   = "Pod"
    34  	crName                   = "example-pod"
    35  	certName                 = "app-cert"
    36  	caConfigMapAndSecretName = tlsutil.ToCASecretAndConfigMapName(crKind, crName)
    37  	appSecretName            = tlsutil.ToAppSecretName(crKind, crName, certName)
    38  
    39  	caConfigMap *v1.ConfigMap
    40  	caSecret    *v1.Secret
    41  	appSecret   *v1.Secret
    42  
    43  	ccfg *tlsutil.CertConfig
    44  )
    45  
    46  // setup test variables.
    47  func init() {
    48  	caCertBytes, err := ioutil.ReadFile("./testdata/ca.crt")
    49  	if err != nil {
    50  		panic(err)
    51  	}
    52  	caConfigMap = &v1.ConfigMap{
    53  		ObjectMeta: metav1.ObjectMeta{
    54  			Name: caConfigMapAndSecretName,
    55  		},
    56  		Data: map[string]string{tlsutil.TLSCACertKey: string(caCertBytes)},
    57  	}
    58  
    59  	caKeyBytes, err := ioutil.ReadFile("./testdata/ca.key")
    60  	if err != nil {
    61  		panic(err)
    62  	}
    63  	caSecret = &v1.Secret{
    64  		ObjectMeta: metav1.ObjectMeta{
    65  			Name: caConfigMapAndSecretName,
    66  		},
    67  		Data: map[string][]byte{tlsutil.TLSPrivateCAKeyKey: caKeyBytes},
    68  	}
    69  
    70  	appSecret = &v1.Secret{
    71  		ObjectMeta: metav1.ObjectMeta{
    72  			Name: appSecretName,
    73  		},
    74  	}
    75  
    76  	ccfg = &tlsutil.CertConfig{
    77  		CertName: certName,
    78  	}
    79  }
    80  
    81  // TestBothAppAndCATLSAssetsExist ensures that when both application
    82  // and CA TLS assets exist in the k8s cluster for a given cr,
    83  // the GenerateCert() simply returns those to the caller.
    84  func TestBothAppAndCATLSAssetsExist(t *testing.T) {
    85  	ctx := framework.NewTestCtx(t)
    86  	defer ctx.Cleanup()
    87  	namespace, err := ctx.GetNamespace()
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  
    92  	f := framework.Global
    93  	appSecret, err := f.KubeClient.CoreV1().Secrets(namespace).Create(appSecret)
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  
    98  	caConfigMap, err := f.KubeClient.CoreV1().ConfigMaps(namespace).Create(caConfigMap)
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	}
   102  
   103  	caSecret, err := f.KubeClient.CoreV1().Secrets(namespace).Create(caSecret)
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  
   108  	cg := tlsutil.NewSDKCertGenerator(f.KubeClient)
   109  	actualAppSecret, actualCaConfigMap, actualCaSecret, err := cg.GenerateCert(newDummyCR(namespace), nil, ccfg)
   110  	if err != nil {
   111  		t.Fatal(err)
   112  	}
   113  
   114  	if !reflect.DeepEqual(appSecret, actualAppSecret) {
   115  		t.Fatalf("Expect %+v, but got %+v", appSecret, actualAppSecret)
   116  	}
   117  	if !reflect.DeepEqual(caConfigMap, actualCaConfigMap) {
   118  		t.Fatalf("Expect %+v, but got %+v", caConfigMap, actualCaConfigMap)
   119  	}
   120  	if !reflect.DeepEqual(caSecret, actualCaSecret) {
   121  		t.Fatalf("Expect %+v, but got %+v", caSecret, actualCaSecret)
   122  	}
   123  }
   124  
   125  // TestOnlyAppSecretExist tests a case where the application TLS asset exists but its
   126  // correspoding CA asset doesn't. In this case, CertGenerator can't genereate a new CA because
   127  // it won't verify the existing application TLS cert. Therefore, CertGenerator can't proceed
   128  // and returns an error to the caller.
   129  func TestOnlyAppSecretExist(t *testing.T) {
   130  	ctx := framework.NewTestCtx(t)
   131  	defer ctx.Cleanup()
   132  	namespace, err := ctx.GetNamespace()
   133  	if err != nil {
   134  		t.Fatal(err)
   135  	}
   136  
   137  	f := framework.Global
   138  	_, err = f.KubeClient.CoreV1().Secrets(namespace).Create(appSecret)
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  
   143  	cg := tlsutil.NewSDKCertGenerator(f.KubeClient)
   144  	_, _, _, err = cg.GenerateCert(newDummyCR(namespace), nil, ccfg)
   145  	if err == nil {
   146  		t.Fatal("Expect error, but got none")
   147  	}
   148  	if err != tlsutil.ErrCANotFound {
   149  		t.Fatalf("Expect %v, but got %v", tlsutil.ErrCANotFound.Error(), err.Error())
   150  	}
   151  }
   152  
   153  // TestOnlyCAExist tests the case where only the CA exists in the cluster;
   154  // GenerateCert can retrieve the CA and uses it to create a new application secret.
   155  func TestOnlyCAExist(t *testing.T) {
   156  	ctx := framework.NewTestCtx(t)
   157  	defer ctx.Cleanup()
   158  	namespace, err := ctx.GetNamespace()
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  
   163  	f := framework.Global
   164  	_, err = f.KubeClient.CoreV1().ConfigMaps(namespace).Create(caConfigMap)
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	_, err = f.KubeClient.CoreV1().Secrets(namespace).Create(caSecret)
   169  	if err != nil {
   170  		t.Fatal(err)
   171  	}
   172  
   173  	cg := tlsutil.NewSDKCertGenerator(f.KubeClient)
   174  	appSecret, _, _, err := cg.GenerateCert(newDummyCR(namespace), newAppSvc(namespace), ccfg)
   175  	if err != nil {
   176  		t.Fatal(err)
   177  	}
   178  
   179  	verifyAppSecret(t, appSecret, namespace)
   180  }
   181  
   182  // TestNoneOfCaAndAppSecretExist ensures that when none of the CA and Application TLS assets
   183  // exist, GenerateCert() creates both and put them into the k8s cluster.
   184  func TestNoneOfCaAndAppSecretExist(t *testing.T) {
   185  	ctx := framework.NewTestCtx(t)
   186  	defer ctx.Cleanup()
   187  	namespace, err := ctx.GetNamespace()
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  
   192  	f := framework.Global
   193  	cg := tlsutil.NewSDKCertGenerator(f.KubeClient)
   194  	appSecret, caConfigMap, caSecret, err := cg.GenerateCert(newDummyCR(namespace), newAppSvc(namespace), ccfg)
   195  	if err != nil {
   196  		t.Fatal(err)
   197  	}
   198  
   199  	verifyAppSecret(t, appSecret, namespace)
   200  	verifyCaConfigMap(t, caConfigMap, namespace)
   201  	verifyCASecret(t, caSecret, namespace)
   202  }
   203  
   204  // TestCustomCA ensures that if a user provides a custom Key and Cert and the CA and Application TLS assets
   205  // do not exist, the GenerateCert method can use the custom CA to generate the TLS assest.
   206  func TestCustomCA(t *testing.T) {
   207  	ctx := framework.NewTestCtx(t)
   208  	defer ctx.Cleanup()
   209  	namespace, err := ctx.GetNamespace()
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  
   214  	f := framework.Global
   215  	cg := tlsutil.NewSDKCertGenerator(f.KubeClient)
   216  
   217  	customConfig := &tlsutil.CertConfig{
   218  		CertName: certName,
   219  		CAKey:    "testdata/ca.key",
   220  		CACert:   "testdata/ca.crt",
   221  	}
   222  	appSecret, _, _, err := cg.GenerateCert(newDummyCR(namespace), newAppSvc(namespace), customConfig)
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  
   227  	verifyAppSecret(t, appSecret, namespace)
   228  
   229  	// ensure caConfigMap does not exist in k8s cluster.
   230  	_, err = framework.Global.KubeClient.CoreV1().Secrets(namespace).Get(caConfigMapAndSecretName, metav1.GetOptions{})
   231  	if !apiErrors.IsNotFound(err) {
   232  		t.Fatal(err)
   233  	}
   234  
   235  	// ensure caConfigMap does not exist in k8s cluster.
   236  	_, err = framework.Global.KubeClient.CoreV1().Secrets(namespace).Get(caConfigMapAndSecretName, metav1.GetOptions{})
   237  	if !apiErrors.IsNotFound(err) {
   238  		t.Fatal(err)
   239  	}
   240  }
   241  
   242  func verifyCASecret(t *testing.T, caSecret *v1.Secret, namespace string) {
   243  	// check if caConfigMap has the correct fields.
   244  	if caConfigMapAndSecretName != caSecret.Name {
   245  		t.Fatalf("Expect the ca config name %v, but got %v", caConfigMapAndSecretName, caConfigMap.Name)
   246  	}
   247  	if namespace != caSecret.Namespace {
   248  		t.Fatalf("Expect the ca config namespace %v, but got %v", namespace, appSecret.Namespace)
   249  	}
   250  	if _, ok := caSecret.Data[tlsutil.TLSPrivateCAKeyKey]; !ok {
   251  		t.Fatalf("Expect the ca config to have the data field %v, but got none", tlsutil.TLSPrivateCAKeyKey)
   252  	}
   253  
   254  	// check if caConfigMap exists in k8s cluster.
   255  	caSecretFromCluster, err := framework.Global.KubeClient.CoreV1().Secrets(namespace).Get(caConfigMapAndSecretName, metav1.GetOptions{})
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  	// check if caSecret returned from GenerateCert is the same as the one that exists in the k8s.
   260  	if !reflect.DeepEqual(caSecret, caSecretFromCluster) {
   261  		t.Fatalf("Expect %+v, but got %+v", caSecret, caSecretFromCluster)
   262  	}
   263  }
   264  
   265  func verifyCaConfigMap(t *testing.T, caConfigMap *v1.ConfigMap, namespace string) {
   266  	// check if caConfigMap has the correct fields.
   267  	if caConfigMapAndSecretName != caConfigMap.Name {
   268  		t.Fatalf("Expect the ca config name %v, but got %v", caConfigMapAndSecretName, caConfigMap.Name)
   269  	}
   270  	if namespace != caConfigMap.Namespace {
   271  		t.Fatalf("Expect the ca config namespace %v, but got %v", namespace, appSecret.Namespace)
   272  	}
   273  	if _, ok := caConfigMap.Data[tlsutil.TLSCACertKey]; !ok {
   274  		t.Fatalf("Expect the ca config to have the data field %v, but got none", tlsutil.TLSCACertKey)
   275  	}
   276  
   277  	// check if caConfigMap exists in k8s cluster.
   278  	caConfigMapFromCluster, err := framework.Global.KubeClient.CoreV1().ConfigMaps(namespace).Get(caConfigMapAndSecretName, metav1.GetOptions{})
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  	// check if caConfigMap returned from GenerateCert is the same as the one that exists in the k8s.
   283  	if !reflect.DeepEqual(caConfigMap, caConfigMapFromCluster) {
   284  		t.Fatalf("Expect %+v, but got %+v", caConfigMap, caConfigMapFromCluster)
   285  	}
   286  }
   287  
   288  func verifyAppSecret(t *testing.T, appSecret *v1.Secret, namespace string) {
   289  	// check if appSecret has the correct fields.
   290  	if appSecretName != appSecret.Name {
   291  		t.Fatalf("Expect the secret name %v, but got %v", appSecretName, appSecret.Name)
   292  	}
   293  	if namespace != appSecret.Namespace {
   294  		t.Fatalf("Expect the secret namespace %v, but got %v", namespace, appSecret.Namespace)
   295  	}
   296  	if v1.SecretTypeTLS != appSecret.Type {
   297  		t.Fatalf("Expect the secret type %v, but got %v", v1.SecretTypeTLS, appSecret.Type)
   298  	}
   299  	if _, ok := appSecret.Data[v1.TLSCertKey]; !ok {
   300  		t.Fatalf("Expect the secret to have the data field %v, but got none", v1.TLSCertKey)
   301  	}
   302  	if _, ok := appSecret.Data[v1.TLSPrivateKeyKey]; !ok {
   303  		t.Fatalf("Expect the secret to have the data field %v, but got none", v1.TLSPrivateKeyKey)
   304  	}
   305  
   306  	// check if appSecret exists in k8s cluster.
   307  	appSecretFromCluster, err := framework.Global.KubeClient.CoreV1().Secrets(namespace).Get(appSecretName, metav1.GetOptions{})
   308  	if err != nil {
   309  		t.Fatal(err)
   310  	}
   311  	// check if appSecret returned from GenerateCert is the same as the one that exists in the k8s.
   312  	if !reflect.DeepEqual(appSecret, appSecretFromCluster) {
   313  		t.Fatalf("Expect %+v, but got %+v", appSecret, appSecretFromCluster)
   314  	}
   315  }
   316  
   317  // newDummyCR returns a dummy runtime object for the CR input of GenerateCert().
   318  func newDummyCR(namespace string) runtime.Object {
   319  	return &v1.Pod{
   320  		TypeMeta: metav1.TypeMeta{
   321  			Kind: crKind,
   322  		},
   323  		ObjectMeta: metav1.ObjectMeta{
   324  			Name:      crName,
   325  			Namespace: namespace,
   326  		},
   327  	}
   328  }
   329  
   330  func newAppSvc(namespace string) *v1.Service {
   331  	return &v1.Service{
   332  		ObjectMeta: metav1.ObjectMeta{
   333  			Name:      "app-service",
   334  			Namespace: namespace,
   335  		},
   336  	}
   337  }