github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/base/peer/peer.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 basepeer 20 21 import ( 22 "bytes" 23 "context" 24 "encoding/json" 25 "fmt" 26 "os" 27 "path/filepath" 28 "strings" 29 "time" 30 31 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 32 config "github.com/IBM-Blockchain/fabric-operator/operatorconfig" 33 "github.com/IBM-Blockchain/fabric-operator/pkg/action" 34 commonapi "github.com/IBM-Blockchain/fabric-operator/pkg/apis/common" 35 "github.com/IBM-Blockchain/fabric-operator/pkg/certificate" 36 commoninit "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common" 37 commonconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common/config" 38 initializer "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/peer" 39 peerconfig "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/peer/config/v1" 40 "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/validator" 41 controllerclient "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient" 42 "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources" 43 jobv1 "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/job" 44 resourcemanager "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/manager" 45 "github.com/IBM-Blockchain/fabric-operator/pkg/migrator/peer/fabric" 46 v2 "github.com/IBM-Blockchain/fabric-operator/pkg/migrator/peer/fabric/v2" 47 "github.com/IBM-Blockchain/fabric-operator/pkg/offering/common" 48 "github.com/IBM-Blockchain/fabric-operator/pkg/offering/common/reconcilechecks" 49 "github.com/IBM-Blockchain/fabric-operator/pkg/operatorerrors" 50 "github.com/IBM-Blockchain/fabric-operator/pkg/restart" 51 "github.com/IBM-Blockchain/fabric-operator/pkg/util" 52 "github.com/IBM-Blockchain/fabric-operator/version" 53 "github.com/pkg/errors" 54 appsv1 "k8s.io/api/apps/v1" 55 batchv1 "k8s.io/api/batch/v1" 56 corev1 "k8s.io/api/core/v1" 57 k8serrors "k8s.io/apimachinery/pkg/api/errors" 58 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 59 "k8s.io/apimachinery/pkg/runtime" 60 "k8s.io/apimachinery/pkg/types" 61 k8sclient "sigs.k8s.io/controller-runtime/pkg/client" 62 logf "sigs.k8s.io/controller-runtime/pkg/log" 63 "sigs.k8s.io/controller-runtime/pkg/reconcile" 64 "sigs.k8s.io/yaml" 65 ) 66 67 var log = logf.Log.WithName("base_peer") 68 69 const ( 70 DefaultCouchContainer = "./definitions/peer/couchdb.yaml" 71 DefaultCouchInitContainer = "./definitions/peer/couchdb-init.yaml" 72 73 defaultDeployment = "./definitions/peer/deployment.yaml" 74 defaultPVC = "./definitions/peer/pvc.yaml" 75 defaultCouchDBPVC = "./definitions/peer/couchdb-pvc.yaml" 76 defaultService = "./definitions/peer/service.yaml" 77 defaultRole = "./definitions/peer/role.yaml" 78 defaultServiceAccount = "./definitions/peer/serviceaccount.yaml" 79 defaultRoleBinding = "./definitions/peer/rolebinding.yaml" 80 defaultFluentdConfigMap = "./definitions/peer/fluentd-configmap.yaml" 81 82 DaysToSecondsConversion = int64(24 * 60 * 60) 83 ) 84 85 type Override interface { 86 Deployment(v1.Object, *appsv1.Deployment, resources.Action) error 87 Service(v1.Object, *corev1.Service, resources.Action) error 88 PVC(v1.Object, *corev1.PersistentVolumeClaim, resources.Action) error 89 StateDBPVC(v1.Object, *corev1.PersistentVolumeClaim, resources.Action) error 90 } 91 92 //go:generate counterfeiter -o mocks/deployment_manager.go -fake-name DeploymentManager . DeploymentManager 93 94 type DeploymentManager interface { 95 resources.Manager 96 CheckForSecretChange(v1.Object, string, func(string, *appsv1.Deployment) bool) error 97 DeploymentStatus(v1.Object) (appsv1.DeploymentStatus, error) 98 GetScheme() *runtime.Scheme 99 } 100 101 //go:generate counterfeiter -o mocks/initializer.go -fake-name InitializeIBPPeer . InitializeIBPPeer 102 103 type InitializeIBPPeer interface { 104 GenerateOrdererCACertsSecret(instance *current.IBPPeer, certs map[string][]byte) error 105 GenerateSecrets(prefix commoninit.SecretType, instance v1.Object, crypto *commonconfig.Response) error 106 Create(initializer.CoreConfig, initializer.IBPPeer, string) (*initializer.Response, error) 107 Update(initializer.CoreConfig, initializer.IBPPeer) (*initializer.Response, error) 108 CheckIfAdminCertsUpdated(*current.IBPPeer) (bool, error) 109 UpdateAdminSecret(*current.IBPPeer) error 110 MissingCrypto(*current.IBPPeer) bool 111 GetInitPeer(instance *current.IBPPeer, storagePath string) (*initializer.Peer, error) 112 GetUpdatedPeer(instance *current.IBPPeer) (*initializer.Peer, error) 113 GenerateSecretsFromResponse(instance *current.IBPPeer, cryptoResponse *commonconfig.CryptoResponse) error 114 UpdateSecretsFromResponse(instance *current.IBPPeer, cryptoResponse *commonconfig.CryptoResponse) error 115 GetCrypto(instance *current.IBPPeer) (*commonconfig.CryptoResponse, error) 116 CoreConfigMap() *initializer.CoreConfigMap 117 } 118 119 //go:generate counterfeiter -o mocks/certificate_manager.go -fake-name CertificateManager . CertificateManager 120 121 type CertificateManager interface { 122 CheckCertificatesForExpire(instance v1.Object, numSecondsBeforeExpire int64) (current.IBPCRStatusType, string, error) 123 GetSignCert(string, string) ([]byte, error) 124 GetDurationToNextRenewal(commoninit.SecretType, v1.Object, int64) (time.Duration, error) 125 RenewCert(commoninit.SecretType, certificate.Instance, *current.EnrollmentSpec, *commonapi.BCCSP, string, bool, bool) error 126 } 127 128 //go:generate counterfeiter -o mocks/restart_manager.go -fake-name RestartManager . RestartManager 129 130 type RestartManager interface { 131 ForAdminCertUpdate(instance v1.Object) error 132 ForCertUpdate(certType commoninit.SecretType, instance v1.Object) error 133 ForConfigOverride(instance v1.Object) error 134 ForNodeOU(instance v1.Object) error 135 ForRestartAction(instance v1.Object) error 136 TriggerIfNeeded(instance restart.Instance) error 137 } 138 139 //go:generate counterfeiter -o mocks/update.go -fake-name Update . Update 140 type Update interface { 141 SpecUpdated() bool 142 ConfigOverridesUpdated() bool 143 DindArgsUpdated() bool 144 TLSCertUpdated() bool 145 EcertUpdated() bool 146 PeerTagUpdated() bool 147 CertificateUpdated() bool 148 SetDindArgsUpdated(updated bool) 149 RestartNeeded() bool 150 EcertReenrollNeeded() bool 151 TLSReenrollNeeded() bool 152 EcertNewKeyReenroll() bool 153 TLScertNewKeyReenroll() bool 154 MigrateToV2() bool 155 MigrateToV24() bool 156 UpgradeDBs() bool 157 MSPUpdated() bool 158 EcertEnroll() bool 159 TLSCertEnroll() bool 160 CertificateCreated() bool 161 GetCreatedCertType() commoninit.SecretType 162 CryptoBackupNeeded() bool 163 NodeOUUpdated() bool 164 FabricVersionUpdated() bool 165 ImagesUpdated() bool 166 } 167 168 type IBPPeer interface { 169 Initialize(instance *current.IBPPeer, update Update) error 170 CheckStates(instance *current.IBPPeer) error 171 PreReconcileChecks(instance *current.IBPPeer, update Update) (bool, error) 172 ReconcileManagers(instance *current.IBPPeer, update Update) error 173 Reconcile(instance *current.IBPPeer, update Update) (common.Result, error) 174 } 175 176 type CoreConfig interface { 177 GetMaxNameLength() *int 178 GetAddressOverrides() []peerconfig.AddressOverride 179 GetBCCSPSection() *commonapi.BCCSP 180 MergeWith(interface{}, bool) error 181 SetPKCS11Defaults(bool) 182 ToBytes() ([]byte, error) 183 UsingPKCS11() bool 184 SetBCCSPLibrary(string) 185 } 186 187 var _ IBPPeer = &Peer{} 188 189 type Peer struct { 190 Client controllerclient.Client 191 Scheme *runtime.Scheme 192 Config *config.Config 193 194 DeploymentManager DeploymentManager 195 ServiceManager resources.Manager 196 PVCManager resources.Manager 197 StateDBPVCManager resources.Manager 198 FluentDConfigMapManager resources.Manager 199 RoleManager resources.Manager 200 RoleBindingManager resources.Manager 201 ServiceAccountManager resources.Manager 202 203 Override Override 204 Initializer InitializeIBPPeer 205 206 CertificateManager CertificateManager 207 RenewCertTimers map[string]*time.Timer 208 209 Restart RestartManager 210 } 211 212 func New(client controllerclient.Client, scheme *runtime.Scheme, config *config.Config, o Override) *Peer { 213 p := &Peer{ 214 Client: client, 215 Scheme: scheme, 216 Config: config, 217 Override: o, 218 } 219 220 p.CreateManagers() 221 222 validator := &validator.Validator{ 223 Client: client, 224 } 225 226 init := initializer.New(config.PeerInitConfig, scheme, client, p.GetLabels, validator, config.Operator.Peer.Timeouts.EnrollJob) 227 p.Initializer = init 228 229 p.CertificateManager = certificate.New(client, scheme) 230 p.RenewCertTimers = make(map[string]*time.Timer) 231 232 p.Restart = restart.New(client, config.Operator.Restart.WaitTime.Get(), config.Operator.Restart.Timeout.Get()) 233 234 return p 235 } 236 237 func (p *Peer) CreateManagers() { 238 override := p.Override 239 resourceManager := resourcemanager.New(p.Client, p.Scheme) 240 peerConfig := p.Config.PeerInitConfig 241 242 p.DeploymentManager = resourceManager.CreateDeploymentManager("", override.Deployment, p.GetLabels, peerConfig.DeploymentFile) 243 p.PVCManager = resourceManager.CreatePVCManager("", override.PVC, p.GetLabels, peerConfig.PVCFile) 244 p.StateDBPVCManager = resourceManager.CreatePVCManager("statedb", override.StateDBPVC, p.GetLabels, peerConfig.CouchDBPVCFile) 245 p.FluentDConfigMapManager = resourceManager.CreateConfigMapManager("fluentd", nil, p.GetLabels, peerConfig.FluentdConfigMapFile, nil) 246 p.RoleManager = resourceManager.CreateRoleManager("", nil, p.GetLabels, peerConfig.RoleFile) 247 p.RoleBindingManager = resourceManager.CreateRoleBindingManager("", nil, p.GetLabels, peerConfig.RoleBindingFile) 248 p.ServiceAccountManager = resourceManager.CreateServiceAccountManager("", nil, p.GetLabels, peerConfig.ServiceAccountFile) 249 p.ServiceManager = resourceManager.CreateServiceManager("", override.Service, p.GetLabels, peerConfig.ServiceFile) 250 } 251 252 func (p *Peer) PreReconcileChecks(instance *current.IBPPeer, update Update) (bool, error) { 253 var maxNameLength *int 254 255 imagesUpdated, err := reconcilechecks.FabricVersionHelper(instance, p.Config.Operator.Versions, update) 256 if err != nil { 257 return false, errors.Wrap(err, "failed to during version and image checks") 258 } 259 260 co, err := instance.GetConfigOverride() 261 if err != nil { 262 return false, err 263 } 264 265 configOverride := co.(CoreConfig) 266 maxNameLength = configOverride.GetMaxNameLength() 267 268 err = util.ValidationChecks(instance.TypeMeta, instance.ObjectMeta, "IBPPeer", maxNameLength) 269 if err != nil { 270 return false, err 271 } 272 273 if instance.Spec.Action.Enroll.Ecert && instance.Spec.Action.Reenroll.Ecert { 274 return false, errors.New("both enroll and renenroll action requested for ecert, must only select one") 275 } 276 277 if instance.Spec.Action.Enroll.TLSCert && instance.Spec.Action.Reenroll.TLSCert { 278 return false, errors.New("both enroll and renenroll action requested for TLS cert, must only select one") 279 } 280 281 if instance.Spec.Action.Reenroll.Ecert && instance.Spec.Action.Reenroll.EcertNewKey { 282 return false, errors.New("both reenroll and renenroll with new action requested for ecert, must only select one") 283 } 284 285 if instance.Spec.Action.Reenroll.TLSCert && instance.Spec.Action.Reenroll.TLSCertNewKey { 286 return false, errors.New("both reenroll and renenroll with new action requested for TLS cert, must only select one") 287 } 288 289 if instance.Spec.HSMSet() { 290 err = util.ValidateHSMProxyURL(instance.Spec.HSM.PKCS11Endpoint) 291 if err != nil { 292 return false, errors.Wrapf(err, "invalid HSM endpoint for peer instance '%s'", instance.GetName()) 293 } 294 } 295 296 hsmImageUpdated := p.ReconcileHSMImages(instance) 297 298 if !instance.Spec.DomainSet() { 299 return false, fmt.Errorf("domain not set for peer instance '%s'", instance.GetName()) 300 } 301 302 zoneUpdated, err := p.SelectZone(instance) 303 if err != nil { 304 return false, err 305 } 306 307 regionUpdated, err := p.SelectRegion(instance) 308 if err != nil { 309 return false, err 310 } 311 312 var replicasUpdated bool 313 if instance.Spec.Replicas == nil { 314 replicas := int32(1) 315 instance.Spec.Replicas = &replicas 316 replicasUpdated = true 317 } 318 319 dbTypeUpdated := p.CheckDBType(instance) 320 updated := dbTypeUpdated || zoneUpdated || regionUpdated || update.DindArgsUpdated() || hsmImageUpdated || replicasUpdated || imagesUpdated 321 322 if updated { 323 log.Info(fmt.Sprintf( 324 "dbTypeUpdate %t, zoneUpdated %t, regionUpdated %t, dindArgsUpdated %t, hsmImageUpdated %t, replicasUpdated %t, imagesUpdated %t", 325 dbTypeUpdated, 326 zoneUpdated, 327 regionUpdated, 328 update.DindArgsUpdated(), 329 hsmImageUpdated, 330 replicasUpdated, 331 imagesUpdated)) 332 } 333 334 return updated, nil 335 } 336 337 func (p *Peer) SetVersion(instance *current.IBPPeer) (bool, error) { 338 if instance.Status.Version == "" || !version.String(instance.Status.Version).Equal(version.Operator) { 339 log.Info("Version of Operator: ", "version", version.Operator) 340 log.Info("Version of CR: ", "version", instance.Status.Version) 341 log.Info(fmt.Sprintf("Updating CR '%s' to version '%s'", instance.Name, version.Operator)) 342 343 instance.Status.Version = version.Operator 344 err := p.Client.PatchStatus(context.TODO(), instance, nil, controllerclient.PatchOption{ 345 Resilient: &controllerclient.ResilientPatch{ 346 Retry: 3, 347 Into: ¤t.IBPPeer{}, 348 Strategy: k8sclient.MergeFrom, 349 }, 350 }) 351 if err != nil { 352 return false, err 353 } 354 return true, nil 355 } 356 return false, nil 357 } 358 359 func (p *Peer) Initialize(instance *current.IBPPeer, update Update) error { 360 var err error 361 362 log.Info(fmt.Sprintf("Checking if peer '%s' needs initialization", instance.GetName())) 363 364 // TODO: Add checks to determine if initialization is neeeded. Split this method into 365 // two, one should handle initialization during the create event of a CR and the other 366 // should update events 367 368 // Service account is required by HSM init job 369 if err := p.ReconcilePeerRBAC(instance); err != nil { 370 return err 371 } 372 373 if instance.IsHSMEnabled() { 374 // If HSM config not found, HSM proxy is being used 375 if instance.UsingHSMProxy() { 376 err = os.Setenv("PKCS11_PROXY_SOCKET", instance.Spec.HSM.PKCS11Endpoint) 377 if err != nil { 378 return err 379 } 380 } else { 381 hsmConfig, err := commonconfig.ReadHSMConfig(p.Client, instance) 382 if err != nil { 383 return errors.New("using non-proxy HSM, but no HSM config defined as config map 'ibp-hsm-config'") 384 } 385 386 if hsmConfig.Daemon != nil { 387 log.Info("Using daemon based HSM, creating pvc...") 388 p.PVCManager.SetCustomName(instance.Spec.CustomNames.PVC.Peer) 389 err = p.PVCManager.Reconcile(instance, update.SpecUpdated()) 390 if err != nil { 391 return errors.Wrap(err, "failed PVC reconciliation") 392 } 393 } 394 } 395 } 396 397 peerConfig := p.Config.PeerInitConfig.CorePeerFile 398 if version.GetMajorReleaseVersion(instance.Spec.FabricVersion) == version.V2 { 399 peerConfig = p.Config.PeerInitConfig.CorePeerV2File 400 } 401 402 if instance.UsingHSMProxy() { 403 err = os.Setenv("PKCS11_PROXY_SOCKET", instance.Spec.HSM.PKCS11Endpoint) 404 if err != nil { 405 return err 406 } 407 } 408 409 storagePath := p.GetInitStoragePath(instance) 410 initPeer, err := p.Initializer.GetInitPeer(instance, storagePath) 411 if err != nil { 412 return err 413 } 414 initPeer.UsingHSMProxy = instance.UsingHSMProxy() 415 initPeer.Config, err = initializer.GetCoreConfigFromFile(instance, peerConfig) 416 if err != nil { 417 return err 418 } 419 420 updated := update.ConfigOverridesUpdated() || update.NodeOUUpdated() 421 if update.ConfigOverridesUpdated() { 422 err = p.InitializeUpdateConfigOverride(instance, initPeer) 423 if err != nil { 424 return err 425 } 426 // Request deployment restart for config override update 427 if err = p.Restart.ForConfigOverride(instance); err != nil { 428 return err 429 } 430 } 431 if update.NodeOUUpdated() { 432 err = p.InitializeUpdateNodeOU(instance) 433 if err != nil { 434 return err 435 } 436 // Request deloyment restart for node OU update 437 if err = p.Restart.ForNodeOU(instance); err != nil { 438 return err 439 } 440 } 441 if !updated { 442 err = p.InitializeCreate(instance, initPeer) 443 if err != nil { 444 return err 445 } 446 } 447 448 updateNeeded, err := p.Initializer.CheckIfAdminCertsUpdated(instance) 449 if err != nil { 450 return err 451 } 452 if updateNeeded { 453 err = p.Initializer.UpdateAdminSecret(instance) 454 if err != nil { 455 return err 456 } 457 // Request deployment restart for admin cert updates 458 if err = p.Restart.ForAdminCertUpdate(instance); err != nil { 459 return err 460 } 461 } 462 463 return nil 464 } 465 466 func (p *Peer) InitializeUpdateConfigOverride(instance *current.IBPPeer, initPeer *initializer.Peer) error { 467 var err error 468 469 if p.Initializer.MissingCrypto(instance) { 470 // If crypto is missing, we should run the create logic 471 err := p.InitializeCreate(instance, initPeer) 472 if err != nil { 473 return err 474 } 475 476 return nil 477 } 478 479 log.Info(fmt.Sprintf("Initialize peer '%s' during update config override", instance.GetName())) 480 481 cm, err := initializer.GetCoreFromConfigMap(p.Client, instance) 482 if err != nil { 483 return err 484 } 485 486 initPeer.Config, err = initializer.GetCoreConfigFromBytes(instance, cm.BinaryData["core.yaml"]) 487 if err != nil { 488 return err 489 } 490 491 co, err := instance.GetConfigOverride() 492 if err != nil { 493 return err 494 } 495 configOverrides := co.(CoreConfig) 496 497 resp, err := p.Initializer.Update(configOverrides, initPeer) 498 if err != nil { 499 return err 500 } 501 502 if resp != nil { 503 if resp.Config != nil { 504 // Update core.yaml in config map 505 err = p.Initializer.CoreConfigMap().CreateOrUpdate(instance, resp.Config) 506 if err != nil { 507 return err 508 } 509 } 510 511 if len(resp.DeliveryClientCerts) > 0 { 512 log.Info(fmt.Sprintf("Orderer CA Certs detected in DeliveryClient config, creating secret '%s-orderercacerts' with certs", instance.Name)) 513 err = p.Initializer.GenerateOrdererCACertsSecret(instance, resp.DeliveryClientCerts) 514 if err != nil { 515 return err 516 } 517 } 518 } 519 520 return nil 521 } 522 523 func (p *Peer) InitializeUpdateNodeOU(instance *current.IBPPeer) error { 524 log.Info(fmt.Sprintf("Node OU updated with enabled: %t for peer '%s", !instance.Spec.NodeOUDisabled(), instance.GetName())) 525 526 crypto, err := p.Initializer.GetCrypto(instance) 527 if err != nil { 528 return err 529 } 530 if !instance.Spec.NodeOUDisabled() { 531 if err := crypto.VerifyCertOU("peer"); err != nil { 532 return err 533 } 534 } else { 535 // If nodeOUDisabled, admin certs are required 536 if crypto.Enrollment.AdminCerts == nil { 537 return errors.New("node OU disabled, admin certs are required but missing") 538 } 539 } 540 541 // Update config.yaml in config map 542 err = p.Initializer.CoreConfigMap().AddNodeOU(instance) 543 if err != nil { 544 return err 545 } 546 547 return nil 548 } 549 550 func (p *Peer) InitializeCreate(instance *current.IBPPeer, initPeer *initializer.Peer) error { 551 var err error 552 553 if p.ConfigExists(instance) { 554 log.Info(fmt.Sprintf("Config '%s-config' exists, not reinitializing peer", instance.GetName())) 555 return nil 556 } 557 558 log.Info(fmt.Sprintf("Initialize peer '%s' during create", instance.GetName())) 559 560 storagePath := p.GetInitStoragePath(instance) 561 562 co, err := instance.GetConfigOverride() 563 if err != nil { 564 return err 565 } 566 configOverrides := co.(CoreConfig) 567 568 resp, err := p.Initializer.Create(configOverrides, initPeer, storagePath) 569 if err != nil { 570 return err 571 } 572 573 if resp != nil { 574 if resp.Crypto != nil { 575 if !instance.Spec.NodeOUDisabled() { 576 if err := resp.Crypto.VerifyCertOU("peer"); err != nil { 577 return err 578 } 579 } 580 581 err = p.Initializer.GenerateSecretsFromResponse(instance, resp.Crypto) 582 if err != nil { 583 return err 584 } 585 } 586 587 if len(resp.DeliveryClientCerts) > 0 { 588 log.Info(fmt.Sprintf("Orderer CA Certs detected in DeliverClient config, creating secret '%s-orderercacerts' with certs", instance.Name)) 589 err = p.Initializer.GenerateOrdererCACertsSecret(instance, resp.DeliveryClientCerts) 590 if err != nil { 591 return err 592 } 593 } 594 595 if resp.Config != nil { 596 if instance.IsHSMEnabled() && !instance.UsingHSMProxy() { 597 hsmConfig, err := commonconfig.ReadHSMConfig(p.Client, instance) 598 if err != nil { 599 return err 600 } 601 resp.Config.SetBCCSPLibrary(filepath.Join("/hsm/lib", filepath.Base(hsmConfig.Library.FilePath))) 602 } 603 604 err = p.Initializer.CoreConfigMap().CreateOrUpdate(instance, resp.Config) 605 if err != nil { 606 return err 607 } 608 } 609 } 610 611 return nil 612 } 613 614 func (p *Peer) Reconcile(instance *current.IBPPeer, update Update) (common.Result, error) { 615 var err error 616 var status *current.CRStatus 617 618 versionSet, err := p.SetVersion(instance) 619 if err != nil { 620 return common.Result{}, errors.Wrap(err, fmt.Sprintf("failed updating CR '%s' to version '%s'", instance.Name, version.Operator)) 621 } 622 if versionSet { 623 log.Info("Instance version updated, requeuing request...") 624 return common.Result{ 625 Result: reconcile.Result{ 626 Requeue: true, 627 }, 628 }, nil 629 } 630 631 instanceUpdated, err := p.PreReconcileChecks(instance, update) 632 if err != nil { 633 return common.Result{}, errors.Wrap(err, "failed pre reconcile checks") 634 } 635 636 // We do not have to wait for service to get the external endpoint 637 // thus we call UpdateExternalEndpoint in reconcile before reconcile managers 638 externalEndpointUpdated := p.UpdateExternalEndpoint(instance) 639 640 if instanceUpdated || externalEndpointUpdated { 641 log.Info(fmt.Sprintf("Updating instance after pre reconcile checks: %t, updating external endpoint: %t", instanceUpdated, externalEndpointUpdated)) 642 err = p.Client.Patch(context.TODO(), instance, nil, controllerclient.PatchOption{ 643 Resilient: &controllerclient.ResilientPatch{ 644 Retry: 3, 645 Into: ¤t.IBPPeer{}, 646 Strategy: k8sclient.MergeFrom, 647 }, 648 }) 649 if err != nil { 650 return common.Result{}, errors.Wrap(err, "failed to update instance") 651 } 652 653 log.Info("Instance updated, requeuing request...") 654 return common.Result{ 655 Result: reconcile.Result{ 656 Requeue: true, 657 }, 658 }, nil 659 } 660 661 err = p.Initialize(instance, update) 662 if err != nil { 663 return common.Result{}, operatorerrors.Wrap(err, operatorerrors.PeerInitilizationFailed, "failed to initialize peer") 664 } 665 666 err = p.ReconcileManagers(instance, update) 667 if err != nil { 668 return common.Result{}, errors.Wrap(err, "failed to reconcile managers") 669 } 670 671 err = p.UpdateConnectionProfile(instance) 672 if err != nil { 673 return common.Result{}, errors.Wrap(err, "failed to create connection profile") 674 } 675 676 err = p.CheckStates(instance) 677 if err != nil { 678 return common.Result{}, errors.Wrap(err, "failed to check and restore state") 679 } 680 681 // custom product logic can be implemented here 682 // No-Op atm 683 status, result, err := p.CustomLogic(instance, update) 684 if err != nil { 685 return common.Result{}, errors.Wrap(err, "failed to run custom offering logic") 686 } 687 if result != nil { 688 log.Info(fmt.Sprintf("Finished reconciling '%s' with Custom Logic result", instance.GetName())) 689 return *result, nil 690 } 691 692 if update.MSPUpdated() { 693 err = p.UpdateMSPCertificates(instance) 694 if err != nil { 695 return common.Result{}, errors.Wrap(err, "failed to update certificates passed in MSP spec") 696 } 697 // A successful update will trigger a tlsCertUpdated or ecertUpdated event, which will handle restarting deployment 698 } 699 700 if update.EcertUpdated() { 701 log.Info("Ecert was updated") 702 // Request deployment restart for tls cert update 703 err = p.Restart.ForCertUpdate(commoninit.ECERT, instance) 704 if err != nil { 705 return common.Result{}, errors.Wrap(err, "failed to update restart config") 706 } 707 } 708 709 if update.TLSCertUpdated() { 710 log.Info("TLS cert was updated") 711 // Request deployment restart for ecert update 712 err = p.Restart.ForCertUpdate(commoninit.TLS, instance) 713 if err != nil { 714 return common.Result{}, errors.Wrap(err, "failed to update restart config") 715 } 716 } 717 718 if err := p.HandleActions(instance, update); err != nil { 719 return common.Result{}, err 720 } 721 722 if err := p.HandleRestart(instance, update); err != nil { 723 return common.Result{}, err 724 } 725 726 return common.Result{ 727 Status: status, 728 }, nil 729 } 730 731 func (p *Peer) ReconcileManagers(instance *current.IBPPeer, updated Update) error { 732 var err error 733 734 update := updated.SpecUpdated() 735 736 p.PVCManager.SetCustomName(instance.Spec.CustomNames.PVC.Peer) 737 err = p.PVCManager.Reconcile(instance, update) 738 if err != nil { 739 return errors.Wrap(err, "failed PVC reconciliation") 740 } 741 742 p.StateDBPVCManager.SetCustomName(instance.Spec.CustomNames.PVC.StateDB) 743 err = p.StateDBPVCManager.Reconcile(instance, update) 744 if err != nil { 745 return errors.Wrap(err, "failed CouchDB PVC reconciliation") 746 } 747 748 err = p.ReconcileSecret(instance) 749 if err != nil { 750 return errors.Wrap(err, "failed Secret reconciliation") 751 } 752 753 err = p.ServiceManager.Reconcile(instance, update) 754 if err != nil { 755 return errors.Wrap(err, "failed Service reconciliation") 756 } 757 758 err = p.DeploymentManager.Reconcile(instance, update) 759 if err != nil { 760 return errors.Wrap(err, "failed Deployment reconciliation") 761 } 762 763 err = p.ReconcilePeerRBAC(instance) 764 if err != nil { 765 return errors.Wrap(err, "failed RBAC reconciliation") 766 } 767 768 err = p.FluentDConfigMapManager.Reconcile(instance, update) 769 if err != nil { 770 return errors.Wrap(err, "failed FluentD ConfigMap reconciliation") 771 } 772 773 return nil 774 } 775 776 func (p *Peer) ReconcilePeerRBAC(instance *current.IBPPeer) error { 777 var err error 778 779 err = p.RoleManager.Reconcile(instance, false) 780 if err != nil { 781 return err 782 } 783 784 err = p.RoleBindingManager.Reconcile(instance, false) 785 if err != nil { 786 return err 787 } 788 789 err = p.ServiceAccountManager.Reconcile(instance, false) 790 if err != nil { 791 return err 792 } 793 794 return nil 795 } 796 797 // this function makes sure the deployment spec matches the expected state 798 func (p *Peer) CheckStates(instance *current.IBPPeer) error { 799 if p.DeploymentManager.Exists(instance) { 800 err := p.DeploymentManager.CheckState(instance) 801 if err != nil { 802 log.Error(err, "unexpected state") 803 err = p.DeploymentManager.RestoreState(instance) 804 if err != nil { 805 return err 806 } 807 } 808 } 809 810 return nil 811 } 812 813 func (p *Peer) ReconcileSecret(instance *current.IBPPeer) error { 814 name := instance.Spec.MSPSecret 815 if name == "" { 816 name = instance.Name + "-secret" // default value for secret, if none specified 817 } 818 819 secret := &corev1.Secret{} 820 err := p.Client.Get(context.TODO(), types.NamespacedName{Name: name, Namespace: instance.Namespace}, secret) 821 if err != nil { 822 if k8serrors.IsNotFound(err) { 823 log.Info(fmt.Sprintf("Creating secret '%s'", name)) 824 createErr := p.CreateSecret(instance) 825 if createErr != nil { 826 return createErr 827 } 828 return nil 829 } 830 return err 831 } else { 832 log.Info(fmt.Sprintf("Updating secret '%s'", name)) 833 updateErr := p.UpdateSecret(instance, secret) 834 if updateErr != nil { 835 return updateErr 836 } 837 } 838 839 return nil 840 } 841 842 func (p *Peer) CreateSecret(instance *current.IBPPeer) error { 843 secret := &corev1.Secret{} 844 secret.Name = instance.Spec.MSPSecret 845 if secret.Name == "" { 846 secret.Name = instance.Name + "-secret" // default value for secret, if none specified 847 } 848 secret.Namespace = instance.Namespace 849 secret.Labels = p.GetLabels(instance) 850 851 secretData := instance.Spec.Secret 852 bytesData, err := json.Marshal(secretData) 853 if err != nil { 854 return err 855 } 856 secret.Data = make(map[string][]byte) 857 secret.Data["secret.json"] = bytesData 858 859 err = p.Client.Create(context.TODO(), secret, controllerclient.CreateOption{Owner: instance, Scheme: p.Scheme}) 860 if err != nil { 861 return err 862 } 863 864 return nil 865 } 866 867 func (p *Peer) UpdateSecret(instance *current.IBPPeer, secret *corev1.Secret) error { 868 secretData := instance.Spec.Secret 869 bytesData, err := json.Marshal(secretData) 870 if err != nil { 871 return err 872 } 873 874 if secret.Data != nil && !bytes.Equal(secret.Data["secret.json"], bytesData) { 875 secret.Data["secret.json"] = bytesData 876 877 err = p.Client.Update(context.TODO(), secret, controllerclient.UpdateOption{Owner: instance, Scheme: p.Scheme}) 878 if err != nil { 879 return err 880 } 881 } 882 883 return nil 884 } 885 886 func (p *Peer) UpdateExternalEndpoint(instance *current.IBPPeer) bool { 887 // Disable Service discovery 888 if instance.Spec.PeerExternalEndpoint == "do-not-set" { 889 return false 890 } 891 892 if instance.Spec.PeerExternalEndpoint == "" { 893 instance.Spec.PeerExternalEndpoint = instance.Namespace + "-" + instance.Name + "-peer." + instance.Spec.Domain + ":443" 894 return true 895 } 896 return false 897 } 898 899 func (p *Peer) SelectZone(instance *current.IBPPeer) (bool, error) { 900 if instance.Spec.Zone == "select" { 901 zone := util.GetZone(p.Client) 902 instance.Spec.Zone = zone 903 log.Info(fmt.Sprintf("Setting zone to '%s', and updating spec", zone)) 904 return true, nil 905 } 906 if instance.Spec.Zone != "" { 907 err := util.ValidateZone(p.Client, instance.Spec.Zone) 908 if err != nil { 909 return false, err 910 } 911 } 912 return false, nil 913 } 914 915 func (p *Peer) SelectRegion(instance *current.IBPPeer) (bool, error) { 916 if instance.Spec.Region == "select" { 917 region := util.GetRegion(p.Client) 918 instance.Spec.Region = region 919 log.Info(fmt.Sprintf("Setting region to '%s', and updating spec", region)) 920 return true, nil 921 } 922 if instance.Spec.Region != "" { 923 err := util.ValidateRegion(p.Client, instance.Spec.Region) 924 if err != nil { 925 return false, err 926 } 927 } 928 return false, nil 929 } 930 931 func (p *Peer) CheckDBType(instance *current.IBPPeer) bool { 932 if instance.Spec.StateDb == "" { 933 log.Info("Setting statedb type to 'CouchDB', and updating spec") 934 instance.Spec.StateDb = "CouchDB" 935 return true 936 } 937 938 return false 939 } 940 941 func (p *Peer) GetLabels(instance v1.Object) map[string]string { 942 label := os.Getenv("OPERATOR_LABEL_PREFIX") 943 if label == "" { 944 label = "fabric" 945 } 946 947 i := instance.(*current.IBPPeer) 948 return map[string]string{ 949 "app": instance.GetName(), 950 "creator": label, 951 "orgname": i.Spec.MSPID, 952 "app.kubernetes.io/name": label, 953 "app.kubernetes.io/instance": label + "peer", 954 "app.kubernetes.io/managed-by": label + "-operator", 955 } 956 } 957 958 func (p *Peer) UpdateConnectionProfile(instance *current.IBPPeer) error { 959 var err error 960 961 endpoints := p.GetEndpoints(instance) 962 963 tlscert, err := common.GetTLSSignCertEncoded(p.Client, instance) 964 if err != nil { 965 return err 966 } 967 968 tlscacerts, err := common.GetTLSCACertEncoded(p.Client, instance) 969 if err != nil { 970 return err 971 } 972 973 tlsintercerts, err := common.GetTLSIntercertEncoded(p.Client, instance) 974 if err != nil { 975 return err 976 } 977 978 ecert, err := common.GetEcertSignCertEncoded(p.Client, instance) 979 if err != nil { 980 return err 981 } 982 983 cacert, err := common.GetEcertCACertEncoded(p.Client, instance) 984 if err != nil { 985 return err 986 } 987 988 admincerts, err := common.GetEcertAdmincertEncoded(p.Client, instance) 989 if err != nil { 990 return err 991 } 992 993 if len(tlsintercerts) > 0 { 994 tlscacerts = tlsintercerts 995 } 996 997 err = p.UpdateConnectionProfileConfigmap(instance, *endpoints, tlscert, tlscacerts, ecert, cacert, admincerts) 998 if err != nil { 999 return err 1000 } 1001 1002 return nil 1003 } 1004 1005 func (p *Peer) UpdateConnectionProfileConfigmap(instance *current.IBPPeer, endpoints current.PeerEndpoints, tlscert string, tlscacerts []string, ecert string, cacert []string, admincerts []string) error { 1006 // TODO add ecert.intermediatecerts and ecert.admincerts 1007 // TODO add tls.cacerts 1008 // TODO get the whole PeerConnectionProfile object from caller?? 1009 name := instance.Name + "-connection-profile" 1010 connectionProfile := ¤t.PeerConnectionProfile{ 1011 Endpoints: endpoints, 1012 TLS: ¤t.MSP{ 1013 SignCerts: tlscert, 1014 CACerts: tlscacerts, 1015 }, 1016 Component: ¤t.MSP{ 1017 SignCerts: ecert, 1018 CACerts: cacert, 1019 AdminCerts: admincerts, 1020 }, 1021 } 1022 1023 bytes, err := json.Marshal(connectionProfile) 1024 if err != nil { 1025 return errors.Wrap(err, "failed to marshal connectionprofile") 1026 } 1027 cm := &corev1.ConfigMap{ 1028 BinaryData: map[string][]byte{"profile.json": bytes}, 1029 } 1030 cm.Name = name 1031 cm.Namespace = instance.Namespace 1032 cm.Labels = p.GetLabels(instance) 1033 1034 nn := types.NamespacedName{ 1035 Name: name, 1036 Namespace: instance.GetNamespace(), 1037 } 1038 1039 err = p.Client.Get(context.TODO(), nn, &corev1.ConfigMap{}) 1040 if err == nil { 1041 log.Info(fmt.Sprintf("Updating connection profle configmap for %s", instance.Name)) 1042 err = p.Client.Update(context.TODO(), cm, controllerclient.UpdateOption{ 1043 Owner: instance, 1044 Scheme: p.Scheme, 1045 }) 1046 if err != nil { 1047 return errors.Wrap(err, "failed to update connection profile configmap") 1048 } 1049 } else { 1050 log.Info(fmt.Sprintf("Creating connection profle configmap for %s", instance.Name)) 1051 err = p.Client.Create(context.TODO(), cm, controllerclient.CreateOption{ 1052 Owner: instance, 1053 Scheme: p.Scheme, 1054 }) 1055 if err != nil { 1056 return errors.Wrap(err, "failed to create connection profile configmap") 1057 } 1058 } 1059 return nil 1060 } 1061 1062 func (p *Peer) GetEndpoints(instance *current.IBPPeer) *current.PeerEndpoints { 1063 endpoints := ¤t.PeerEndpoints{ 1064 API: "grpcs://" + instance.Namespace + "-" + instance.Name + "-peer." + instance.Spec.Domain + ":443", 1065 Operations: "https://" + instance.Namespace + "-" + instance.Name + "-operations." + instance.Spec.Domain + ":443", 1066 Grpcweb: "https://" + instance.Namespace + "-" + instance.Name + "-grpcweb." + instance.Spec.Domain + ":443", 1067 } 1068 return endpoints 1069 } 1070 1071 func (p *Peer) ConfigExists(instance *current.IBPPeer) bool { 1072 name := fmt.Sprintf("%s-config", instance.GetName()) 1073 namespacedName := types.NamespacedName{ 1074 Name: name, 1075 Namespace: instance.Namespace, 1076 } 1077 1078 cm := &corev1.ConfigMap{} 1079 err := p.Client.Get(context.TODO(), namespacedName, cm) 1080 if err != nil { 1081 return false 1082 } 1083 1084 return true 1085 } 1086 1087 func (p *Peer) CheckCSRHosts(instance *current.IBPPeer, hosts []string) bool { 1088 if instance.Spec.Secret != nil { 1089 if instance.Spec.Secret.Enrollment != nil { 1090 if instance.Spec.Secret.Enrollment.TLS == nil { 1091 instance.Spec.Secret.Enrollment.TLS = ¤t.Enrollment{} 1092 } 1093 if instance.Spec.Secret.Enrollment.TLS.CSR == nil { 1094 instance.Spec.Secret.Enrollment.TLS.CSR = ¤t.CSR{} 1095 instance.Spec.Secret.Enrollment.TLS.CSR.Hosts = hosts 1096 return true 1097 } else { 1098 originalLength := len(instance.Spec.Secret.Enrollment.TLS.CSR.Hosts) 1099 for _, host := range instance.Spec.Secret.Enrollment.TLS.CSR.Hosts { 1100 hosts = util.AppendStringIfMissing(hosts, host) 1101 } 1102 instance.Spec.Secret.Enrollment.TLS.CSR.Hosts = hosts 1103 newLength := len(instance.Spec.Secret.Enrollment.TLS.CSR.Hosts) 1104 return originalLength != newLength 1105 } 1106 } 1107 } 1108 return false 1109 } 1110 1111 func (p *Peer) GetBCCSPSectionForInstance(instance *current.IBPPeer) (*commonapi.BCCSP, error) { 1112 var bccsp *commonapi.BCCSP 1113 if instance.IsHSMEnabled() { 1114 co, err := instance.GetConfigOverride() 1115 if err != nil { 1116 return nil, errors.Wrap(err, "failed to get configoverride") 1117 } 1118 1119 configOverride := co.(CoreConfig) 1120 configOverride.SetPKCS11Defaults(instance.UsingHSMProxy()) 1121 bccsp = configOverride.GetBCCSPSection() 1122 } 1123 1124 return bccsp, nil 1125 } 1126 1127 func (p *Peer) GetInitStoragePath(instance *current.IBPPeer) string { 1128 if p.Config != nil && p.Config.PeerInitConfig != nil && p.Config.PeerInitConfig.StoragePath != "" { 1129 return filepath.Join(p.Config.PeerInitConfig.StoragePath, instance.GetName()) 1130 } 1131 1132 return filepath.Join("/", "peerinit", instance.GetName()) 1133 } 1134 1135 func (p *Peer) ReconcileFabricPeerMigrationV1_4(instance *current.IBPPeer) error { 1136 peerConfig, err := p.FabricPeerMigrationV1_4(instance) 1137 if err != nil { 1138 return errors.Wrap(err, "failed to migrate peer between fabric versions") 1139 } 1140 1141 if peerConfig != nil { 1142 log.Info("Peer config updated during fabric peer migration, updating config map...") 1143 if err := p.Initializer.CoreConfigMap().CreateOrUpdate(instance, peerConfig); err != nil { 1144 return errors.Wrapf(err, "failed to create/update '%s' peer's config map", instance.GetName()) 1145 } 1146 } 1147 1148 return nil 1149 } 1150 1151 // Moving to fabric version above 1.4.6 require that the `msp/keystore` value be removed 1152 // from BCCSP section if configured to use PKCS11 (HSM). NOTE: This does not support 1153 // migration across major release, will not cover migration peer from 1.4.x to 2.x 1154 func (p *Peer) FabricPeerMigrationV1_4(instance *current.IBPPeer) (*peerconfig.Core, error) { 1155 if !instance.IsHSMEnabled() { 1156 return nil, nil 1157 } 1158 1159 peerTag := instance.Spec.Images.PeerTag 1160 if !strings.Contains(peerTag, "sha") { 1161 tag := strings.Split(peerTag, "-")[0] 1162 1163 peerVersion := version.String(tag) 1164 if !peerVersion.GreaterThan(version.V1_4_6) { 1165 return nil, nil 1166 } 1167 1168 log.Info(fmt.Sprintf("Peer moving to fabric version %s", peerVersion)) 1169 } else { 1170 if version.GetMajorReleaseVersion(instance.Spec.FabricVersion) == version.V2 { 1171 return nil, nil 1172 } 1173 log.Info(fmt.Sprintf("Peer moving to digest %s", peerTag)) 1174 } 1175 1176 // Read peer config map and remove keystore value from BCCSP section 1177 cm, err := initializer.GetCoreFromConfigMap(p.Client, instance) 1178 if err != nil { 1179 return nil, errors.Wrapf(err, "failed to get '%s' peer's config map", instance.GetName()) 1180 } 1181 1182 peerConfig := &peerconfig.Core{} 1183 if err := yaml.Unmarshal(cm.BinaryData["core.yaml"], peerConfig); err != nil { 1184 return nil, errors.Wrap(err, "invalid peer config") 1185 } 1186 1187 // If already nil, don't need to proceed further as config updates are not required 1188 if peerConfig.Peer.BCCSP.PKCS11.FileKeyStore == nil { 1189 return nil, nil 1190 } 1191 1192 peerConfig.Peer.BCCSP.PKCS11.FileKeyStore = nil 1193 1194 return peerConfig, nil 1195 } 1196 1197 func (p *Peer) ReconcileFabricPeerMigrationV2_0(instance *current.IBPPeer) error { 1198 log.Info("Migration to V2 requested, checking if migration is needed") 1199 1200 migrator := &v2.Migrate{ 1201 DeploymentManager: p.DeploymentManager, 1202 ConfigMapManager: &initializer.CoreConfigMap{Config: p.Config.PeerInitConfig, Scheme: p.Scheme, GetLabels: p.GetLabels, Client: p.Client}, 1203 Client: p.Client, 1204 } 1205 1206 if err := fabric.V2Migrate(instance, migrator, instance.Spec.FabricVersion, p.Config.Operator.Peer.Timeouts.DBMigration); err != nil { 1207 return err 1208 } 1209 1210 return nil 1211 } 1212 1213 func (p *Peer) ReconcileFabricPeerMigrationV2_4(instance *current.IBPPeer) error { 1214 log.Info("Migration to V2.4.x requested, checking if migration is needed") 1215 1216 migrator := &v2.Migrate{ 1217 DeploymentManager: p.DeploymentManager, 1218 ConfigMapManager: &initializer.CoreConfigMap{Config: p.Config.PeerInitConfig, Scheme: p.Scheme, GetLabels: p.GetLabels, Client: p.Client}, 1219 Client: p.Client, 1220 } 1221 1222 if err := fabric.V24Migrate(instance, migrator, instance.Spec.FabricVersion, p.Config.Operator.Peer.Timeouts.DBMigration); err != nil { 1223 return err 1224 } 1225 1226 return nil 1227 } 1228 1229 func (p *Peer) HandleMigrationJobs(listOpt k8sclient.ListOption, instance *current.IBPPeer) (bool, error) { 1230 status, job, err := p.CheckForRunningJobs(listOpt) 1231 if err != nil { 1232 return false, err 1233 } 1234 1235 switch status { 1236 case RUNNING: 1237 return true, nil 1238 case COMPLETED: 1239 jobName := job.GetName() 1240 log.Info(fmt.Sprintf("Migration job '%s' completed, cleaning up...", jobName)) 1241 1242 migrationJob := &batchv1.Job{ 1243 ObjectMeta: v1.ObjectMeta{ 1244 Name: jobName, 1245 Namespace: instance.GetNamespace(), 1246 }, 1247 } 1248 1249 if err := p.Client.Delete(context.TODO(), migrationJob); err != nil { 1250 return false, errors.Wrap(err, "failed to delete migration job after completion") 1251 } 1252 1253 // TODO: Need to investigate why job is not adding controller reference to job pod, 1254 // this manual cleanup should not be required 1255 podList := &corev1.PodList{} 1256 if err := p.Client.List(context.TODO(), podList, k8sclient.MatchingLabels{"job-name": jobName}); err != nil { 1257 return false, errors.Wrap(err, "failed to list db migraton pods") 1258 } 1259 1260 if len(podList.Items) == 1 { 1261 if err := p.Client.Delete(context.TODO(), &podList.Items[0]); err != nil { 1262 return false, errors.Wrap(err, "failed to delete db migration pod") 1263 } 1264 } 1265 1266 if instance.UsingCouchDB() { 1267 couchDBPod := &corev1.Pod{ 1268 ObjectMeta: v1.ObjectMeta{ 1269 Name: fmt.Sprintf("%s-couchdb", instance.GetName()), 1270 Namespace: instance.GetNamespace(), 1271 }, 1272 } 1273 1274 if err := p.Client.Delete(context.TODO(), couchDBPod); err != nil { 1275 return false, errors.Wrap(err, "failed to delete couchdb pod") 1276 } 1277 } 1278 1279 return false, nil 1280 default: 1281 return false, nil 1282 } 1283 } 1284 1285 type JobStatus string 1286 1287 const ( 1288 COMPLETED JobStatus = "completed" 1289 RUNNING JobStatus = "running" 1290 NOTFOUND JobStatus = "not-found" 1291 UNKNOWN JobStatus = "unknown" 1292 ) 1293 1294 func (p *Peer) CheckForRunningJobs(listOpt k8sclient.ListOption) (JobStatus, *jobv1.Job, error) { 1295 jobList := &batchv1.JobList{} 1296 if err := p.Client.List(context.TODO(), jobList, listOpt); err != nil { 1297 return NOTFOUND, nil, nil 1298 } 1299 1300 if len(jobList.Items) == 0 { 1301 return NOTFOUND, nil, nil 1302 } 1303 1304 // There should only be one job that is triggered per migration request 1305 k8sJob := jobList.Items[0] 1306 job := jobv1.NewWithDefaultsUseExistingName(&k8sJob) 1307 1308 if len(job.Job.Status.Conditions) > 0 { 1309 cond := job.Job.Status.Conditions[0] 1310 if cond.Type == batchv1.JobFailed { 1311 log.Info(fmt.Sprintf("Job '%s' failed for reason: %s: %s", job.Name, cond.Reason, cond.Message)) 1312 } 1313 } 1314 1315 completed, err := job.ContainerFinished(p.Client, "dbmigration") 1316 if err != nil { 1317 return UNKNOWN, nil, err 1318 } 1319 1320 if completed { 1321 return COMPLETED, job, nil 1322 1323 } 1324 1325 return RUNNING, nil, nil 1326 } 1327 1328 func (p *Peer) UpgradeDBs(instance *current.IBPPeer) error { 1329 log.Info("Upgrade DBs action requested") 1330 if err := action.UpgradeDBs(p.DeploymentManager, p.Client, instance, p.Config.Operator.Peer.Timeouts.DBMigration); err != nil { 1331 return errors.Wrap(err, "failed to reset peer") 1332 } 1333 orig := instance.DeepCopy() 1334 1335 instance.Spec.Action.UpgradeDBs = false 1336 if err := p.Client.Patch(context.TODO(), instance, k8sclient.MergeFrom(orig)); err != nil { 1337 return errors.Wrap(err, "failed to reset reenroll action flag") 1338 } 1339 1340 return nil 1341 } 1342 1343 func (p *Peer) EnrollForEcert(instance *current.IBPPeer) error { 1344 log.Info(fmt.Sprintf("Ecert enroll triggered via action parameter for '%s'", instance.GetName())) 1345 1346 secret := instance.Spec.Secret 1347 if secret == nil || secret.Enrollment == nil || secret.Enrollment.Component == nil { 1348 return errors.New("unable to enroll, no ecert enrollment information provided") 1349 } 1350 ecertSpec := secret.Enrollment.Component 1351 1352 storagePath := filepath.Join(p.GetInitStoragePath(instance), "ecert") 1353 crypto, err := action.Enroll(instance, ecertSpec, storagePath, p.Client, p.Scheme, true, p.Config.Operator.Peer.Timeouts.EnrollJob) 1354 if err != nil { 1355 return errors.Wrap(err, "failed to enroll for ecert") 1356 } 1357 1358 err = p.Initializer.GenerateSecrets("ecert", instance, crypto) 1359 if err != nil { 1360 return errors.Wrap(err, "failed to generate ecert secrets") 1361 } 1362 1363 return nil 1364 } 1365 1366 func (p *Peer) EnrollForTLSCert(instance *current.IBPPeer) error { 1367 log.Info(fmt.Sprintf("TLS cert enroll triggered via action parameter for '%s'", instance.GetName())) 1368 1369 secret := instance.Spec.Secret 1370 if secret == nil || secret.Enrollment == nil || secret.Enrollment.TLS == nil { 1371 return errors.New("unable to enroll, no TLS enrollment information provided") 1372 } 1373 tlscertSpec := secret.Enrollment.TLS 1374 1375 storagePath := filepath.Join(p.GetInitStoragePath(instance), "tls") 1376 crypto, err := action.Enroll(instance, tlscertSpec, storagePath, p.Client, p.Scheme, false, p.Config.Operator.Peer.Timeouts.EnrollJob) 1377 if err != nil { 1378 return errors.Wrap(err, "failed to enroll for TLS cert") 1379 } 1380 1381 err = p.Initializer.GenerateSecrets("tls", instance, crypto) 1382 if err != nil { 1383 return errors.Wrap(err, "failed to generate ecert secrets") 1384 } 1385 1386 return nil 1387 } 1388 1389 func (p *Peer) ReconcileHSMImages(instance *current.IBPPeer) bool { 1390 hsmConfig, err := commonconfig.ReadHSMConfig(p.Client, instance) 1391 if err != nil { 1392 return false 1393 } 1394 1395 if hsmConfig.Library.AutoUpdateDisabled { 1396 return false 1397 } 1398 1399 updated := false 1400 if hsmConfig.Library.Image != "" { 1401 hsmImage := hsmConfig.Library.Image 1402 lastIndex := strings.LastIndex(hsmImage, ":") 1403 image := hsmImage[:lastIndex] 1404 tag := hsmImage[lastIndex+1:] 1405 1406 if instance.Spec.Images.HSMImage != image { 1407 instance.Spec.Images.HSMImage = image 1408 updated = true 1409 } 1410 1411 if instance.Spec.Images.HSMTag != tag { 1412 instance.Spec.Images.HSMTag = tag 1413 updated = true 1414 } 1415 } 1416 1417 return updated 1418 } 1419 1420 func (p *Peer) HandleActions(instance *current.IBPPeer, update Update) error { 1421 orig := instance.DeepCopy() 1422 1423 if update.EcertReenrollNeeded() { 1424 if err := p.ReenrollEcert(instance); err != nil { 1425 log.Error(err, "Resetting action flag on failure") 1426 instance.ResetEcertReenroll() 1427 return err 1428 } 1429 instance.ResetEcertReenroll() 1430 } 1431 1432 if update.TLSReenrollNeeded() { 1433 if err := p.ReenrollTLSCert(instance); err != nil { 1434 log.Error(err, "Resetting action flag on failure") 1435 instance.ResetTLSReenroll() 1436 return err 1437 } 1438 instance.ResetTLSReenroll() 1439 } 1440 1441 if update.EcertNewKeyReenroll() { 1442 if err := p.ReenrollEcertNewKey(instance); err != nil { 1443 log.Error(err, "Resetting action flag on failure") 1444 instance.ResetEcertReenroll() 1445 return err 1446 } 1447 instance.ResetEcertReenroll() 1448 } 1449 1450 if update.TLScertNewKeyReenroll() { 1451 if err := p.ReenrollTLSCertNewKey(instance); err != nil { 1452 log.Error(err, "Resetting action flag on failure") 1453 instance.ResetTLSReenroll() 1454 return err 1455 } 1456 instance.ResetTLSReenroll() 1457 } 1458 1459 if update.EcertEnroll() { 1460 if err := p.EnrollForEcert(instance); err != nil { 1461 log.Error(err, "Resetting action flag on failure") 1462 instance.ResetEcertEnroll() 1463 return err 1464 } 1465 instance.ResetEcertEnroll() 1466 } 1467 1468 if update.TLSCertEnroll() { 1469 if err := p.EnrollForTLSCert(instance); err != nil { 1470 log.Error(err, "Resetting action flag on failure") 1471 instance.ResetTLSEnroll() 1472 return err 1473 } 1474 instance.ResetTLSEnroll() 1475 } 1476 1477 // Upgrade DBs needs to be one of the last thing that should be performed to allow for other 1478 // update flags to be processed 1479 if update.UpgradeDBs() { 1480 if err := p.UpgradeDBs(instance); err != nil { 1481 // not adding reset as this action should not be run twice 1482 // log.Error(err, "Resetting action flag on failure") 1483 return err 1484 } 1485 // Can return without continuing down to restart logic cause resetting a peer will 1486 // initiate a restart anyways 1487 instance.ResetUpgradeDBs() 1488 1489 } else if update.RestartNeeded() { 1490 if err := p.RestartAction(instance); err != nil { 1491 log.Error(err, "Resetting action flag on failure") 1492 instance.ResetRestart() 1493 return err 1494 } 1495 instance.ResetRestart() 1496 } 1497 1498 if err := p.Client.Patch(context.TODO(), instance, k8sclient.MergeFrom(orig)); err != nil { 1499 return errors.Wrap(err, "failed to reset action flags") 1500 } 1501 1502 return nil 1503 } 1504 1505 func (p *Peer) ReenrollEcert(instance *current.IBPPeer) error { 1506 log.Info("Ecert reenroll triggered via action parameter") 1507 if err := p.reenrollCert(instance, commoninit.ECERT, false); err != nil { 1508 return errors.Wrap(err, "ecert reenroll reusing existing private key action failed") 1509 } 1510 return nil 1511 } 1512 1513 func (p *Peer) ReenrollEcertNewKey(instance *current.IBPPeer) error { 1514 log.Info("Ecert with new key reenroll triggered via action parameter") 1515 if err := p.reenrollCert(instance, commoninit.ECERT, true); err != nil { 1516 return errors.Wrap(err, "ecert reenroll with new key action failed") 1517 } 1518 return nil 1519 } 1520 1521 func (p *Peer) ReenrollTLSCert(instance *current.IBPPeer) error { 1522 log.Info("TLS reenroll triggered via action parameter") 1523 if err := p.reenrollCert(instance, commoninit.TLS, false); err != nil { 1524 return errors.Wrap(err, "tls reenroll reusing existing private key action failed") 1525 } 1526 return nil 1527 } 1528 1529 func (p *Peer) ReenrollTLSCertNewKey(instance *current.IBPPeer) error { 1530 log.Info("TLS with new key reenroll triggered via action parameter") 1531 if err := p.reenrollCert(instance, commoninit.TLS, true); err != nil { 1532 return errors.Wrap(err, "tls reenroll with new key action failed") 1533 } 1534 return nil 1535 } 1536 1537 func (p *Peer) reenrollCert(instance *current.IBPPeer, certType commoninit.SecretType, newKey bool) error { 1538 return action.Reenroll(p, p.Client, certType, instance, newKey) 1539 } 1540 1541 func (p *Peer) RestartAction(instance *current.IBPPeer) error { 1542 log.Info("Restart triggered via action parameter") 1543 if err := p.Restart.ForRestartAction(instance); err != nil { 1544 return errors.Wrap(err, "failed to restart peer pods") 1545 } 1546 return nil 1547 } 1548 1549 func (p *Peer) HandleRestart(instance *current.IBPPeer, update Update) error { 1550 // If restart is disabled for components, can return immediately 1551 if p.Config.Operator.Restart.Disable.Components { 1552 return nil 1553 } 1554 1555 err := p.Restart.TriggerIfNeeded(instance) 1556 if err != nil { 1557 return errors.Wrap(err, "failed to restart deployment") 1558 } 1559 1560 return nil 1561 } 1562 1563 func (p *Peer) UpdateMSPCertificates(instance *current.IBPPeer) error { 1564 log.Info("Updating certificates passed in MSP spec") 1565 1566 updatedPeer, err := p.Initializer.GetUpdatedPeer(instance) 1567 if err != nil { 1568 return err 1569 } 1570 1571 crypto, err := updatedPeer.GenerateCrypto() 1572 if err != nil { 1573 return err 1574 } 1575 1576 if crypto != nil { 1577 err = p.Initializer.UpdateSecretsFromResponse(instance, crypto) 1578 if err != nil { 1579 return err 1580 } 1581 } 1582 1583 return nil 1584 } 1585 1586 func (p *Peer) RenewCert(certType commoninit.SecretType, obj runtime.Object, newKey bool) error { 1587 instance := obj.(*current.IBPPeer) 1588 if instance.Spec.Secret == nil { 1589 return errors.New(fmt.Sprintf("missing secret spec for instance '%s'", instance.GetName())) 1590 } 1591 1592 if instance.Spec.Secret.Enrollment != nil { 1593 log.Info(fmt.Sprintf("Renewing %s certificate for instance '%s'", string(certType), instance.Name)) 1594 1595 hsmEnabled := instance.IsHSMEnabled() 1596 storagePath := p.GetInitStoragePath(instance) 1597 spec := instance.Spec.Secret.Enrollment 1598 bccsp, err := p.GetBCCSPSectionForInstance(instance) 1599 if err != nil { 1600 return err 1601 } 1602 1603 err = p.CertificateManager.RenewCert(certType, instance, spec, bccsp, storagePath, hsmEnabled, newKey) 1604 if err != nil { 1605 return err 1606 } 1607 } else { 1608 return errors.New("cannot auto-renew certificate created by MSP, force renewal required") 1609 } 1610 1611 return nil 1612 } 1613 1614 func (p *Peer) CustomLogic(instance *current.IBPPeer, update Update) (*current.CRStatus, *common.Result, error) { 1615 var status *current.CRStatus 1616 var err error 1617 1618 if !p.CanSetCertificateTimer(instance, update) { 1619 log.Info("Certificate update detected but peer not yet deployed, requeuing request...") 1620 return status, &common.Result{ 1621 Result: reconcile.Result{ 1622 Requeue: true, 1623 }, 1624 }, nil 1625 } 1626 1627 // Check if crypto needs to be backed up before an update overrides exisitng secrets 1628 if update.CryptoBackupNeeded() { 1629 log.Info("Performing backup of TLS and ecert crypto") 1630 err = common.BackupCrypto(p.Client, p.Scheme, instance, p.GetLabels(instance)) 1631 if err != nil { 1632 return status, nil, errors.Wrap(err, "failed to backup TLS and ecert crypto") 1633 } 1634 } 1635 1636 status, err = p.CheckCertificates(instance) 1637 if err != nil { 1638 return status, nil, errors.Wrap(err, "failed to check for expiring certificates") 1639 } 1640 1641 if update.CertificateCreated() { 1642 log.Info(fmt.Sprintf("%s certificate was created", update.GetCreatedCertType())) 1643 err = p.SetCertificateTimer(instance, update.GetCreatedCertType()) 1644 if err != nil { 1645 return status, nil, errors.Wrap(err, "failed to set timer for certificate renewal") 1646 } 1647 } 1648 1649 if update.EcertUpdated() { 1650 log.Info("Ecert was updated") 1651 err = p.SetCertificateTimer(instance, commoninit.ECERT) 1652 if err != nil { 1653 return status, nil, errors.Wrap(err, "failed to set timer for certificate renewal") 1654 } 1655 } 1656 1657 if update.TLSCertUpdated() { 1658 log.Info("TLS cert was updated") 1659 err = p.SetCertificateTimer(instance, commoninit.TLS) 1660 if err != nil { 1661 return status, nil, errors.Wrap(err, "failed to set timer for certificate renewal") 1662 } 1663 } 1664 1665 return status, nil, err 1666 1667 } 1668 1669 func (p *Peer) CheckCertificates(instance *current.IBPPeer) (*current.CRStatus, error) { 1670 numSecondsBeforeExpire := instance.Spec.GetNumSecondsWarningPeriod() 1671 statusType, message, err := p.CertificateManager.CheckCertificatesForExpire(instance, numSecondsBeforeExpire) 1672 if err != nil { 1673 return nil, err 1674 } 1675 1676 crStatus := ¤t.CRStatus{ 1677 Type: statusType, 1678 Message: message, 1679 } 1680 1681 switch statusType { 1682 case current.Deployed: 1683 crStatus.Reason = "allPodsDeployed" 1684 default: 1685 crStatus.Reason = "certRenewalRequired" 1686 } 1687 1688 return crStatus, nil 1689 } 1690 1691 func (p *Peer) SetCertificateTimer(instance *current.IBPPeer, certType commoninit.SecretType) error { 1692 certName := fmt.Sprintf("%s-%s-signcert", certType, instance.Name) 1693 numSecondsBeforeExpire := instance.Spec.GetNumSecondsWarningPeriod() 1694 duration, err := p.CertificateManager.GetDurationToNextRenewal(certType, instance, numSecondsBeforeExpire) 1695 if err != nil { 1696 return err 1697 } 1698 1699 log.Info((fmt.Sprintf("Setting timer to renew %s %d days before it expires", certName, int(numSecondsBeforeExpire/DaysToSecondsConversion)))) 1700 1701 if p.RenewCertTimers[certName] != nil { 1702 p.RenewCertTimers[certName].Stop() 1703 p.RenewCertTimers[certName] = nil 1704 } 1705 p.RenewCertTimers[certName] = time.AfterFunc(duration, func() { 1706 // Check certs for updated status & set status so that reconcile is triggered after cert renewal. Reconcile loop will handle 1707 // checking certs again to determine whether instance status can return to Deployed 1708 err := p.UpdateCRStatus(instance) 1709 if err != nil { 1710 log.Error(err, "failed to update CR status") 1711 } 1712 1713 // get instance 1714 instanceLatest := ¤t.IBPPeer{} 1715 err = p.Client.Get(context.TODO(), types.NamespacedName{Namespace: instance.Namespace, Name: instance.Name}, instanceLatest) 1716 if err != nil { 1717 log.Error(err, "failed to get latest instance") 1718 return 1719 } 1720 1721 err = common.BackupCrypto(p.Client, p.Scheme, instance, p.GetLabels(instance)) 1722 if err != nil { 1723 log.Error(err, "failed to backup crypto before renewing cert") 1724 return 1725 } 1726 1727 err = p.RenewCert(certType, instanceLatest, false) 1728 if err != nil { 1729 log.Info(fmt.Sprintf("Failed to renew %s certificate: %s, status of %s remaining in Warning phase", certType, err, instanceLatest.GetName())) 1730 return 1731 } 1732 log.Info(fmt.Sprintf("%s renewal complete", certName)) 1733 }) 1734 1735 return nil 1736 } 1737 1738 // NOTE: This is called by the timer's subroutine when it goes off, not during a reconcile loop. 1739 // Therefore, it won't be overriden by the "SetStatus" method in ibppeer_controller.go 1740 func (p *Peer) UpdateCRStatus(instance *current.IBPPeer) error { 1741 status, err := p.CheckCertificates(instance) 1742 if err != nil { 1743 return errors.Wrap(err, "failed to check certificates") 1744 } 1745 1746 // Get most up-to-date instance at the time of update 1747 updatedInstance := ¤t.IBPPeer{} 1748 err = p.Client.Get(context.TODO(), types.NamespacedName{Name: instance.Name, Namespace: instance.Namespace}, updatedInstance) 1749 if err != nil { 1750 return errors.Wrap(err, "failed to get new instance") 1751 } 1752 1753 // Don't trigger reconcile if status remaining the same 1754 if updatedInstance.Status.Type == status.Type && updatedInstance.Status.Reason == status.Reason && updatedInstance.Status.Message == status.Message { 1755 return nil 1756 } 1757 1758 updatedInstance.Status.Type = status.Type 1759 updatedInstance.Status.Reason = status.Reason 1760 updatedInstance.Status.Message = status.Message 1761 updatedInstance.Status.Status = current.True 1762 updatedInstance.Status.LastHeartbeatTime = time.Now().String() 1763 1764 log.Info(fmt.Sprintf("Updating status of IBPPeer custom resource %s to %s phase", instance.Name, status.Type)) 1765 err = p.Client.UpdateStatus(context.TODO(), updatedInstance) 1766 if err != nil { 1767 return errors.Wrapf(err, "failed to update status to %s phase", status.Type) 1768 } 1769 1770 return nil 1771 } 1772 1773 // This function checks whether the instance is in Deployed or Warning state when a cert 1774 // update is detected. Only if Deployed or in Warning will a timer be set; otherwise, 1775 // the update will be requeued until the Peer has completed deploying. 1776 func (p *Peer) CanSetCertificateTimer(instance *current.IBPPeer, update Update) bool { 1777 if update.CertificateCreated() || update.CertificateUpdated() { 1778 if !(instance.Status.Type == current.Deployed || instance.Status.Type == current.Warning) { 1779 return false 1780 } 1781 } 1782 return true 1783 }