github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/initializer/common/enroller/hsmenroller_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 enroller_test 20 21 import ( 22 "context" 23 24 . "github.com/onsi/ginkgo/v2" 25 . "github.com/onsi/gomega" 26 "github.com/pkg/errors" 27 28 batchv1 "k8s.io/api/batch/v1" 29 corev1 "k8s.io/api/core/v1" 30 "k8s.io/apimachinery/pkg/types" 31 32 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 33 ccmocks "github.com/IBM-Blockchain/fabric-operator/controllers/mocks" 34 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/common" 35 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config" 36 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/enroller" 37 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/enroller/mocks" 38 39 k8sclient "sigs.k8s.io/controller-runtime/pkg/client" 40 ) 41 42 var _ = Describe("HSM sidecar enroller", func() { 43 var ( 44 e *enroller.HSMEnroller 45 ccClient *ccmocks.Client 46 hsmcaClient *mocks.HSMCAClient 47 instance *mocks.Instance 48 ) 49 50 BeforeEach(func() { 51 instance = &mocks.Instance{} 52 instance.GetNameReturns("test") 53 54 ccClient = &ccmocks.Client{ 55 GetStub: func(ctx context.Context, nn types.NamespacedName, obj k8sclient.Object) error { 56 switch obj.(type) { 57 case *batchv1.Job: 58 j := obj.(*batchv1.Job) 59 j.Status.Active = int32(1) 60 j.Name = "test-job" 61 } 62 return nil 63 }, 64 ListStub: func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 65 switch obj.(type) { 66 case *corev1.PodList: 67 p := obj.(*corev1.PodList) 68 p.Items = []corev1.Pod{{ 69 Status: corev1.PodStatus{ 70 ContainerStatuses: []corev1.ContainerStatus{ 71 { 72 State: corev1.ContainerState{ 73 Terminated: &corev1.ContainerStateTerminated{}, 74 }, 75 }, 76 }, 77 Phase: corev1.PodSucceeded, 78 }, 79 }} 80 } 81 return nil 82 }, 83 } 84 85 hsmcaClient = &mocks.HSMCAClient{} 86 hsmcaClient.GetEnrollmentRequestReturns(¤t.Enrollment{ 87 CATLS: ¤t.CATLS{ 88 CACert: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNGakNDQWIyZ0F3SUJBZ0lVZi84bk94M2NqM1htVzNDSUo1L0Q1ejRRcUVvd0NnWUlLb1pJemowRUF3SXcKYURFTE1Ba0dBMVVFQmhNQ1ZWTXhGekFWQmdOVkJBZ1REazV2Y25Sb0lFTmhjbTlzYVc1aE1SUXdFZ1lEVlFRSwpFd3RJZVhCbGNteGxaR2RsY2pFUE1BMEdBMVVFQ3hNR1JtRmljbWxqTVJrd0Z3WURWUVFERXhCbVlXSnlhV010ClkyRXRjMlZ5ZG1WeU1CNFhEVEU1TVRBek1ERTNNamd3TUZvWERUTTBNVEF5TmpFM01qZ3dNRm93YURFTE1Ba0cKQTFVRUJoTUNWVk14RnpBVkJnTlZCQWdURGs1dmNuUm9JRU5oY205c2FXNWhNUlF3RWdZRFZRUUtFd3RJZVhCbApjbXhsWkdkbGNqRVBNQTBHQTFVRUN4TUdSbUZpY21sak1Sa3dGd1lEVlFRREV4Qm1ZV0p5YVdNdFkyRXRjMlZ5CmRtVnlNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVSbzNmbUc2UHkyUHd6cUMwNnFWZDlFOFgKZ044eldqZzFMb3lnMmsxdkQ4MXY1dENRRytCTVozSUJGQnI2VTRhc0tZTUREakd6TElERmdUUTRjVDd1VktORgpNRU13RGdZRFZSMFBBUUgvQkFRREFnRUdNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUV3SFFZRFZSME9CQllFCkZFa0RtUHhjbTdGcXZSMXllN0tNNGdLLy9KZ1JNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRC92QVFVSEh2SWwKQWZZLzM5UWdEU2ltTWpMZnhPTG44NllyR1EvWHpkQVpBaUFpUmlyZmlMdzVGbXBpRDhtYmlmRjV4bzdFUzdqNApaUWQyT0FUNCt5OWE0Zz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", 89 }, 90 }) 91 92 hsmConfig := &config.HSMConfig{ 93 Type: "hsm", 94 Version: "v1", 95 Library: config.Library{ 96 FilePath: "/usr/lib/libCryptoki2_64.so", 97 Image: "ghcr.io/ibm-blockchain/ibp-pkcs11-proxy/gemalto-client:skarim-amd64", 98 Auth: &config.Auth{ 99 ImagePullSecret: "hsmpullsecret", 100 }, 101 }, 102 Envs: []corev1.EnvVar{ 103 { 104 Name: "DUMMY_ENV_NAME", 105 Value: "DUMMY_ENV_VALUE", 106 }, 107 }, 108 MountPaths: []config.MountPath{ 109 { 110 Name: "hsmcrypto", 111 Secret: "hsmcrypto", 112 MountPath: "/hsm", 113 Paths: []config.Path{ 114 { 115 Key: "cafile.pem", 116 Path: "cafile.pem", 117 }, 118 }, 119 }, 120 { 121 Name: "hsmconfig", 122 Secret: "hsmcrypto", 123 MountPath: "/etc/Chrystoki.conf", 124 SubPath: "Chrystoki.conf", 125 }, 126 }, 127 } 128 129 e = &enroller.HSMEnroller{ 130 Config: hsmConfig, 131 Client: ccClient, 132 Instance: instance, 133 CAClient: hsmcaClient, 134 Timeouts: enroller.HSMEnrollJobTimeouts{ 135 JobStart: common.MustParseDuration("1s"), 136 JobCompletion: common.MustParseDuration("1s"), 137 }, 138 } 139 }) 140 141 Context("enroll", func() { 142 It("returns error if creating ca crypto secret fails", func() { 143 ccClient.CreateReturnsOnCall(0, errors.New("failed to create root TLS secret")) 144 _, err := e.Enroll() 145 Expect(err).To(MatchError(ContainSubstring("failed to create root TLS secret"))) 146 }) 147 148 It("returns error if creating ca config map fails", func() { 149 ccClient.CreateReturnsOnCall(1, errors.New("failed to create ca config map")) 150 _, err := e.Enroll() 151 Expect(err).To(MatchError(ContainSubstring("failed to create ca config map"))) 152 }) 153 154 It("returns error if creating job fails", func() { 155 ccClient.CreateReturnsOnCall(2, errors.New("failed to create job")) 156 _, err := e.Enroll() 157 Expect(err).To(MatchError(ContainSubstring("failed to create job"))) 158 }) 159 160 Context("job start timeout", func() { 161 BeforeEach(func() { 162 ccClient.GetStub = func(ctx context.Context, nn types.NamespacedName, obj k8sclient.Object) error { 163 switch obj.(type) { 164 case *batchv1.Job: 165 j := obj.(*batchv1.Job) 166 j.Status.Active = int32(0) 167 j.Name = "test-job" 168 169 } 170 return nil 171 } 172 }) 173 174 It("returns error if job doesn't start before timeout", func() { 175 _, err := e.Enroll() 176 Expect(err).To(MatchError(ContainSubstring("job failed to start"))) 177 }) 178 }) 179 180 Context("job fails", func() { 181 When("job timesout", func() { 182 BeforeEach(func() { 183 ccClient.ListStub = func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 184 switch obj.(type) { 185 case *corev1.PodList: 186 p := obj.(*corev1.PodList) 187 p.Items = []corev1.Pod{} 188 } 189 return nil 190 } 191 }) 192 193 It("returns error", func() { 194 _, err := e.Enroll() 195 Expect(err).To(MatchError(ContainSubstring("failed to finish"))) 196 }) 197 }) 198 199 When("pod enters failed state", func() { 200 BeforeEach(func() { 201 ccClient.ListStub = func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 202 switch obj.(type) { 203 case *corev1.PodList: 204 p := obj.(*corev1.PodList) 205 p.Items = []corev1.Pod{{ 206 Status: corev1.PodStatus{ 207 Phase: corev1.PodFailed, 208 }, 209 }} 210 } 211 return nil 212 } 213 }) 214 215 It("returns error", func() { 216 _, err := e.Enroll() 217 Expect(err).To(MatchError(ContainSubstring("finished unsuccessfully, not cleaning up pods to allow for error"))) 218 }) 219 }) 220 }) 221 222 It("returns no error on successfull enroll", func() { 223 resp, err := e.Enroll() 224 Expect(err).NotTo(HaveOccurred()) 225 Expect(resp).NotTo(BeNil()) 226 227 By("creating a job resource", func() { 228 _, obj, _ := ccClient.CreateArgsForCall(2) 229 Expect(obj).NotTo(BeNil()) 230 231 job := obj.(*batchv1.Job) 232 Expect(job.Spec.Template.Spec.Containers[0].Env).To(Equal([]corev1.EnvVar{ 233 { 234 Name: "DUMMY_ENV_NAME", 235 Value: "DUMMY_ENV_VALUE", 236 }, 237 })) 238 239 Expect(job.Spec.Template.Spec.Containers[0].VolumeMounts).To(ContainElements([]corev1.VolumeMount{ 240 { 241 Name: "hsmcrypto", 242 MountPath: "/hsm", 243 }, 244 { 245 Name: "hsmconfig", 246 MountPath: "/etc/Chrystoki.conf", 247 SubPath: "Chrystoki.conf", 248 }, 249 })) 250 251 Expect(job.Spec.Template.Spec.Volumes).To(ContainElements([]corev1.Volume{ 252 { 253 Name: "hsmconfig", 254 VolumeSource: corev1.VolumeSource{ 255 Secret: &corev1.SecretVolumeSource{ 256 SecretName: "hsmcrypto", 257 }, 258 }, 259 }, 260 { 261 Name: "hsmcrypto", 262 VolumeSource: corev1.VolumeSource{ 263 Secret: &corev1.SecretVolumeSource{ 264 SecretName: "hsmcrypto", 265 Items: []corev1.KeyToPath{ 266 { 267 Key: "cafile.pem", 268 Path: "cafile.pem", 269 }, 270 }, 271 }, 272 }, 273 }, 274 })) 275 }) 276 277 By("deleting completed job", func() { 278 // One delete to clean up ca config map before starting job 279 // Second delete to delete job 280 // Third delete to delete associated pod 281 // Fourth delete to delete root tls secret 282 // Fifth delete to delete ca config map 283 Expect(ccClient.DeleteCallCount()).To(Equal(5)) 284 }) 285 286 By("setting controller reference on resources created by enroll job", func() { 287 Expect(ccClient.UpdateCallCount()).To(Equal(4)) 288 }) 289 }) 290 }) 291 292 // TODO: Add more tests for error path testing 293 })