github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/initializer/orderer/initializer.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 20 21 import ( 22 "context" 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "strings" 28 29 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 30 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common" 31 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config" 32 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/enroller" 33 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/secretmanager" 34 ordererconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/orderer/config/v1" 35 v2ordererconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/orderer/config/v2" 36 v24ordererconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/orderer/config/v24" 37 "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient" 38 k8sclient "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient" 39 "github.com/IBM-Blockchain/fabric-operator/pkg/util" 40 "github.com/IBM-Blockchain/fabric-operator/version" 41 "github.com/pkg/errors" 42 k8serrors "k8s.io/apimachinery/pkg/api/errors" 43 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 44 "k8s.io/apimachinery/pkg/runtime" 45 46 corev1 "k8s.io/api/core/v1" 47 logf "sigs.k8s.io/controller-runtime/pkg/log" 48 ) 49 50 var log = logf.Log.WithName("orderer_initializer") 51 52 type Config struct { 53 ConfigTxFile string 54 OrdererFile string 55 OrdererV2File string 56 OrdererV24File string 57 OUFile string 58 InterOUFile string 59 DeploymentFile string 60 PVCFile string 61 ServiceFile string 62 CMFile string 63 RoleFile string 64 ServiceAccountFile string 65 RoleBindingFile string 66 IngressFile string 67 Ingressv1beta1File string 68 RouteFile string 69 StoragePath string 70 } 71 72 type Response struct { 73 Config OrdererConfig 74 Crypto *config.CryptoResponse 75 } 76 77 //go:generate counterfeiter -o mocks/ibporderer.go -fake-name IBPOrderer . IBPOrderer 78 79 type IBPOrderer interface { 80 OverrideConfig(newConfig OrdererConfig) error 81 GenerateCrypto() (*config.CryptoResponse, error) 82 GetConfig() OrdererConfig 83 } 84 85 type Initializer struct { 86 Config *Config 87 Scheme *runtime.Scheme 88 Client k8sclient.Client 89 Name string 90 Timeouts enroller.HSMEnrollJobTimeouts 91 92 Validator common.CryptoValidator 93 SecretManager *secretmanager.SecretManager 94 } 95 96 func New(client controllerclient.Client, scheme *runtime.Scheme, cfg *Config, name string, validator common.CryptoValidator) *Initializer { 97 initializer := &Initializer{ 98 Client: client, 99 Scheme: scheme, 100 Config: cfg, 101 Name: name, 102 Validator: validator, 103 } 104 105 initializer.SecretManager = secretmanager.New(client, scheme, initializer.GetLabels) 106 107 return initializer 108 } 109 110 func (i *Initializer) Create(overrides OrdererConfig, orderer IBPOrderer, storagePath string) (*Response, error) { 111 var err error 112 113 log.Info(fmt.Sprintf("Creating orderer %s's config and crypto...", i.Name)) 114 115 err = os.RemoveAll(storagePath) 116 if err != nil { 117 return nil, err 118 } 119 120 err = orderer.OverrideConfig(overrides) 121 if err != nil { 122 return nil, err 123 } 124 125 cresp, err := orderer.GenerateCrypto() 126 if err != nil { 127 return nil, err 128 } 129 130 err = os.RemoveAll(storagePath) 131 if err != nil { 132 return nil, err 133 } 134 135 return &Response{ 136 Config: orderer.GetConfig(), 137 Crypto: cresp, 138 }, nil 139 } 140 141 func (i *Initializer) Update(overrides OrdererConfig, orderer IBPOrderer) (*Response, error) { 142 var err error 143 144 log.Info(fmt.Sprintf("Updating orderer %s's config...", i.Name)) 145 146 err = orderer.OverrideConfig(overrides) 147 if err != nil { 148 return nil, err 149 } 150 151 return &Response{ 152 Config: orderer.GetConfig(), 153 }, nil 154 } 155 156 func (i *Initializer) GetEnrollers(cryptos *config.Cryptos, instance *current.IBPOrderer, storagePath string) error { 157 // If no enrollment information provided, don't need to proceed further 158 if instance.Spec.Secret == nil || instance.Spec.Secret.Enrollment == nil { 159 return nil 160 } 161 162 enrollmentSpec := instance.Spec.Secret.Enrollment 163 if enrollmentSpec.Component != nil && cryptos.Enrollment == nil { 164 bytes, err := enrollmentSpec.Component.GetCATLSBytes() 165 if err != nil { 166 return err 167 } 168 169 cryptos.Enrollment, err = enroller.Factory(enrollmentSpec.Component, i.Client, instance, 170 filepath.Join(storagePath, "ecert"), 171 i.Scheme, 172 bytes, 173 i.Timeouts, 174 ) 175 if err != nil { 176 return err 177 } 178 } 179 180 // err := common.GetSWEnrollers(cryptos, enrollmentSpec, storagePath) 181 err := common.GetCommonEnrollers(cryptos, enrollmentSpec, storagePath) 182 if err != nil { 183 return err 184 } 185 186 return nil 187 } 188 189 func (i *Initializer) GetMSPCrypto(cryptos *config.Cryptos, instance *current.IBPOrderer) error { 190 mspSpec := instance.Spec.Secret.MSP 191 if mspSpec != nil { 192 err := common.GetMSPCrypto(cryptos, mspSpec) 193 if err != nil { 194 return err 195 } 196 } 197 198 return nil 199 } 200 201 func (i *Initializer) GetInitOrderer(instance *current.IBPOrderer, storagePath string) (*Orderer, error) { 202 cryptos := &config.Cryptos{} 203 204 if instance.Spec.Secret != nil { 205 // Prioritize any crypto passed through MSP spec first 206 err := i.GetMSPCrypto(cryptos, instance) 207 if err != nil { 208 return nil, errors.Wrap(err, "failed to populate init orderer with MSP spec") 209 } 210 211 err = i.GetEnrollers(cryptos, instance, storagePath) 212 if err != nil { 213 return nil, errors.Wrap(err, "failed to populate init orderer with Enrollment spec") 214 } 215 } 216 217 return &Orderer{ 218 Cryptos: cryptos, 219 }, nil 220 } 221 222 func (i *Initializer) GetUpdatedOrderer(instance *current.IBPOrderer) (*Orderer, error) { 223 cryptos := &config.Cryptos{} 224 225 // Only check for any new certs passed through MSP spec 226 err := i.GetMSPCrypto(cryptos, instance) 227 if err != nil { 228 return nil, errors.Wrap(err, "failed to populate updated init orderer with MSP spec") 229 } 230 231 return &Orderer{ 232 Cryptos: cryptos, 233 }, nil 234 } 235 236 func (i *Initializer) GenerateSecrets(prefix common.SecretType, instance *current.IBPOrderer, crypto *config.Response) error { 237 if crypto == nil { 238 return nil 239 } 240 return i.SecretManager.GenerateSecrets(prefix, instance, crypto) 241 } 242 243 func (i *Initializer) GenerateSecretsFromResponse(instance *current.IBPOrderer, cryptoResponse *config.CryptoResponse) error { 244 return i.SecretManager.GenerateSecretsFromResponse(instance, cryptoResponse) 245 } 246 247 func (i *Initializer) UpdateSecrets(prefix common.SecretType, instance *current.IBPOrderer, crypto *config.Response) error { 248 if crypto == nil { 249 return nil 250 } 251 return i.SecretManager.UpdateSecrets(prefix, instance, crypto) 252 } 253 254 func (i *Initializer) UpdateSecretsFromResponse(instance *current.IBPOrderer, cryptoResponse *config.CryptoResponse) error { 255 return i.SecretManager.UpdateSecretsFromResponse(instance, cryptoResponse) 256 } 257 258 func (i *Initializer) GetCrypto(instance *current.IBPOrderer) (*config.CryptoResponse, error) { 259 return i.SecretManager.GetCryptoResponseFromSecrets(instance) 260 } 261 262 func (i *Initializer) Delete(instance *current.IBPOrderer) error { 263 name := fmt.Sprintf("%s%s", instance.Name, i.Name) 264 prefix := "ecert" 265 err := i.SecretManager.DeleteSecrets(prefix, instance, name) 266 if err != nil { 267 return err 268 } 269 270 prefix = "tls" 271 err = i.SecretManager.DeleteSecrets(prefix, instance, name) 272 if err != nil { 273 return err 274 } 275 276 cm := &corev1.ConfigMap{} 277 cm.Name = instance.Name + "-" + i.Name + "-config" 278 cm.Namespace = instance.Namespace 279 280 err = i.Client.Delete(context.TODO(), cm) 281 if err != nil { 282 if !k8serrors.IsNotFound(err) { 283 return errors.Wrapf(err, "failed to delete config map '%s'", cm.Name) 284 } 285 } 286 287 return nil 288 } 289 290 func (i *Initializer) MissingCrypto(instance *current.IBPOrderer) bool { 291 isHSMEnabled := instance.IsHSMEnabled() 292 if isHSMEnabled { 293 i.Validator.SetHSMEnabled(true) 294 } 295 296 checkClientAuth := instance.ClientAuthCryptoSet() 297 err := common.CheckCrypto(i.Validator, instance, checkClientAuth) 298 if err != nil { 299 log.Info(err.Error()) 300 return true 301 } 302 303 return false 304 } 305 306 func (i *Initializer) CreateOrUpdateConfigMap(instance *current.IBPOrderer, orderer OrdererConfig) error { 307 name := fmt.Sprintf("%s-config", instance.GetName()) 308 log.Info(fmt.Sprintf("Creating/Updating config map '%s'...", name)) 309 310 cm := &corev1.ConfigMap{ 311 ObjectMeta: metav1.ObjectMeta{ 312 Name: name, 313 Namespace: instance.GetNamespace(), 314 Labels: i.GetLabels(instance), 315 }, 316 BinaryData: map[string][]byte{}, 317 } 318 319 existing, err := i.GetConfigFromConfigMap(instance) 320 if err != nil { 321 if !k8serrors.IsNotFound(err) { 322 return err 323 } 324 } 325 if existing != nil { 326 cm.BinaryData = existing.BinaryData 327 } 328 329 if orderer != nil { 330 err := i.addOrdererConfigToCM(instance, cm, orderer) 331 if err != nil { 332 return err 333 } 334 } 335 336 err = i.addNodeOUToCM(instance, cm) 337 if err != nil { 338 return err 339 } 340 341 err = i.Client.CreateOrUpdate(context.TODO(), cm, k8sclient.CreateOrUpdateOption{ 342 Owner: instance, 343 Scheme: i.Scheme, 344 }) 345 if err != nil { 346 return errors.Wrap(err, "failed to create Orderer config map") 347 } 348 349 return nil 350 } 351 352 func (i *Initializer) addOrdererConfigToCM(instance *current.IBPOrderer, cm *corev1.ConfigMap, orderer OrdererConfig) error { 353 ordererBytes, err := orderer.ToBytes() 354 if err != nil { 355 return err 356 } 357 cm.BinaryData["orderer.yaml"] = ordererBytes 358 359 return nil 360 } 361 362 func (i *Initializer) addNodeOUToCM(instance *current.IBPOrderer, cm *corev1.ConfigMap) error { 363 if !instance.Spec.NodeOUDisabled() { 364 configFilePath := i.Config.OUFile 365 // Check if both intermediate ecerts and tlscerts secrets exists 366 if util.IntermediateSecretExists(i.Client, instance.Namespace, fmt.Sprintf("ecert-%s-intercerts", instance.Name)) && 367 util.IntermediateSecretExists(i.Client, instance.Namespace, fmt.Sprintf("tls-%s-intercerts", instance.Name)) { 368 configFilePath = i.Config.InterOUFile 369 } 370 ouBytes, err := ioutil.ReadFile(filepath.Clean(configFilePath)) 371 if err != nil { 372 return err 373 } 374 cm.BinaryData["config.yaml"] = ouBytes 375 } else { 376 // Set enabled to false in config 377 nodeOUConfig, err := config.NodeOUConfigFromBytes(cm.BinaryData["config.yaml"]) 378 if err != nil { 379 return err 380 } 381 382 nodeOUConfig.NodeOUs.Enable = false 383 ouBytes, err := config.NodeOUConfigToBytes(nodeOUConfig) 384 if err != nil { 385 return err 386 } 387 388 cm.BinaryData["config.yaml"] = ouBytes 389 } 390 391 return nil 392 } 393 394 func (i *Initializer) GetConfigFromConfigMap(instance *current.IBPOrderer) (*corev1.ConfigMap, error) { 395 return common.GetConfigFromConfigMap(i.Client, instance) 396 } 397 398 func GetDomain(address string) string { 399 u := strings.Split(address, ":") 400 return u[0] 401 } 402 403 func (i *Initializer) GetLabels(instance metav1.Object) map[string]string { 404 label := os.Getenv("OPERATOR_LABEL_PREFIX") 405 if label == "" { 406 label = "fabric" 407 } 408 409 return map[string]string{ 410 "app": instance.GetName(), 411 "app.kubernetes.io/name": label, 412 "app.kubernetes.io/instance": label + "orderer", 413 "app.kubernetes.io/managed-by": label + "-operator", 414 } 415 } 416 417 func (i *Initializer) CheckIfAdminCertsUpdated(instance *current.IBPOrderer) (bool, error) { 418 log.Info("Checking if admin certs updated") 419 current := common.GetAdminCertsFromSecret(i.Client, instance) 420 updated := common.GetAdminCertsFromSpec(instance.Spec.Secret) 421 422 return common.CheckIfCertsDifferent(current, updated) 423 } 424 425 func (i *Initializer) UpdateAdminSecret(instance *current.IBPOrderer) error { 426 return i.SecretManager.UpdateAdminCertSecret(instance, instance.Spec.Secret) 427 } 428 429 func (i *Initializer) GetCoreConfigFromFile(instance *current.IBPOrderer, file string) (OrdererConfig, error) { 430 switch version.GetMajorReleaseVersion(instance.Spec.FabricVersion) { 431 case version.V2: 432 currentVer := version.String(instance.Spec.FabricVersion) 433 if currentVer.EqualWithoutTag(version.V2_4_1) || currentVer.GreaterThan(version.V2_4_1) { 434 log.Info("v2.4.x Fabric Orderer requested") 435 v24config, err := v24ordererconfig.ReadOrdererFile(file) 436 if err != nil { 437 return nil, errors.Wrap(err, "failed to read v2.4.x default config file") 438 } 439 return v24config, nil 440 } else { 441 log.Info("v2.2.x Fabric Orderer requested") 442 v2config, err := v2ordererconfig.ReadOrdererFile(file) 443 if err != nil { 444 return nil, errors.Wrap(err, "failed to read v2.2.x default config file") 445 } 446 return v2config, nil 447 } 448 case version.V1: 449 fallthrough 450 default: 451 // Choosing to default to v1.4 to not break backwards comptability, if coming 452 // from a previous version of operator the 'FabricVersion' field would not be set and would 453 // result in an error. // TODO: Determine if we want to throw error or handle setting 454 // FabricVersion as part of migration logic. 455 log.Info("v1.4 Fabric Orderer requested") 456 oconfig, err := ordererconfig.ReadOrdererFile(file) 457 if err != nil { 458 return nil, errors.Wrap(err, "failed to read v1.4 default config file") 459 } 460 return oconfig, nil 461 } 462 } 463 464 func (i *Initializer) GetCoreConfigFromBytes(instance *current.IBPOrderer, bytes []byte) (OrdererConfig, error) { 465 switch version.GetMajorReleaseVersion(instance.Spec.FabricVersion) { 466 case version.V2: 467 currentVer := version.String(instance.Spec.FabricVersion) 468 if currentVer.EqualWithoutTag(version.V2_4_1) || currentVer.GreaterThan(version.V2_4_1) { 469 log.Info("v2.4.x Fabric Orderer requested") 470 v24config, err := v24ordererconfig.ReadOrdererFromBytes(bytes) 471 if err != nil { 472 return nil, err 473 } 474 return v24config, nil 475 } else { 476 log.Info("v2.2.x Fabric Orderer requested") 477 v2config, err := v2ordererconfig.ReadOrdererFromBytes(bytes) 478 if err != nil { 479 return nil, err 480 } 481 return v2config, nil 482 } 483 case version.V1: 484 fallthrough 485 default: 486 // Choosing to default to v1.4 to not break backwards comptability, if coming 487 // from a previous version of operator the 'FabricVersion' field would not be set and would 488 // result in an error. 489 log.Info("v1.4 Fabric Orderer requested") 490 oconfig, err := ordererconfig.ReadOrdererFromBytes(bytes) 491 if err != nil { 492 return nil, err 493 } 494 return oconfig, nil 495 } 496 }