github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/offering/openshift/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 openshiftpeer 20 21 import ( 22 "context" 23 "fmt" 24 "regexp" 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/openshift/peer/override" 36 "github.com/IBM-Blockchain/fabric-operator/pkg/operatorerrors" 37 "github.com/IBM-Blockchain/fabric-operator/version" 38 openshiftv1 "github.com/openshift/api/config/v1" 39 routev1 "github.com/openshift/api/route/v1" 40 "github.com/pkg/errors" 41 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 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("openshift_peer") 50 51 type Override interface { 52 basepeer.Override 53 PeerRoute(object v1.Object, route *routev1.Route, action resources.Action) error 54 OperationsRoute(object v1.Object, route *routev1.Route, action resources.Action) error 55 PeerGRPCRoute(object v1.Object, route *routev1.Route, action resources.Action) error 56 } 57 58 var _ basepeer.IBPPeer = &Peer{} 59 60 type Peer struct { 61 *basepeer.Peer 62 63 RouteManager resources.Manager 64 OperationsRouteManager resources.Manager 65 GRPCRouteManager resources.Manager 66 RestClient *clientset.Clientset 67 68 Override Override 69 } 70 71 func New(client controllerclient.Client, scheme *runtime.Scheme, config *config.Config, restclient *clientset.Clientset) *Peer { 72 o := &override.Override{ 73 Override: basepeeroverride.Override{ 74 Client: client, 75 DefaultCouchContainerFile: config.PeerInitConfig.CouchContainerFile, 76 DefaultCouchInitContainerFile: config.PeerInitConfig.CouchInitContainerFile, 77 DefaultCCLauncherFile: config.PeerInitConfig.CCLauncherFile, 78 }, 79 } 80 81 peer := &Peer{ 82 Peer: basepeer.New(client, scheme, config, o), 83 Override: o, 84 RestClient: restclient, 85 } 86 87 peer.CreateManagers() 88 return peer 89 } 90 91 func (p *Peer) CreateManagers() { 92 resourceManager := resourcemanager.New(p.Client, p.Scheme) 93 p.RouteManager = resourceManager.CreateRouteManager("peer", p.Override.PeerRoute, p.GetLabels, p.Config.PeerInitConfig.RouteFile) 94 p.OperationsRouteManager = resourceManager.CreateRouteManager("operations", p.Override.OperationsRoute, p.GetLabels, p.Config.PeerInitConfig.RouteFile) 95 p.GRPCRouteManager = resourceManager.CreateRouteManager("grpcweb", p.Override.PeerGRPCRoute, p.GetLabels, p.Config.PeerInitConfig.RouteFile) 96 } 97 98 func (p *Peer) ReconcileManagers(instance *current.IBPPeer, update basepeer.Update) error { 99 err := p.Peer.ReconcileManagers(instance, update) 100 if err != nil { 101 return err 102 } 103 104 err = p.RouteManager.Reconcile(instance, update.SpecUpdated()) 105 if err != nil { 106 return errors.Wrap(err, "failed Peer Route reconciliation") 107 } 108 109 err = p.OperationsRouteManager.Reconcile(instance, update.SpecUpdated()) 110 if err != nil { 111 return errors.Wrap(err, "failed Operations Route reconciliation") 112 } 113 114 err = p.GRPCRouteManager.Reconcile(instance, update.SpecUpdated()) 115 if err != nil { 116 return errors.Wrap(err, "failed Peer GRPC Route reconciliation") 117 } 118 119 return nil 120 } 121 122 func (p *Peer) Reconcile(instance *current.IBPPeer, update basepeer.Update) (common.Result, error) { 123 var err error 124 var status *current.CRStatus 125 126 versionSet, err := p.SetVersion(instance) 127 if err != nil { 128 return common.Result{}, errors.Wrap(err, fmt.Sprintf("failed updating CR '%s' to version '%s'", instance.Name, version.Operator)) 129 } 130 if versionSet { 131 log.Info("Instance version updated, requeuing request...") 132 return common.Result{ 133 Result: reconcile.Result{ 134 Requeue: true, 135 }, 136 }, nil 137 } 138 139 updatecr, err := p.SelectDinDArgs(instance) 140 if err != nil { 141 log.Info("Cannot get cluster version. Ignoring openshift cluster version") 142 } 143 144 update.SetDindArgsUpdated(updatecr) 145 instanceUpdated, err := p.PreReconcileChecks(instance, update) 146 if err != nil { 147 return common.Result{}, errors.Wrap(err, "failed pre reconcile checks") 148 } 149 150 // We do not have to wait for service to get the external endpoint 151 // thus we call UpdateExternalEndpoint in reconcile before reconcile managers 152 externalEndpointUpdated := p.UpdateExternalEndpoint(instance) 153 154 hostAPI := fmt.Sprintf("%s-%s-peer.%s", instance.Namespace, instance.Name, instance.Spec.Domain) 155 hostOperations := fmt.Sprintf("%s-%s-operations.%s", instance.Namespace, instance.Name, instance.Spec.Domain) 156 hostGrpc := fmt.Sprintf("%s-%s-grpcweb.%s", instance.Namespace, instance.Name, instance.Spec.Domain) 157 hosts := []string{hostAPI, hostOperations, hostGrpc, "127.0.0.1"} 158 csrHostUpdated := p.CheckCSRHosts(instance, hosts) 159 160 if instanceUpdated || externalEndpointUpdated || csrHostUpdated { 161 log.Info(fmt.Sprintf("Updating instance after pre reconcile checks: %t, updating external endpoint: %t, csrhost Updated: %t", instanceUpdated, externalEndpointUpdated, csrHostUpdated)) 162 err := p.Client.Patch(context.TODO(), instance, nil, controllerclient.PatchOption{ 163 Resilient: &controllerclient.ResilientPatch{ 164 Retry: 3, 165 Into: ¤t.IBPPeer{}, 166 Strategy: k8sclient.MergeFrom, 167 }, 168 }) 169 if err != nil { 170 return common.Result{}, errors.Wrap(err, "failed to update instance after prereconcile checks") 171 } 172 173 log.Info("Instance updated, requeuing request...") 174 return common.Result{ 175 Result: reconcile.Result{ 176 Requeue: true, 177 }, 178 }, nil 179 } 180 181 jobRunning, err := p.HandleMigrationJobs(k8sclient.MatchingLabels{ 182 "owner": instance.GetName(), 183 "job-name": fmt.Sprintf("%s-dbmigration", instance.GetName()), 184 }, instance) 185 if jobRunning { 186 log.Info(fmt.Sprintf("Requeuing request until job completes")) 187 return common.Result{ 188 Result: reconcile.Result{ 189 Requeue: true, 190 }, 191 }, nil 192 } 193 if err != nil { 194 return common.Result{}, err 195 } 196 197 err = p.Initialize(instance, update) 198 if err != nil { 199 return common.Result{}, operatorerrors.Wrap(err, operatorerrors.PeerInitilizationFailed, "failed to initialize peer") 200 } 201 202 if update.PeerTagUpdated() { 203 if err := p.ReconcileFabricPeerMigrationV1_4(instance); err != nil { 204 return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer versions") 205 } 206 } 207 208 if update.MigrateToV2() { 209 if err := p.ReconcileFabricPeerMigrationV2_0(instance); err != nil { 210 return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer to version v2.0.x") 211 } 212 } 213 214 if update.MigrateToV24() { 215 if err := p.ReconcileFabricPeerMigrationV2_4(instance); err != nil { 216 return common.Result{}, operatorerrors.Wrap(err, operatorerrors.FabricPeerMigrationFailed, "failed to migrate fabric peer to version v2.4.x") 217 } 218 } 219 220 err = p.ReconcileManagers(instance, update) 221 if err != nil { 222 return common.Result{}, errors.Wrap(err, "failed to reconcile managers") 223 } 224 225 err = p.UpdateConnectionProfile(instance) 226 if err != nil { 227 return common.Result{}, errors.Wrap(err, "failed to create connection profile") 228 } 229 230 err = p.CheckStates(instance) 231 if err != nil { 232 return common.Result{}, errors.Wrap(err, "failed to check and restore state") 233 } 234 235 // custom product logic can be implemented here 236 // No-Op atm 237 status, result, err := p.CustomLogic(instance, update) 238 if err != nil { 239 return common.Result{}, errors.Wrap(err, "failed to run custom offering logic") 240 } 241 if result != nil { 242 log.Info(fmt.Sprintf("Finished reconciling '%s' with Custom Logic result", instance.GetName())) 243 return *result, nil 244 } 245 246 if update.EcertUpdated() { 247 log.Info("Ecert was updated") 248 // Request deployment restart for tls cert update 249 err = p.Restart.ForCertUpdate(commoninit.ECERT, instance) 250 if err != nil { 251 return common.Result{}, errors.Wrap(err, "failed to update restart config") 252 } 253 } 254 255 if update.TLSCertUpdated() { 256 log.Info("TLS cert was updated") 257 // Request deployment restart for ecert update 258 err = p.Restart.ForCertUpdate(commoninit.TLS, instance) 259 if err != nil { 260 return common.Result{}, errors.Wrap(err, "failed to update restart config") 261 } 262 } 263 264 if update.MSPUpdated() { 265 err = p.UpdateMSPCertificates(instance) 266 if err != nil { 267 return common.Result{}, errors.Wrap(err, "failed to update certificates passed in MSP spec") 268 } 269 } 270 271 if err := p.HandleActions(instance, update); err != nil { 272 return common.Result{}, err 273 } 274 275 // If configs were update during initialize, need to restart pods to pick up new 276 // config changes. This should be done as the last the step, specifically after ReconcileManagers, 277 // to allow all any updates to the deployment to be completed before restarting. 278 // Trigger deployment restart by deleting deployment 279 if err := p.HandleRestart(instance, update); err != nil { 280 return common.Result{}, err 281 } 282 283 return common.Result{ 284 Status: status, 285 }, nil 286 } 287 288 func (p *Peer) SelectDinDArgs(instance *current.IBPPeer) (bool, error) { 289 290 if len(instance.Spec.DindArgs) != 0 { 291 return false, nil 292 } 293 294 clusterversion := openshiftv1.ClusterVersion{} 295 296 err := p.RestClient.RESTClient().Get(). 297 AbsPath("apis", "config.openshift.io", "v1", "clusterversions", "version"). 298 Do(context.TODO()). 299 Into(&clusterversion) 300 301 if err != nil { 302 return false, err 303 } 304 305 dindargs := []string{"--log-driver", "fluentd", "--log-opt", "fluentd-address=localhost:9880", "--mtu", "1400", "--iptables=true"} 306 307 re := regexp.MustCompile(`4\.[0-9]\.[0-9]`) 308 if re.MatchString(clusterversion.Status.Desired.Version) { 309 dindargs = []string{"--log-driver", "fluentd", "--log-opt", "fluentd-address=localhost:9880", "--mtu", "1400", "--iptables=false"} 310 } 311 312 instance.Spec.DindArgs = dindargs 313 314 return true, nil 315 }