github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/command/operator.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 command 20 21 import ( 22 "context" 23 "flag" 24 "fmt" 25 "os" 26 "runtime" 27 "time" 28 29 k8sruntime "k8s.io/apimachinery/pkg/runtime" 30 31 routev1 "github.com/openshift/api/route/v1" 32 "github.com/operator-framework/operator-lib/leader" 33 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 34 clientgoscheme "k8s.io/client-go/kubernetes/scheme" 35 ctrl "sigs.k8s.io/controller-runtime" 36 37 "github.com/pkg/errors" 38 "sigs.k8s.io/controller-runtime/pkg/log/zap" 39 40 apis "github.com/IBM-Blockchain/fabric-operator/api" 41 ibpv1beta1 "github.com/IBM-Blockchain/fabric-operator/api/v1beta1" 42 controller "github.com/IBM-Blockchain/fabric-operator/controllers" 43 oconfig "github.com/IBM-Blockchain/fabric-operator/operatorconfig" 44 "github.com/IBM-Blockchain/fabric-operator/pkg/migrator" 45 "github.com/IBM-Blockchain/fabric-operator/pkg/offering" 46 openshiftv1 "github.com/openshift/api/config/v1" 47 48 "k8s.io/apimachinery/pkg/types" 49 _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 50 "sigs.k8s.io/controller-runtime/pkg/client" 51 52 logf "sigs.k8s.io/controller-runtime/pkg/log" 53 "sigs.k8s.io/controller-runtime/pkg/manager/signals" 54 ) 55 56 var log = logf.Log.WithName("cmd_operator") 57 58 var ( 59 scheme = k8sruntime.NewScheme() 60 setupLog = ctrl.Log.WithName("setup") 61 ) 62 63 func init() { 64 utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 65 utilruntime.Must(ibpv1beta1.AddToScheme(scheme)) 66 // +kubebuilder:scaffold:scheme 67 } 68 69 func printVersion() { 70 log.Info(fmt.Sprintf("Go Version: %s", runtime.Version())) 71 log.Info(fmt.Sprintf("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)) 72 } 73 74 func Operator(operatorCfg *oconfig.Config) error { 75 signalHandler := signals.SetupSignalHandler() 76 77 // In local mode, the operator may be launched and debugged directly as a native process without 78 // being deployed to a Kubernetes cluster. 79 local := os.Getenv("OPERATOR_LOCAL_MODE") == "true" 80 81 return OperatorWithSignal(operatorCfg, signalHandler, true, local) 82 } 83 84 func OperatorWithSignal(operatorCfg *oconfig.Config, signalHandler context.Context, blocking, local bool) error { 85 var err error 86 87 // Add the zap logger flag set to the CLI. The flag set must 88 // be added before calling pflag.Parse(). 89 // pflag.CommandLine.AddFlagSet(flagset) 90 91 // Add flags registered by imported packages (e.g. glog and 92 // controller-runtime) 93 // pflag.CommandLine.AddGoFlagSet(flag.CommandLine) 94 // pflag.Parse() 95 96 // Use a zap logr.Logger implementation. If none of the zap 97 // flags are configured (or if the zap flag set is not being 98 // used), this defaults to a production zap logger. 99 // 100 // The logger instantiated here can be changed to any logger 101 // implementing the logr.Logger interface. This logger will 102 // be propagated through the whole operator, generating 103 // uniform and structured logs. 104 if operatorCfg.Logger != nil { 105 logf.SetLogger(*operatorCfg.Logger) 106 ctrl.SetLogger(*operatorCfg.Logger) 107 } else { 108 // Use the unstructured log formatter when running locally. 109 logf.SetLogger(zap.New(zap.UseDevMode(local))) 110 ctrl.SetLogger(zap.New(zap.UseDevMode(true))) 111 } 112 113 printVersion() 114 115 watchNamespace := os.Getenv("WATCH_NAMESPACE") 116 var operatorNamespace string 117 if watchNamespace == "" { 118 // Operator is running in all namespace mode 119 log.Info("Installing operator in all namespace mode") 120 operatorNamespace, err = GetOperatorNamespace() 121 if err != nil { 122 log.Error(err, "Failed to get operator namespace") 123 time.Sleep(15 * time.Second) 124 return err 125 } 126 } else { 127 log.Info("Installing operator in own namespace mode", "WATCH_NAMESPACE", watchNamespace) 128 operatorNamespace = watchNamespace 129 } 130 131 if !local { 132 label := os.Getenv("OPERATOR_LABEL_PREFIX") 133 if label == "" { 134 label = "fabric" 135 } 136 err = leader.Become(context.TODO(), label+"-operator-lock") 137 if err != nil { 138 log.Error(err, "Failed to retry for leader lock") 139 os.Exit(1) 140 } 141 } else { 142 log.Info("local run detected, skipping leader election") 143 } 144 145 var metricsAddr string 146 var enableLeaderElection bool 147 148 if flag.Lookup("metrics-addr") == nil { 149 flag.StringVar(&metricsAddr, "metrics-addr", ":8383", "The address the metric endpoint binds to.") 150 } 151 if flag.Lookup("enable-leader-election") == nil { 152 flag.BoolVar(&enableLeaderElection, "enable-leader-election", true, 153 "Enable leader election for controller manager. "+ 154 "Enabling this will ensure there is only one active controller manager.") 155 } 156 flag.Parse() 157 158 mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ 159 Scheme: scheme, 160 MetricsBindAddress: metricsAddr, 161 Port: 9443, 162 // LeaderElection: enableLeaderElection, 163 LeaderElectionID: "c30dd930.ibp.com", 164 LeaderElectionNamespace: operatorNamespace, 165 Namespace: watchNamespace, 166 }) 167 if err != nil { 168 setupLog.Error(err, "unable to start manager") 169 return err 170 } 171 172 log.Info("Registering Components.") 173 174 // Setup Scheme for all resources 175 if err := apis.AddToScheme(mgr.GetScheme()); err != nil { 176 log.Error(err, "") 177 return err 178 } 179 180 //Add route scheme 181 if err := routev1.AddToScheme(mgr.GetScheme()); err != nil { 182 log.Error(err, "") 183 return err 184 } 185 186 //Add clusterversion scheme 187 if err := openshiftv1.AddToScheme(mgr.GetScheme()); err != nil { 188 log.Error(err, "") 189 return err 190 } 191 192 utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 193 utilruntime.Must(ibpv1beta1.AddToScheme(scheme)) 194 195 go func() { 196 runtime.Gosched() 197 mgrSyncContext, mgrSyncContextCancel := context.WithTimeout(context.Background(), 30*time.Second) 198 defer mgrSyncContextCancel() 199 200 log.Info("Waiting for cache sync") 201 if synced := mgr.GetCache().WaitForCacheSync(mgrSyncContext); !synced { 202 log.Error(nil, "Timed out waiting for cache sync") 203 os.Exit(1) 204 } 205 206 log.Info("Cache sync done") 207 208 // Migrate first 209 m := migrator.New(mgr, operatorCfg, operatorNamespace) 210 err = m.Migrate() 211 if err != nil { 212 log.Error(err, "Unable to complete migration") 213 os.Exit(1) 214 } 215 216 // Setup all Controllers 217 if err := controller.AddToManager(mgr, operatorCfg); err != nil { 218 log.Error(err, "") 219 os.Exit(1) 220 } 221 }() 222 223 if err := InitConfig(operatorNamespace, operatorCfg, mgr.GetAPIReader()); err != nil { 224 log.Error(err, "Invalid configuration") 225 time.Sleep(15 * time.Second) 226 return err 227 } 228 229 log.Info("Starting the Cmd.") 230 231 // Start the Cmd 232 if blocking { 233 if err := mgr.Start(signalHandler); err != nil { 234 log.Error(err, "Manager exited non-zero") 235 return err 236 } 237 } else { 238 go mgr.Start(signalHandler) 239 } 240 241 return nil 242 } 243 244 //go:generate counterfeiter -o mocks/reader.go -fake-name Reader . Reader 245 246 type Reader interface { 247 client.Reader 248 } 249 250 // InitConfig initializes the passed in config by overriding values from environment variable 251 // or config map if set 252 func InitConfig(namespace string, cfg *oconfig.Config, client client.Reader) error { 253 // Read from config map if it exists otherwise return default values 254 err := oconfig.LoadFromConfigMap( 255 types.NamespacedName{Name: "operator-config", Namespace: namespace}, 256 "config.yaml", 257 client, 258 &cfg.Operator, 259 ) 260 if err != nil { 261 return errors.Wrap(err, "failed to get 'config.yaml' from 'ibp-operator' config map") 262 } 263 264 clusterType := os.Getenv("CLUSTERTYPE") 265 offeringType, err := offering.GetType(clusterType) 266 if err != nil { 267 return err 268 } 269 cfg.Offering = offeringType 270 271 log.Info(fmt.Sprintf("Operator configured for cluster type '%s'", cfg.Offering)) 272 273 if cfg.Operator.Versions == nil { 274 return errors.New("no default images defined") 275 } 276 277 if cfg.Operator.Versions.CA == nil { 278 return errors.New("no default CA images defined") 279 } 280 281 if cfg.Operator.Versions.Peer == nil { 282 return errors.New("no default Peer images defined") 283 } 284 285 if cfg.Operator.Versions.Orderer == nil { 286 return errors.New("no default Orderer images defined") 287 } 288 289 return nil 290 } 291 292 func GetOperatorNamespace() (string, error) { 293 operatorNamespace := os.Getenv("OPERATOR_NAMESPACE") 294 if operatorNamespace == "" { 295 return "", fmt.Errorf("OPERATOR_NAMESPACE not found") 296 } 297 298 return operatorNamespace, nil 299 }