github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/base/ca/override/deployment_test.go (about)

     1  /*
     2   * Copyright contributors to the Hyperledger Fabric Operator project
     3   *
     4   * SPDX-License-Identifier: Apache-2.0
     5   *
     6   * Licensed under the Apache License, Version 2.0 (the "License");
     7   * you may not use this file except in compliance with the License.
     8   * You may obtain a copy of the License at:
     9   *
    10   * 	  http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing, software
    13   * distributed under the License is distributed on an "AS IS" BASIS,
    14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15   * See the License for the specific language governing permissions and
    16   * limitations under the License.
    17   */
    18  
    19  package override_test
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  
    25  	. "github.com/onsi/ginkgo/v2"
    26  	. "github.com/onsi/gomega"
    27  
    28  	appsv1 "k8s.io/api/apps/v1"
    29  	corev1 "k8s.io/api/core/v1"
    30  	"k8s.io/apimachinery/pkg/api/resource"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/apimachinery/pkg/types"
    34  
    35  	current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1"
    36  	"github.com/IBM-Blockchain/fabric-operator/controllers/mocks"
    37  	v1 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/ca/v1"
    38  	"github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config"
    39  	"github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources"
    40  	dep "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/deployment"
    41  	"github.com/IBM-Blockchain/fabric-operator/pkg/offering/base/ca/override"
    42  	"github.com/IBM-Blockchain/fabric-operator/pkg/util"
    43  
    44  	"sigs.k8s.io/controller-runtime/pkg/client"
    45  	"sigs.k8s.io/yaml"
    46  )
    47  
    48  var _ = Describe("Deployment Overrides", func() {
    49  	var (
    50  		overrider      *override.Override
    51  		instance       *current.IBPCA
    52  		deployment     *appsv1.Deployment
    53  		mockKubeClient *mocks.Client
    54  	)
    55  
    56  	BeforeEach(func() {
    57  		var err error
    58  
    59  		mockKubeClient = &mocks.Client{}
    60  		mockKubeClient.GetStub = func(ctx context.Context, types types.NamespacedName, obj client.Object) error {
    61  			switch obj.(type) {
    62  			case *corev1.ConfigMap:
    63  				hsmConfig := &config.HSMConfig{
    64  					Type:    "hsm",
    65  					Version: "v1",
    66  					MountPaths: []config.MountPath{
    67  						config.MountPath{
    68  							Name:      "hsmcrypto",
    69  							Secret:    "hsmcrypto",
    70  							MountPath: "/hsm",
    71  							Paths: []config.Path{
    72  								{
    73  									Key:  "cafile.pem",
    74  									Path: "cafile.pem",
    75  								},
    76  								{
    77  									Key:  "cert.pem",
    78  									Path: "cert.pem",
    79  								},
    80  								{
    81  									Key:  "key.pem",
    82  									Path: "key.pem",
    83  								},
    84  								{
    85  									Key:  "server.pem",
    86  									Path: "server.pem",
    87  								},
    88  							},
    89  						},
    90  						config.MountPath{
    91  							Name:      "hsmconfig",
    92  							Secret:    "hsmcrypto",
    93  							MountPath: "/etc/Chrystoki.conf",
    94  							SubPath:   "Chrystoki.conf",
    95  							Paths: []config.Path{
    96  								{
    97  									Key:  "Chrystoki.conf",
    98  									Path: "Chrystoki.conf",
    99  								},
   100  							},
   101  						},
   102  					},
   103  					Envs: []corev1.EnvVar{
   104  						{
   105  							Name:  "env1",
   106  							Value: "env1value",
   107  						},
   108  					},
   109  				}
   110  
   111  				configBytes, err := yaml.Marshal(hsmConfig)
   112  				if err != nil {
   113  					return err
   114  				}
   115  				o := obj.(*corev1.ConfigMap)
   116  				o.Data = map[string]string{"ibp-hsm-config.yaml": string(configBytes)}
   117  			}
   118  			return nil
   119  		}
   120  
   121  		overrider = &override.Override{
   122  			Client: mockKubeClient,
   123  		}
   124  		deployment, err = util.GetDeploymentFromFile("../../../../../definitions/ca/deployment.yaml")
   125  		Expect(err).NotTo(HaveOccurred())
   126  		deployment.Spec.Template.Spec.InitContainers[0].Image = "fake-init-image:1234"
   127  		deployment.Spec.Template.Spec.Containers[0].Image = "fake-ca-image:1234"
   128  
   129  		instance = &current.IBPCA{
   130  			ObjectMeta: metav1.ObjectMeta{
   131  				Name:      "override1",
   132  				Namespace: "namespace1",
   133  			},
   134  			Spec: current.IBPCASpec{
   135  				License: current.License{
   136  					Accept: true,
   137  				},
   138  				Storage: &current.CAStorages{},
   139  				Service: &current.Service{},
   140  				Images: &current.CAImages{
   141  					CAImage:     "ca-image",
   142  					CAInitImage: "init-image",
   143  				},
   144  				Arch:             []string{"test-arch"},
   145  				Zone:             "dal",
   146  				Region:           "us-south",
   147  				ImagePullSecrets: []string{"pullsecret"},
   148  				Resources: &current.CAResources{
   149  					CA: &corev1.ResourceRequirements{
   150  						Requests: corev1.ResourceList{
   151  							corev1.ResourceCPU:              resource.MustParse("0.6m"),
   152  							corev1.ResourceMemory:           resource.MustParse("0.4m"),
   153  							corev1.ResourceEphemeralStorage: resource.MustParse("100M"),
   154  						},
   155  						Limits: corev1.ResourceList{
   156  							corev1.ResourceCPU:              resource.MustParse("0.7m"),
   157  							corev1.ResourceMemory:           resource.MustParse("0.5m"),
   158  							corev1.ResourceEphemeralStorage: resource.MustParse("1G"),
   159  						},
   160  					},
   161  				},
   162  			},
   163  		}
   164  	})
   165  
   166  	When("creating a new deployment", func() {
   167  		It("returns an error if license is not accepted", func() {
   168  			instance.Spec.License.Accept = false
   169  			err := overrider.Deployment(instance, deployment, resources.Create)
   170  			Expect(err).To(HaveOccurred())
   171  			Expect(err.Error()).To(Equal("user must accept license before continuing"))
   172  		})
   173  
   174  		It("overrides values in deployment based on CA's instance spec", func() {
   175  			err := overrider.Deployment(instance, deployment, resources.Create)
   176  			Expect(err).NotTo(HaveOccurred())
   177  
   178  			By("setting service account name to be name of CA instance", func() {
   179  				Expect(deployment.Spec.Template.Spec.ServiceAccountName).To(Equal(instance.Name))
   180  			})
   181  
   182  			By("setting image pull secret", func() {
   183  				Expect(deployment.Spec.Template.Spec.ImagePullSecrets[0].Name).To(Equal(instance.Spec.ImagePullSecrets[0]))
   184  			})
   185  
   186  			By("setting resources", func() {
   187  				updated, err := util.GetResourcePatch(&corev1.ResourceRequirements{}, instance.Spec.Resources.CA)
   188  				Expect(err).NotTo(HaveOccurred())
   189  				Expect(deployment.Spec.Template.Spec.Containers[0].Resources).To(Equal(*updated))
   190  			})
   191  
   192  			By("setting affinity", func() {
   193  				affinity := corev1.Affinity{
   194  					NodeAffinity: &corev1.NodeAffinity{
   195  						RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{
   196  							NodeSelectorTerms: []corev1.NodeSelectorTerm{
   197  								corev1.NodeSelectorTerm{
   198  									MatchExpressions: []corev1.NodeSelectorRequirement{
   199  										corev1.NodeSelectorRequirement{
   200  											Key:      "kubernetes.io/arch",
   201  											Operator: corev1.NodeSelectorOpIn,
   202  											Values:   instance.Spec.Arch,
   203  										},
   204  										corev1.NodeSelectorRequirement{
   205  											Key:      "topology.kubernetes.io/zone",
   206  											Operator: corev1.NodeSelectorOpIn,
   207  											Values:   []string{instance.Spec.Zone},
   208  										},
   209  										corev1.NodeSelectorRequirement{
   210  											Key:      "topology.kubernetes.io/region",
   211  											Operator: corev1.NodeSelectorOpIn,
   212  											Values:   []string{instance.Spec.Region},
   213  										},
   214  									},
   215  								},
   216  								corev1.NodeSelectorTerm{
   217  									MatchExpressions: []corev1.NodeSelectorRequirement{
   218  										corev1.NodeSelectorRequirement{
   219  											Key:      "failure-domain.beta.kubernetes.io/zone",
   220  											Operator: corev1.NodeSelectorOpIn,
   221  											Values:   []string{instance.Spec.Zone},
   222  										},
   223  										corev1.NodeSelectorRequirement{
   224  											Key:      "failure-domain.beta.kubernetes.io/region",
   225  											Operator: corev1.NodeSelectorOpIn,
   226  											Values:   []string{instance.Spec.Region},
   227  										},
   228  									},
   229  								},
   230  							},
   231  						},
   232  					},
   233  				}
   234  				affinity.PodAntiAffinity = &corev1.PodAntiAffinity{
   235  					PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{
   236  						corev1.WeightedPodAffinityTerm{
   237  							Weight: 100,
   238  							PodAffinityTerm: corev1.PodAffinityTerm{
   239  								LabelSelector: &metav1.LabelSelector{
   240  									MatchExpressions: []metav1.LabelSelectorRequirement{
   241  										metav1.LabelSelectorRequirement{
   242  											Key:      "app",
   243  											Operator: metav1.LabelSelectorOpIn,
   244  											Values:   []string{instance.Name},
   245  										},
   246  									},
   247  								},
   248  								TopologyKey: "topology.kubernetes.io/zone",
   249  							},
   250  						},
   251  						corev1.WeightedPodAffinityTerm{
   252  							Weight: 100,
   253  							PodAffinityTerm: corev1.PodAffinityTerm{
   254  								LabelSelector: &metav1.LabelSelector{
   255  									MatchExpressions: []metav1.LabelSelectorRequirement{
   256  										metav1.LabelSelectorRequirement{
   257  											Key:      "app",
   258  											Operator: metav1.LabelSelectorOpIn,
   259  											Values:   []string{instance.Name},
   260  										},
   261  									},
   262  								},
   263  								TopologyKey: "failure-domain.beta.kubernetes.io/zone",
   264  							},
   265  						},
   266  					},
   267  				}
   268  				Expect(*deployment.Spec.Template.Spec.Affinity).To(Equal(affinity))
   269  			})
   270  
   271  			By("volumes creating a ca crypto volume", func() {
   272  				volume := corev1.Volume{
   273  					Name: "ca-crypto",
   274  					VolumeSource: corev1.VolumeSource{
   275  						Secret: &corev1.SecretVolumeSource{
   276  							SecretName: instance.Name + "-ca-crypto",
   277  						},
   278  					},
   279  				}
   280  				Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(volume))
   281  			})
   282  
   283  			By("volumes creating a tlsca crypto volume", func() {
   284  				volume := corev1.Volume{
   285  					Name: "tlsca-crypto",
   286  					VolumeSource: corev1.VolumeSource{
   287  						Secret: &corev1.SecretVolumeSource{
   288  							SecretName: instance.Name + "-tlsca-crypto",
   289  						},
   290  					},
   291  				}
   292  				Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(volume))
   293  			})
   294  
   295  			By("volumes creating a ca config volume", func() {
   296  				volume := corev1.Volume{
   297  					Name: "ca-config",
   298  					VolumeSource: corev1.VolumeSource{
   299  						ConfigMap: &corev1.ConfigMapVolumeSource{
   300  							LocalObjectReference: corev1.LocalObjectReference{
   301  								Name: instance.Name + "-ca-config",
   302  							},
   303  						},
   304  					},
   305  				}
   306  				Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(volume))
   307  			})
   308  
   309  			By("volumes creating a tlsca config volume", func() {
   310  				volume := corev1.Volume{
   311  					Name: "tlsca-config",
   312  					VolumeSource: corev1.VolumeSource{
   313  						ConfigMap: &corev1.ConfigMapVolumeSource{
   314  							LocalObjectReference: corev1.LocalObjectReference{
   315  								Name: instance.Name + "-tlsca-config",
   316  							},
   317  						},
   318  					},
   319  				}
   320  				Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(volume))
   321  			})
   322  		})
   323  
   324  		Context("images", func() {
   325  			When("no tag is passed", func() {
   326  				It("uses 'latest' for image tags", func() {
   327  					Expect(deployment.Spec.Template.Spec.InitContainers[0].Image).To(Equal("fake-init-image:1234"))
   328  					Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("fake-ca-image:1234"))
   329  
   330  					err := overrider.Deployment(instance, deployment, resources.Create)
   331  					Expect(err).NotTo(HaveOccurred())
   332  					Expect(deployment.Spec.Template.Spec.InitContainers[0].Image).To(Equal("init-image:latest"))
   333  					Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("ca-image:latest"))
   334  				})
   335  			})
   336  
   337  			When("tag is passed", func() {
   338  				It("uses the passed in tag for image tags", func() {
   339  					instance.Spec.Images.CAInitTag = "2.0.0"
   340  					instance.Spec.Images.CATag = "1.0.0"
   341  
   342  					err := overrider.Deployment(instance, deployment, resources.Create)
   343  					Expect(err).NotTo(HaveOccurred())
   344  					Expect(deployment.Spec.Template.Spec.InitContainers[0].Image).To(Equal("init-image:2.0.0"))
   345  					Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("ca-image:1.0.0"))
   346  				})
   347  			})
   348  		})
   349  
   350  		Context("database overrides", func() {
   351  			When("not using postgres", func() {
   352  				It("performs overrides", func() {
   353  					err := overrider.Deployment(instance, deployment, resources.Create)
   354  					Expect(err).NotTo(HaveOccurred())
   355  
   356  					By("creating a PVC volume", func() {
   357  						volume := corev1.Volume{
   358  							Name: "fabric-ca",
   359  							VolumeSource: corev1.VolumeSource{
   360  								PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
   361  									ClaimName: instance.Name + "-pvc",
   362  								},
   363  							},
   364  						}
   365  						Expect(deployment.Spec.Template.Spec.Volumes).To(ContainElement(volume))
   366  					})
   367  
   368  					By("creating a volume mount for both init and ca containers", func() {
   369  						volumeMount := corev1.VolumeMount{
   370  							Name:      "fabric-ca",
   371  							MountPath: "/data",
   372  							SubPath:   "fabric-ca-server",
   373  						}
   374  						Expect(deployment.Spec.Template.Spec.InitContainers[0].VolumeMounts).To(ContainElement(volumeMount))
   375  						Expect(deployment.Spec.Template.Spec.Containers[0].VolumeMounts).To(ContainElement(volumeMount))
   376  					})
   377  				})
   378  			})
   379  
   380  			When("using postgres", func() {
   381  				BeforeEach(func() {
   382  					instance.Spec.ConfigOverride = &current.ConfigOverride{
   383  						CA:    &runtime.RawExtension{},
   384  						TLSCA: &runtime.RawExtension{},
   385  					}
   386  
   387  					caConfig := &v1.ServerConfig{
   388  						CAConfig: v1.CAConfig{
   389  							DB: &v1.CAConfigDB{
   390  								Type: "postgres",
   391  							},
   392  						},
   393  					}
   394  
   395  					caConfigJson, err := util.ConvertToJsonMessage(caConfig)
   396  					Expect(err).NotTo(HaveOccurred())
   397  					instance.Spec.ConfigOverride.CA = &runtime.RawExtension{Raw: *caConfigJson}
   398  				})
   399  
   400  				It("performs overrides", func() {
   401  					err := overrider.Deployment(instance, deployment, resources.Create)
   402  					Expect(err).NotTo(HaveOccurred())
   403  
   404  					By("creating a volume mount for both init and ca containers", func() {
   405  						volumeMount := corev1.VolumeMount{
   406  							Name:      "shared",
   407  							MountPath: "/data",
   408  						}
   409  						Expect(deployment.Spec.Template.Spec.InitContainers[0].VolumeMounts).To(ContainElement(volumeMount))
   410  						Expect(deployment.Spec.Template.Spec.Containers[0].VolumeMounts).To(ContainElement(volumeMount))
   411  					})
   412  
   413  					By("setting strategy to rolling update", func() {
   414  						Expect(deployment.Spec.Strategy.Type).To(Equal(appsv1.RollingUpdateDeploymentStrategyType))
   415  					})
   416  				})
   417  			})
   418  		})
   419  
   420  		Context("replicas is greater than 1", func() {
   421  			BeforeEach(func() {
   422  				replicas := int32(2)
   423  				instance.Spec.Replicas = &replicas
   424  			})
   425  
   426  			It("returns an error if db is not set in CA override", func() {
   427  				err := overrider.Deployment(instance, deployment, resources.Create)
   428  				Expect(err).To(HaveOccurred())
   429  				Expect(err.Error()).To(Equal("Failed to provide override configuration to support greater than 1 replicas"))
   430  
   431  			})
   432  
   433  			It("returns an error if db is set to not equal postgres in CA override", func() {
   434  				ca := &v1.ServerConfig{
   435  					CAConfig: v1.CAConfig{
   436  						DB: &v1.CAConfigDB{
   437  							Type: "mysql",
   438  						},
   439  					},
   440  				}
   441  				caJson, err := util.ConvertToJsonMessage(ca)
   442  				Expect(err).NotTo(HaveOccurred())
   443  
   444  				instance.Spec.ConfigOverride = &current.ConfigOverride{
   445  					CA: &runtime.RawExtension{Raw: *caJson},
   446  				}
   447  				err = overrider.Deployment(instance, deployment, resources.Create)
   448  				Expect(err).To(HaveOccurred())
   449  				Expect(err.Error()).To(Equal("DB Type in CA config override should be `postgres` to allow replicas > 1"))
   450  			})
   451  
   452  			It("returns an error if datasource is empty in CA override", func() {
   453  				ca := &v1.ServerConfig{
   454  					CAConfig: v1.CAConfig{
   455  						DB: &v1.CAConfigDB{
   456  							Type: "postgres",
   457  						},
   458  					},
   459  				}
   460  				caJson, err := util.ConvertToJsonMessage(ca)
   461  				Expect(err).NotTo(HaveOccurred())
   462  
   463  				instance.Spec.ConfigOverride = &current.ConfigOverride{
   464  					CA:    &runtime.RawExtension{Raw: *caJson},
   465  					TLSCA: &runtime.RawExtension{Raw: *caJson},
   466  				}
   467  
   468  				err = overrider.Deployment(instance, deployment, resources.Create)
   469  				Expect(err).To(HaveOccurred())
   470  				Expect(err.Error()).To(Equal("Datasource in CA config override should not be empty to allow replicas > 1"))
   471  			})
   472  
   473  			It("returns an error if db is not set in TLSCA override", func() {
   474  				ca := &v1.ServerConfig{
   475  					CAConfig: v1.CAConfig{
   476  						DB: &v1.CAConfigDB{
   477  							Type:       "postgres",
   478  							Datasource: "datasource",
   479  						},
   480  					},
   481  				}
   482  				caJson, err := util.ConvertToJsonMessage(ca)
   483  				Expect(err).NotTo(HaveOccurred())
   484  
   485  				instance.Spec.ConfigOverride = &current.ConfigOverride{
   486  					CA: &runtime.RawExtension{Raw: *caJson},
   487  				}
   488  
   489  				err = overrider.Deployment(instance, deployment, resources.Create)
   490  				Expect(err).To(HaveOccurred())
   491  				Expect(err.Error()).To(Equal("Failed to provide database configuration for TLSCA to support greater than 1 replicas"))
   492  			})
   493  
   494  			It("returns an error if db is set to not equal postgres in TLSCA override", func() {
   495  				ca := &v1.ServerConfig{
   496  					CAConfig: v1.CAConfig{
   497  						DB: &v1.CAConfigDB{
   498  							Type:       "postgres",
   499  							Datasource: "fake",
   500  						},
   501  					},
   502  				}
   503  				caJson, err := util.ConvertToJsonMessage(ca)
   504  				Expect(err).NotTo(HaveOccurred())
   505  
   506  				tlsca := &v1.ServerConfig{
   507  					CAConfig: v1.CAConfig{
   508  						DB: &v1.CAConfigDB{
   509  							Type: "mysql",
   510  						},
   511  					},
   512  				}
   513  				tlscaJson, err := util.ConvertToJsonMessage(tlsca)
   514  				Expect(err).NotTo(HaveOccurred())
   515  
   516  				instance.Spec.ConfigOverride = &current.ConfigOverride{
   517  					CA:    &runtime.RawExtension{Raw: *caJson},
   518  					TLSCA: &runtime.RawExtension{Raw: *tlscaJson},
   519  				}
   520  				err = overrider.Deployment(instance, deployment, resources.Create)
   521  				Expect(err).To(HaveOccurred())
   522  				Expect(err.Error()).To(Equal("DB Type in TLSCA config override should be `postgres` to allow replicas > 1"))
   523  			})
   524  
   525  			It("returns an error if datasource is empty in TLSCA override", func() {
   526  				ca := &v1.ServerConfig{
   527  					CAConfig: v1.CAConfig{
   528  						DB: &v1.CAConfigDB{
   529  							Type:       "postgres",
   530  							Datasource: "fake",
   531  						},
   532  					},
   533  				}
   534  				caJson, err := util.ConvertToJsonMessage(ca)
   535  				Expect(err).NotTo(HaveOccurred())
   536  
   537  				tlsca := &v1.ServerConfig{
   538  					CAConfig: v1.CAConfig{
   539  						DB: &v1.CAConfigDB{
   540  							Type: "postgres",
   541  						},
   542  					},
   543  				}
   544  				tlscaJson, err := util.ConvertToJsonMessage(tlsca)
   545  				Expect(err).NotTo(HaveOccurred())
   546  
   547  				instance.Spec.ConfigOverride = &current.ConfigOverride{
   548  					CA:    &runtime.RawExtension{Raw: *caJson},
   549  					TLSCA: &runtime.RawExtension{Raw: *tlscaJson},
   550  				}
   551  
   552  				err = overrider.Deployment(instance, deployment, resources.Create)
   553  				Expect(err).To(HaveOccurred())
   554  				Expect(err.Error()).To(Equal("Datasource in TLSCA config override should not be empty to allow replicas > 1"))
   555  			})
   556  
   557  			It("returns no error if db is set to postgres", func() {
   558  				ca := &v1.ServerConfig{
   559  					CAConfig: v1.CAConfig{
   560  						DB: &v1.CAConfigDB{
   561  							Type:       "postgres",
   562  							Datasource: "fake",
   563  						},
   564  					},
   565  				}
   566  				caBytes, err := json.Marshal(ca)
   567  				Expect(err).NotTo(HaveOccurred())
   568  				caJson := json.RawMessage(caBytes)
   569  
   570  				tlsca := &v1.ServerConfig{
   571  					CAConfig: v1.CAConfig{
   572  						DB: &v1.CAConfigDB{
   573  							Type:       "postgres",
   574  							Datasource: "fake",
   575  						},
   576  					},
   577  				}
   578  				tlscaBytes, err := json.Marshal(tlsca)
   579  				Expect(err).NotTo(HaveOccurred())
   580  				tlscaJson := json.RawMessage(tlscaBytes)
   581  
   582  				instance.Spec.ConfigOverride = &current.ConfigOverride{
   583  					CA:    &runtime.RawExtension{Raw: caJson},
   584  					TLSCA: &runtime.RawExtension{Raw: tlscaJson},
   585  				}
   586  
   587  				err = overrider.Deployment(instance, deployment, resources.Create)
   588  				Expect(err).NotTo(HaveOccurred())
   589  				Expect(deployment.Spec.Strategy.Type).To(Equal(appsv1.RollingUpdateDeploymentStrategyType))
   590  			})
   591  		})
   592  
   593  		Context("Replicas is nil", func() {
   594  			It("returns success", func() {
   595  				instance.Spec.Replicas = nil
   596  				err := overrider.Deployment(instance, deployment, resources.Create)
   597  				Expect(err).NotTo(HaveOccurred())
   598  			})
   599  		})
   600  	})
   601  
   602  	When("updating a deployment", func() {
   603  		Context("images", func() {
   604  			var image *current.CAImages
   605  
   606  			BeforeEach(func() {
   607  				image = &current.CAImages{
   608  					CAImage:     "ca-image",
   609  					CAInitImage: "init-image",
   610  				}
   611  				instance.Spec.Images = image
   612  			})
   613  
   614  			When("no tag is passed", func() {
   615  				It("uses 'latest' for image tags", func() {
   616  					Expect(deployment.Spec.Template.Spec.InitContainers[0].Image).To(Equal("fake-init-image:1234"))
   617  					Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("fake-ca-image:1234"))
   618  
   619  					err := overrider.Deployment(instance, deployment, resources.Create)
   620  					Expect(err).NotTo(HaveOccurred())
   621  					Expect(deployment.Spec.Template.Spec.InitContainers[0].Image).To(Equal("init-image:latest"))
   622  					Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("ca-image:latest"))
   623  				})
   624  			})
   625  
   626  			When("tag is passed", func() {
   627  				It("uses the passed in tag for image tags", func() {
   628  					image.CATag = "1.0.0"
   629  					image.CAInitTag = "2.0.0"
   630  
   631  					err := overrider.Deployment(instance, deployment, resources.Update)
   632  					Expect(err).NotTo(HaveOccurred())
   633  					Expect(deployment.Spec.Template.Spec.InitContainers[0].Image).To(Equal("init-image:2.0.0"))
   634  					Expect(deployment.Spec.Template.Spec.Containers[0].Image).To(Equal("ca-image:1.0.0"))
   635  				})
   636  			})
   637  		})
   638  	})
   639  
   640  	Context("replicas is greater than 1", func() {
   641  		BeforeEach(func() {
   642  			replicas := int32(2)
   643  			instance.Spec.Replicas = &replicas
   644  		})
   645  
   646  		It("returns an error if db is not set in CA override", func() {
   647  			err := overrider.Deployment(instance, deployment, resources.Update)
   648  			Expect(err).To(HaveOccurred())
   649  			Expect(err.Error()).To(Equal("Failed to provide override configuration to support greater than 1 replicas"))
   650  
   651  		})
   652  
   653  		It("returns an error if db is set to not equal postgres in CA override", func() {
   654  			ca := &v1.ServerConfig{
   655  				CAConfig: v1.CAConfig{
   656  					DB: &v1.CAConfigDB{
   657  						Type: "mysql",
   658  					},
   659  				},
   660  			}
   661  			caBytes, err := json.Marshal(ca)
   662  			Expect(err).NotTo(HaveOccurred())
   663  			caJson := json.RawMessage(caBytes)
   664  
   665  			instance.Spec.ConfigOverride = &current.ConfigOverride{
   666  				CA: &runtime.RawExtension{Raw: caJson},
   667  			}
   668  			err = overrider.Deployment(instance, deployment, resources.Update)
   669  			Expect(err).To(HaveOccurred())
   670  			Expect(err.Error()).To(Equal("DB Type in CA config override should be `postgres` to allow replicas > 1"))
   671  		})
   672  
   673  		It("returns an error if datasource is empty in CA override", func() {
   674  			ca := &v1.ServerConfig{
   675  				CAConfig: v1.CAConfig{
   676  					DB: &v1.CAConfigDB{
   677  						Type: "postgres",
   678  					},
   679  				},
   680  			}
   681  			caBytes, err := json.Marshal(ca)
   682  			Expect(err).NotTo(HaveOccurred())
   683  
   684  			tlsca := &v1.ServerConfig{
   685  				CAConfig: v1.CAConfig{
   686  					DB: &v1.CAConfigDB{
   687  						Type: "postgres",
   688  					},
   689  				},
   690  			}
   691  			tlscaBytes, err := json.Marshal(tlsca)
   692  			Expect(err).NotTo(HaveOccurred())
   693  
   694  			instance.Spec.ConfigOverride = &current.ConfigOverride{
   695  				CA:    &runtime.RawExtension{Raw: caBytes},
   696  				TLSCA: &runtime.RawExtension{Raw: tlscaBytes},
   697  			}
   698  
   699  			err = overrider.Deployment(instance, deployment, resources.Update)
   700  			Expect(err).To(HaveOccurred())
   701  			Expect(err.Error()).To(Equal("Datasource in CA config override should not be empty to allow replicas > 1"))
   702  		})
   703  
   704  		It("returns an error if db is not set in TLSCA override", func() {
   705  			ca := &v1.ServerConfig{
   706  				CAConfig: v1.CAConfig{
   707  					DB: &v1.CAConfigDB{
   708  						Type:       "postgres",
   709  						Datasource: "datasource",
   710  					},
   711  				},
   712  			}
   713  			caBytes, err := json.Marshal(ca)
   714  			Expect(err).NotTo(HaveOccurred())
   715  
   716  			instance.Spec.ConfigOverride = &current.ConfigOverride{
   717  				CA: &runtime.RawExtension{Raw: caBytes},
   718  			}
   719  
   720  			err = overrider.Deployment(instance, deployment, resources.Update)
   721  			Expect(err).To(HaveOccurred())
   722  			Expect(err.Error()).To(Equal("Failed to provide database configuration for TLSCA to support greater than 1 replicas"))
   723  		})
   724  
   725  		It("returns an error if db is set to not equal postgres in TLSCA override", func() {
   726  			ca := &v1.ServerConfig{
   727  				CAConfig: v1.CAConfig{
   728  					DB: &v1.CAConfigDB{
   729  						Type:       "postgres",
   730  						Datasource: "fake",
   731  					},
   732  				},
   733  			}
   734  			caBytes, err := json.Marshal(ca)
   735  			Expect(err).NotTo(HaveOccurred())
   736  
   737  			tlsca := &v1.ServerConfig{
   738  				CAConfig: v1.CAConfig{
   739  					DB: &v1.CAConfigDB{
   740  						Type: "mysql",
   741  					},
   742  				},
   743  			}
   744  			tlscaBytes, err := json.Marshal(tlsca)
   745  			Expect(err).NotTo(HaveOccurred())
   746  
   747  			instance.Spec.ConfigOverride = &current.ConfigOverride{
   748  				CA:    &runtime.RawExtension{Raw: caBytes},
   749  				TLSCA: &runtime.RawExtension{Raw: tlscaBytes},
   750  			}
   751  
   752  			err = overrider.Deployment(instance, deployment, resources.Update)
   753  			Expect(err).To(HaveOccurred())
   754  			Expect(err.Error()).To(Equal("DB Type in TLSCA config override should be `postgres` to allow replicas > 1"))
   755  		})
   756  
   757  		It("returns an error if datasource is empty in TLSCA override", func() {
   758  			ca := &v1.ServerConfig{
   759  				CAConfig: v1.CAConfig{
   760  					DB: &v1.CAConfigDB{
   761  						Type:       "postgres",
   762  						Datasource: "fake",
   763  					},
   764  				},
   765  			}
   766  			caBytes, err := json.Marshal(ca)
   767  			Expect(err).NotTo(HaveOccurred())
   768  
   769  			tlsca := &v1.ServerConfig{
   770  				CAConfig: v1.CAConfig{
   771  					DB: &v1.CAConfigDB{
   772  						Type: "postgres",
   773  					},
   774  				},
   775  			}
   776  			tlscaBytes, err := json.Marshal(tlsca)
   777  			Expect(err).NotTo(HaveOccurred())
   778  
   779  			instance.Spec.ConfigOverride = &current.ConfigOverride{
   780  				CA:    &runtime.RawExtension{Raw: caBytes},
   781  				TLSCA: &runtime.RawExtension{Raw: tlscaBytes},
   782  			}
   783  
   784  			err = overrider.Deployment(instance, deployment, resources.Update)
   785  			Expect(err).To(HaveOccurred())
   786  			Expect(err.Error()).To(Equal("Datasource in TLSCA config override should not be empty to allow replicas > 1"))
   787  		})
   788  
   789  		It("returns no error if db is set to postgres", func() {
   790  			ca := &v1.ServerConfig{
   791  				CAConfig: v1.CAConfig{
   792  					DB: &v1.CAConfigDB{
   793  						Type:       "postgres",
   794  						Datasource: "fake",
   795  					},
   796  				},
   797  			}
   798  			caJson, err := util.ConvertToJsonMessage(ca)
   799  			Expect(err).NotTo(HaveOccurred())
   800  
   801  			tlsca := &v1.ServerConfig{
   802  				CAConfig: v1.CAConfig{
   803  					DB: &v1.CAConfigDB{
   804  						Type:       "postgres",
   805  						Datasource: "fake",
   806  					},
   807  				},
   808  			}
   809  			tlscaJson, err := util.ConvertToJsonMessage(tlsca)
   810  			Expect(err).NotTo(HaveOccurred())
   811  
   812  			instance.Spec.ConfigOverride = &current.ConfigOverride{
   813  				CA:    &runtime.RawExtension{Raw: *caJson},
   814  				TLSCA: &runtime.RawExtension{Raw: *tlscaJson},
   815  			}
   816  
   817  			err = overrider.Deployment(instance, deployment, resources.Update)
   818  			Expect(err).NotTo(HaveOccurred())
   819  			Expect(deployment.Spec.Strategy.Type).To(Equal(appsv1.RollingUpdateDeploymentStrategyType))
   820  		})
   821  	})
   822  
   823  	Context("HSM", func() {
   824  		BeforeEach(func() {
   825  			ca := &v1.ServerConfig{
   826  				CAConfig: v1.CAConfig{
   827  					CSP: &v1.BCCSP{
   828  						ProviderName: "PKCS11",
   829  						PKCS11: &v1.PKCS11Opts{
   830  							Label: "partition1",
   831  							Pin:   "B6T9Q7mGNG",
   832  						},
   833  					},
   834  				},
   835  			}
   836  			caJson, err := util.ConvertToJsonMessage(ca)
   837  			Expect(err).NotTo(HaveOccurred())
   838  
   839  			instance.Spec.ConfigOverride = &current.ConfigOverride{
   840  				CA: &runtime.RawExtension{Raw: *caJson},
   841  			}
   842  		})
   843  
   844  		It("sets proxy env on ca container", func() {
   845  			instance.Spec.HSM = &current.HSM{PKCS11Endpoint: "1.2.3.4"}
   846  			err := overrider.Deployment(instance, deployment, resources.Create)
   847  			Expect(err).NotTo(HaveOccurred())
   848  
   849  			d := dep.New(deployment)
   850  			Expect(d.MustGetContainer(override.CA).Env).To(ContainElement(corev1.EnvVar{
   851  				Name:  "PKCS11_PROXY_SOCKET",
   852  				Value: "1.2.3.4",
   853  			}))
   854  		})
   855  
   856  		It("configures deployment to use HSM init image", func() {
   857  			err := overrider.Deployment(instance, deployment, resources.Create)
   858  			Expect(err).NotTo(HaveOccurred())
   859  
   860  			d := dep.New(deployment)
   861  			By("setting volume mounts", func() {
   862  				Expect(d.MustGetContainer(override.CA).VolumeMounts).To(ContainElement(corev1.VolumeMount{
   863  					Name:      "shared",
   864  					MountPath: "/hsm/lib",
   865  					SubPath:   "hsm",
   866  				}))
   867  
   868  				Expect(d.MustGetContainer(override.CA).VolumeMounts).To(ContainElement(corev1.VolumeMount{
   869  					Name:      "hsmconfig",
   870  					MountPath: "/etc/Chrystoki.conf",
   871  					SubPath:   "Chrystoki.conf",
   872  				}))
   873  			})
   874  
   875  			By("setting env vars", func() {
   876  				Expect(d.MustGetContainer(override.CA).Env).To(ContainElement(corev1.EnvVar{
   877  					Name:  "env1",
   878  					Value: "env1value",
   879  				}))
   880  			})
   881  
   882  			By("creating HSM init container", func() {
   883  				Expect(d.ContainerExists("hsm-client")).To(Equal(true))
   884  			})
   885  		})
   886  	})
   887  })