github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/k8s/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 k8speer 20 21 import ( 22 "context" 23 "fmt" 24 "strings" 25 26 current "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 27 config "github.com/IBM-Blockchain/fabric-operator/operatorconfig" 28 commoninit "github.com/IBM-Blockchain/fabric-operator/pkg/initializer/common" 29 controllerclient "github.com/IBM-Blockchain/fabric-operator/pkg/k8s/controllerclient" 30 "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources" 31 resourcemanager "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/manager" 32 basepeer "github.com/IBM-Blockchain/fabric-operator/pkg/offering/base/peer" 33 basepeeroverride "github.com/IBM-Blockchain/fabric-operator/pkg/offering/base/peer/override" 34 "github.com/IBM-Blockchain/fabric-operator/pkg/offering/common" 35 "github.com/IBM-Blockchain/fabric-operator/pkg/offering/k8s/peer/override" 36 "github.com/IBM-Blockchain/fabric-operator/pkg/operatorerrors" 37 "github.com/IBM-Blockchain/fabric-operator/pkg/util" 38 "github.com/IBM-Blockchain/fabric-operator/version" 39 "github.com/pkg/errors" 40 networkingv1 "k8s.io/api/networking/v1" 41 networkingv1beta1 "k8s.io/api/networking/v1beta1" 42 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 43 "k8s.io/apimachinery/pkg/runtime" 44 k8sclient "sigs.k8s.io/controller-runtime/pkg/client" 45 logf "sigs.k8s.io/controller-runtime/pkg/log" 46 "sigs.k8s.io/controller-runtime/pkg/reconcile" 47 ) 48 49 var log = logf.Log.WithName("k8s_peer") 50 51 type Override interface { 52 basepeer.Override 53 Ingress(v1.Object, *networkingv1.Ingress, resources.Action) error 54 Ingressv1beta1(v1.Object, *networkingv1beta1.Ingress, resources.Action) error 55 } 56 57 var _ basepeer.IBPPeer = &Peer{} 58 59 type Peer struct { 60 *basepeer.Peer 61 62 IngressManager resources.Manager 63 Ingressv1beta1Manager resources.Manager 64 65 Override Override 66 } 67 68 func New(client controllerclient.Client, scheme *runtime.Scheme, config *config.Config) *Peer { 69 o := &override.Override{ 70 Override: basepeeroverride.Override{ 71 Client: client, 72 DefaultCouchContainerFile: config.PeerInitConfig.CouchContainerFile, 73 DefaultCouchInitContainerFile: config.PeerInitConfig.CouchInitContainerFile, 74 DefaultCCLauncherFile: config.PeerInitConfig.CCLauncherFile, 75 }, 76 } 77 78 p := &Peer{ 79 Peer: basepeer.New(client, scheme, config, o), 80 Override: o, 81 } 82 83 p.CreateManagers() 84 return p 85 } 86 87 func (p *Peer) CreateManagers() { 88 resourceManager := resourcemanager.New(p.Client, p.Scheme) 89 p.IngressManager = resourceManager.CreateIngressManager("", p.Override.Ingress, p.GetLabels, p.Config.PeerInitConfig.IngressFile) 90 p.Ingressv1beta1Manager = resourceManager.CreateIngressv1beta1Manager("", p.Override.Ingressv1beta1, p.GetLabels, p.Config.PeerInitConfig.Ingressv1beta1File) 91 } 92 93 func (p *Peer) ReconcileManagers(instance *current.IBPPeer, update basepeer.Update) error { 94 err := p.Peer.ReconcileManagers(instance, update) 95 if err != nil { 96 return err 97 } 98 99 err = p.ReconcileIngressManager(instance, update.SpecUpdated()) 100 if err != nil { 101 return errors.Wrap(err, "failed Ingress reconciliation") 102 } 103 104 return nil 105 } 106 107 func (p *Peer) Reconcile(instance *current.IBPPeer, update basepeer.Update) (common.Result, error) { 108 var err error 109 var status *current.CRStatus 110 111 versionSet, err := p.SetVersion(instance) 112 if err != nil { 113 return common.Result{}, errors.Wrap(err, fmt.Sprintf("failed updating CR '%s' to version '%s'", instance.Name, version.Operator)) 114 } 115 if versionSet { 116 log.Info("Instance version updated, requeuing request...") 117 return common.Result{ 118 Result: reconcile.Result{ 119 Requeue: true, 120 }, 121 }, nil 122 } 123 124 instanceUpdated, err := p.PreReconcileChecks(instance, update) 125 if err != nil { 126 return common.Result{}, errors.Wrap(err, "failed pre reconcile checks") 127 } 128 129 // We do not have to wait for service to get the external endpoint 130 // thus we call UpdateExternalEndpoint in reconcile before reconcile managers 131 externalEndpointUpdated := p.UpdateExternalEndpoint(instance) 132 133 hostAPI := fmt.Sprintf("%s-%s-peer.%s", instance.Namespace, instance.Name, instance.Spec.Domain) 134 hostOperations := fmt.Sprintf("%s-%s-operations.%s", instance.Namespace, instance.Name, instance.Spec.Domain) 135 hostGrpcWeb := fmt.Sprintf("%s-%s-grpcweb.%s", instance.Namespace, instance.Name, instance.Spec.Domain) 136 hosts := []string{hostAPI, hostOperations, hostGrpcWeb, "127.0.0.1"} 137 csrHostUpdated := p.CheckCSRHosts(instance, hosts) 138 139 if instanceUpdated || externalEndpointUpdated || csrHostUpdated { 140 log.Info(fmt.Sprintf("Updating instance after pre reconcile checks: %t, updating external endpoint: %t, csr host updated: %t", instanceUpdated, externalEndpointUpdated, csrHostUpdated)) 141 err := p.Client.Patch(context.TODO(), instance, nil, controllerclient.PatchOption{ 142 Resilient: &controllerclient.ResilientPatch{ 143 Retry: 3, 144 Into: ¤t.IBPPeer{}, 145 Strategy: k8sclient.MergeFrom, 146 }, 147 }) 148 if err != nil { 149 return common.Result{}, errors.Wrap(err, "failed to update instance after prereconcile checks") 150 } 151 152 log.Info("Instance updated, requeuing request...") 153 return common.Result{ 154 Result: reconcile.Result{ 155 Requeue: true, 156 }, 157 }, nil 158 } 159 160 jobRunning, err := p.HandleMigrationJobs(k8sclient.MatchingLabels{ 161 "owner": instance.GetName(), 162 "job-name": fmt.Sprintf("%s-dbmigration", instance.GetName()), 163 }, instance) 164 if jobRunning { 165 log.Info(fmt.Sprintf("Requeuing request until job completes")) 166 return common.Result{ 167 Result: reconcile.Result{ 168 Requeue: true, 169 }, 170 }, nil 171 } 172 if err != nil { 173 return common.Result{}, err 174 } 175 176 err = p.Initialize(instance, update) 177 if err != nil { 178 return common.Result{}, operatorerrors.Wrap(err, operatorerrors.PeerInitilizationFailed, "failed to initialize peer") 179 } 180 181 if update.PeerTagUpdated() { 182 if err := p.ReconcileFabricPeerMigrationV1_4(instance); err != nil { 183 return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer versions") 184 } 185 } 186 187 if update.MigrateToV2() { 188 if err := p.ReconcileFabricPeerMigrationV2_0(instance); err != nil { 189 return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer to version v2.0.x") 190 } 191 } 192 193 if update.MigrateToV24() { 194 if err := p.ReconcileFabricPeerMigrationV2_4(instance); err != nil { 195 return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer to version v2.4.x") 196 } 197 } 198 199 err = p.ReconcileManagers(instance, update) 200 if err != nil { 201 return common.Result{}, errors.Wrap(err, "failed to reconcile managers") 202 } 203 204 err = p.UpdateConnectionProfile(instance) 205 if err != nil { 206 return common.Result{}, errors.Wrap(err, "failed to create connection profile") 207 } 208 209 err = p.CheckStates(instance) 210 if err != nil { 211 return common.Result{}, errors.Wrap(err, "failed to check and restore state") 212 } 213 214 // custom product logic can be implemented here 215 // No-Op atm 216 status, result, err := p.CustomLogic(instance, update) 217 if err != nil { 218 return common.Result{}, errors.Wrap(err, "failed to run custom offering logic") 219 } 220 if result != nil { 221 log.Info(fmt.Sprintf("Finished reconciling '%s' with Custom Logic result", instance.GetName())) 222 return *result, nil 223 } 224 225 if update.EcertUpdated() { 226 log.Info("Ecert was updated") 227 // Request deployment restart for tls cert update 228 err = p.Restart.ForCertUpdate(commoninit.ECERT, instance) 229 if err != nil { 230 return common.Result{}, errors.Wrap(err, "failed to update restart config") 231 } 232 } 233 234 if update.TLSCertUpdated() { 235 log.Info("TLS cert was updated") 236 // Request deployment restart for ecert update 237 err = p.Restart.ForCertUpdate(commoninit.TLS, instance) 238 if err != nil { 239 return common.Result{}, errors.Wrap(err, "failed to update restart config") 240 } 241 } 242 243 if update.MSPUpdated() { 244 err = p.UpdateMSPCertificates(instance) 245 if err != nil { 246 return common.Result{}, errors.Wrap(err, "failed to update certificates passed in MSP spec") 247 } 248 } 249 250 if err := p.HandleActions(instance, update); err != nil { 251 return common.Result{}, err 252 } 253 254 // If configs were update during initialize, need to restart pods to pick up new 255 // config changes. This should be done as the last the step, specifically after ReconcileManagers, 256 // to allow all any updates to the deployment to be completed before restarting. 257 // Trigger deployment restart by deleting deployment 258 if err := p.HandleRestart(instance, update); err != nil { 259 return common.Result{}, err 260 } 261 262 return common.Result{ 263 Status: status, 264 }, nil 265 } 266 267 func (p *Peer) ReconcileIngressManager(instance *current.IBPPeer, update bool) error { 268 if p.Config.Operator.Globals.AllowKubernetesEighteen == "true" { 269 // check k8s version 270 version, err := util.GetServerVersion() 271 if err != nil { 272 return err 273 } 274 if strings.Compare(version.Minor, "19") < 0 { // v1beta 275 err = p.Ingressv1beta1Manager.Reconcile(instance, update) 276 if err != nil { 277 return errors.Wrap(err, "failed Ingressv1beta1 reconciliation") 278 } 279 } else { 280 err = p.IngressManager.Reconcile(instance, update) 281 if err != nil { 282 return errors.Wrap(err, "failed Ingress reconciliation") 283 } 284 } 285 } else { 286 err := p.IngressManager.Reconcile(instance, update) 287 if err != nil { 288 return errors.Wrap(err, "failed Ingress reconciliation") 289 } 290 } 291 return nil 292 }