github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/initializer/ca/hsm_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 initializer_test 20 21 import ( 22 "context" 23 "fmt" 24 25 . "github.com/onsi/ginkgo/v2" 26 . "github.com/onsi/gomega" 27 "github.com/pkg/errors" 28 29 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 30 v1 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/ca/v1" 31 "github.com/IBM-Blockchain/fabric-operator/pkg/apis/common" 32 initializer "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/ca" 33 caconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/ca/config" 34 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/ca/mocks" 35 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config" 36 37 batchv1 "k8s.io/api/batch/v1" 38 corev1 "k8s.io/api/core/v1" 39 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 40 "k8s.io/apimachinery/pkg/types" 41 42 k8sclient "sigs.k8s.io/controller-runtime/pkg/client" 43 ) 44 45 var _ = Describe("HSM CA initializer", func() { 46 var ( 47 client *mocks.Client 48 ca *mocks.IBPCA 49 // defaultConfig *mocks.CAConfig 50 51 hsmca *initializer.HSM 52 instance *current.IBPCA 53 ) 54 55 BeforeEach(func() { 56 client = &mocks.Client{ 57 GetStub: func(ctx context.Context, nn types.NamespacedName, obj k8sclient.Object) error { 58 switch obj.(type) { 59 case *batchv1.Job: 60 j := obj.(*batchv1.Job) 61 j.Status.Active = int32(1) 62 j.Name = "test-job" 63 } 64 return nil 65 }, 66 ListStub: func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 67 switch obj.(type) { 68 case *corev1.PodList: 69 pods := obj.(*corev1.PodList) 70 pods.Items = []corev1.Pod{ 71 { 72 Status: corev1.PodStatus{ 73 Phase: corev1.PodSucceeded, 74 ContainerStatuses: []corev1.ContainerStatus{ 75 { 76 State: corev1.ContainerState{ 77 Terminated: &corev1.ContainerStateTerminated{}, 78 }, 79 }, 80 }, 81 }, 82 }, 83 } 84 } 85 return nil 86 }, 87 } 88 89 hsmConfig := &config.HSMConfig{ 90 Type: "hsm", 91 Version: "v1", 92 Library: config.Library{ 93 FilePath: "/usr/lib/libCryptoki2_64.so", 94 Image: "ghcr.io/ibm-blockchain/gemalto-client:skarim-amd64", 95 Auth: &config.Auth{ 96 ImagePullSecret: "hsmpullsecret", 97 }, 98 }, 99 Envs: []corev1.EnvVar{ 100 { 101 Name: "DUMMY_ENV_NAME", 102 Value: "DUMMY_ENV_VALUE", 103 }, 104 }, 105 MountPaths: []config.MountPath{ 106 { 107 Name: "hsmcrypto", 108 Secret: "hsmcrypto", 109 MountPath: "/hsm", 110 Paths: []config.Path{ 111 { 112 Key: "cafile.pem", 113 Path: "cafile.pem", 114 }, 115 }, 116 }, 117 { 118 Name: "hsmconfig", 119 Secret: "hsmcrypto", 120 MountPath: "/etc/Chrystoki.conf", 121 SubPath: "Chrystoki.conf", 122 }, 123 }, 124 } 125 126 ca = &mocks.IBPCA{} 127 ca.GetServerConfigReturns(&v1.ServerConfig{ 128 CAConfig: v1.CAConfig{ 129 CSP: &v1.BCCSP{ 130 PKCS11: &v1.PKCS11Opts{}, 131 }, 132 }, 133 }) 134 ca.GetTypeReturns(caconfig.EnrollmentCA) 135 136 instance = ¤t.IBPCA{ 137 ObjectMeta: metav1.ObjectMeta{ 138 Name: "test-ibpca", 139 }, 140 Spec: current.IBPCASpec{ 141 Resources: ¤t.CAResources{ 142 CA: &corev1.ResourceRequirements{}, 143 Init: &corev1.ResourceRequirements{}, 144 }, 145 Images: ¤t.CAImages{}, 146 }, 147 } 148 149 hsmca = &initializer.HSM{ 150 Config: hsmConfig, 151 Timeouts: initializer.HSMInitJobTimeouts{ 152 JobStart: common.MustParseDuration("1s"), 153 JobCompletion: common.MustParseDuration("1s"), 154 }, 155 Client: client, 156 } 157 }) 158 159 Context("creates", func() { 160 It("returns error if overriding server config fails", func() { 161 ca.OverrideServerConfigReturns(errors.New("override failed")) 162 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 163 Expect(err).To(MatchError("override failed")) 164 }) 165 166 It("returns error if creating ca crypto secret fails", func() { 167 client.CreateReturnsOnCall(0, errors.New("failed to create crypto secret")) 168 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 169 Expect(err).To(MatchError(ContainSubstring("failed to create crypto secret"))) 170 }) 171 172 It("returns error if creating ca config map fails", func() { 173 client.CreateReturnsOnCall(1, errors.New("failed to create config map")) 174 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 175 Expect(err).To(MatchError(ContainSubstring("failed to create config map"))) 176 }) 177 178 It("returns error if creating job fails", func() { 179 client.CreateReturnsOnCall(2, errors.New("failed to create job")) 180 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 181 Expect(err).To(MatchError(ContainSubstring("failed to create job"))) 182 }) 183 184 Context("job start timeout", func() { 185 BeforeEach(func() { 186 client.GetStub = func(ctx context.Context, nn types.NamespacedName, obj k8sclient.Object) error { 187 switch obj.(type) { 188 case *batchv1.Job: 189 j := obj.(*batchv1.Job) 190 j.Status.Active = int32(0) 191 j.Name = "test-job" 192 193 } 194 return nil 195 } 196 }) 197 198 It("returns error if job doesn't start before timeout", func() { 199 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 200 Expect(err).To(MatchError(ContainSubstring("job failed to start"))) 201 }) 202 }) 203 204 Context("job fails", func() { 205 When("job timesout", func() { 206 BeforeEach(func() { 207 client.ListStub = func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 208 switch obj.(type) { 209 case *corev1.PodList: 210 p := obj.(*corev1.PodList) 211 p.Items = []corev1.Pod{} 212 } 213 return nil 214 } 215 }) 216 217 It("returns error", func() { 218 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 219 Expect(err).To(MatchError(ContainSubstring("failed to finish"))) 220 }) 221 }) 222 223 When("pod enters failed state", func() { 224 BeforeEach(func() { 225 client.ListStub = func(ctx context.Context, obj k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 226 switch obj.(type) { 227 case *corev1.PodList: 228 p := obj.(*corev1.PodList) 229 p.Items = []corev1.Pod{{ 230 Status: corev1.PodStatus{ 231 Phase: corev1.PodFailed, 232 }, 233 }} 234 } 235 return nil 236 } 237 }) 238 239 It("returns error", func() { 240 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 241 Expect(err).To(MatchError(ContainSubstring(fmt.Sprintf("check job '%s' pods for errors", instance.GetName()+"-ca-init")))) 242 }) 243 }) 244 }) 245 246 It("returns error if unable to delete job after success", func() { 247 client.DeleteReturns(errors.New("failed to delete job")) 248 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 249 Expect(err).To(MatchError(ContainSubstring("failed to delete job"))) 250 }) 251 252 It("returns error if unable to update ca config map", func() { 253 client.UpdateReturns(errors.New("failed to update ca config map")) 254 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 255 Expect(err).To(MatchError(ContainSubstring("failed to update ca config map"))) 256 }) 257 258 It("returns sucessfully with no error and nil response", func() { 259 _, err := hsmca.Create(instance, &v1.ServerConfig{}, ca) 260 Expect(err).NotTo(HaveOccurred()) 261 262 By("creating a job resource", func() { 263 _, obj, _ := client.CreateArgsForCall(2) 264 Expect(obj).NotTo(BeNil()) 265 266 job := obj.(*batchv1.Job) 267 Expect(job.Spec.Template.Spec.Containers[0].Env).To(ContainElements( 268 corev1.EnvVar{ 269 Name: "DUMMY_ENV_NAME", 270 Value: "DUMMY_ENV_VALUE", 271 }, 272 )) 273 274 Expect(job.Spec.Template.Spec.Containers[0].VolumeMounts).To(ContainElements([]corev1.VolumeMount{ 275 { 276 Name: "hsmcrypto", 277 MountPath: "/hsm", 278 }, 279 { 280 Name: "hsmconfig", 281 MountPath: "/etc/Chrystoki.conf", 282 SubPath: "Chrystoki.conf", 283 }, 284 })) 285 286 Expect(job.Spec.Template.Spec.Volumes).To(ContainElements([]corev1.Volume{ 287 { 288 Name: "hsmconfig", 289 VolumeSource: corev1.VolumeSource{ 290 Secret: &corev1.SecretVolumeSource{ 291 SecretName: "hsmcrypto", 292 }, 293 }, 294 }, 295 { 296 Name: "hsmcrypto", 297 VolumeSource: corev1.VolumeSource{ 298 Secret: &corev1.SecretVolumeSource{ 299 SecretName: "hsmcrypto", 300 Items: []corev1.KeyToPath{ 301 { 302 Key: "cafile.pem", 303 Path: "cafile.pem", 304 }, 305 }, 306 }, 307 }, 308 }, 309 })) 310 }) 311 312 By("deleting completed job", func() { 313 // One delete count to delete job and second delete count to delete associated pod 314 Expect(client.DeleteCallCount()).To(Equal(2)) 315 }) 316 317 By("updating config map if enrollment CA", func() { 318 Expect(client.UpdateCallCount()).To(Equal(1)) 319 }) 320 }) 321 }) 322 })