github.com/verrazzano/verrazzano@v1.7.1/application-operator/controllers/wlsworkload/weblogicworkload_controller.go (about) 1 // Copyright (c) 2021, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package wlsworkload 5 6 import ( 7 "bytes" 8 "context" 9 "crypto/rand" 10 "encoding/json" 11 "errors" 12 "fmt" 13 "math/big" 14 "os" 15 "reflect" 16 "strconv" 17 "strings" 18 "text/template" 19 20 vzlogInit "github.com/verrazzano/verrazzano/pkg/log" 21 22 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2" 23 "github.com/crossplane/oam-kubernetes-runtime/pkg/oam" 24 vzapi "github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1" 25 "github.com/verrazzano/verrazzano/application-operator/constants" 26 "github.com/verrazzano/verrazzano/application-operator/controllers/clusters" 27 "github.com/verrazzano/verrazzano/application-operator/controllers/logging" 28 "github.com/verrazzano/verrazzano/application-operator/controllers/metricstrait" 29 vznav "github.com/verrazzano/verrazzano/application-operator/controllers/navigation" 30 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 31 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 32 "go.uber.org/zap" 33 corev1 "k8s.io/api/core/v1" 34 k8serrors "k8s.io/apimachinery/pkg/api/errors" 35 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 36 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 37 "k8s.io/apimachinery/pkg/runtime" 38 "k8s.io/apimachinery/pkg/types" 39 ctrl "sigs.k8s.io/controller-runtime" 40 "sigs.k8s.io/controller-runtime/pkg/client" 41 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 42 "sigs.k8s.io/controller-runtime/pkg/reconcile" 43 "sigs.k8s.io/yaml" 44 ) 45 46 const ( 47 metadataField = "metadata" 48 specField = "spec" 49 loggingNamePart = "logging-stdout" 50 loggingMountPath = "/fluentd/etc/custom.conf" 51 loggingKey = "custom.conf" 52 defaultMode int32 = 400 53 lastServerStartPolicyAnnotation = "verrazzano-io/last-server-start-policy" 54 Never = "Never" 55 NeverV8 = "NEVER" 56 IfNeeded = "IfNeeded" 57 IfNeededV8 = "IF_NEEDED" 58 webLogicDomainUIDLabel = "weblogic.domainUID" 59 webLogicPluginConfigYamlKey = "WebLogicPlugin.yaml" 60 WDTConfigMapNameSuffix = "-wdt-config-map" 61 controllerName = "weblogicworkload" 62 DomainKind = "Domain" 63 ClusterKind = "Cluster" 64 APIVersionV8 = "weblogic.oracle/v8" 65 APIVersionV9 = "weblogic.oracle/v9" 66 APIVersionV1 = "weblogic.oracle/v1" 67 ) 68 69 const defaultMonitoringExporterTemplate = ` 70 { 71 {{.ImageSetting}}"imagePullPolicy": "IfNotPresent", 72 "configuration": { 73 "domainQualifier": true, 74 "metricsNameSnakeCase": true, 75 "queries": [ 76 { 77 "key": "name", 78 "keyName": "location", 79 "prefix": "wls_server_", 80 "applicationRuntimes": { 81 "key": "name", 82 "keyName": "app", 83 "componentRuntimes": { 84 "prefix": "wls_webapp_config_", 85 "type": "WebAppComponentRuntime", 86 "key": "name", 87 "values": [ 88 "deploymentState", 89 "contextRoot", 90 "sourceInfo", 91 "sessionsOpenedTotalCount", 92 "openSessionsCurrentCount", 93 "openSessionsHighCount" 94 ], 95 "servlets": { 96 "prefix": "wls_servlet_", 97 "key": "servletName" 98 } 99 } 100 } 101 }, 102 { 103 "JVMRuntime": { 104 "prefix": "wls_jvm_", 105 "key": "name" 106 } 107 }, 108 { 109 "executeQueueRuntimes": { 110 "prefix": "wls_socketmuxer_", 111 "key": "name", 112 "values": [ 113 "pendingRequestCurrentCount" 114 ] 115 } 116 }, 117 { 118 "workManagerRuntimes": { 119 "prefix": "wls_workmanager_", 120 "key": "name", 121 "values": [ 122 "stuckThreadCount", 123 "pendingRequests", 124 "completedRequests" 125 ] 126 } 127 }, 128 { 129 "threadPoolRuntime": { 130 "prefix": "wls_threadpool_", 131 "key": "name", 132 "values": [ 133 "executeThreadTotalCount", 134 "queueLength", 135 "stuckThreadCount", 136 "hoggingThreadCount" 137 ] 138 } 139 }, 140 { 141 "JMSRuntime": { 142 "key": "name", 143 "keyName": "jmsruntime", 144 "prefix": "wls_jmsruntime_", 145 "JMSServers": { 146 "prefix": "wls_jms_", 147 "key": "name", 148 "keyName": "jmsserver", 149 "destinations": { 150 "prefix": "wls_jms_dest_", 151 "key": "name", 152 "keyName": "destination" 153 } 154 } 155 } 156 }, 157 { 158 "persistentStoreRuntimes": { 159 "prefix": "wls_persistentstore_", 160 "key": "name" 161 } 162 }, 163 { 164 "JDBCServiceRuntime": { 165 "JDBCDataSourceRuntimeMBeans": { 166 "prefix": "wls_datasource_", 167 "key": "name" 168 } 169 } 170 }, 171 { 172 "JTARuntime": { 173 "prefix": "wls_jta_", 174 "key": "name" 175 } 176 } 177 ] 178 } 179 } 180 ` 181 182 type defaultMonitoringExporterTemplateData struct { 183 ImageSetting string 184 } 185 186 const defaultWDTConfigMapData = ` 187 { 188 "resources": { 189 "WebAppContainer": { 190 "WeblogicPluginEnabled" : true 191 } 192 } 193 } 194 ` 195 196 var metaAnnotationFields = []string{metadataField, "annotations"} 197 var specDomainUID = []string{specField, "domainUID"} 198 var specServerPodFields = []string{specField, "serverPod"} 199 var specServerServiceFields = []string{specField, "serverService"} 200 var specServerPodLabelsFields = append(specServerPodFields, "labels") 201 var specServerPodContainersFields = append(specServerPodFields, "containers") 202 var specServerPodVolumesFields = append(specServerPodFields, "volumes") 203 var specServerPodVolumeMountsFields = append(specServerPodFields, "volumeMounts") 204 var specServerServiceLabelsFields = append(specServerServiceFields, "labels") 205 var specConfigurationIstioEnabledFields = []string{specField, "configuration", "istio", "enabled"} 206 var specConfigurationRuntimeEncryptionSecret = []string{specField, "configuration", "model", "runtimeEncryptionSecret"} 207 var specDomainSourceType = []string{specField, "domainHomeSourceType"} 208 var specConfigurationWDTConfigMap = []string{specField, "configuration", "model", "configMap"} 209 var specConfigurationDomainOnPVConfigMap = []string{specField, "configuration", "initializeDomainOnPV", "domain", "domainCreationConfigMap"} 210 var specConfigurationInitializeDomainOnPV = []string{specField, "configuration", "initializeDomainOnPV"} 211 var specMonitoringExporterFields = []string{specField, "monitoringExporter"} 212 var specRestartVersionFields = []string{specField, "restartVersion"} 213 var specServerStartPolicyFields = []string{specField, "serverStartPolicy"} 214 var specLogHomeFields = []string{specField, "logHome"} 215 var specLogHomeEnabledFields = []string{specField, "logHomeEnabled"} 216 var specLogHomeLayoutFields = []string{specField, "logHomeLayout"} 217 218 // this struct allows us to extract information from the unstructured WebLogic spec, 219 // so we can interface with the FLUENTD code 220 type containersMountsVolumes struct { 221 Containers []corev1.Container 222 Volumes []corev1.Volume 223 VolumeMounts []corev1.VolumeMount 224 } 225 226 // Reconciler reconciles a VerrazzanoWebLogicWorkload object 227 type Reconciler struct { 228 client.Client 229 Log *zap.SugaredLogger 230 Scheme *runtime.Scheme 231 Metrics *metricstrait.Reconciler 232 } 233 234 // SetupWithManager registers our controller with the manager 235 func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { 236 return ctrl.NewControllerManagedBy(mgr). 237 For(&vzapi.VerrazzanoWebLogicWorkload{}). 238 Complete(r) 239 } 240 241 // Reconcile reconciles a VerrazzanoWebLogicWorkload resource. It fetches the embedded WebLogic Domain CR, mutates it to add 242 // scopes and traits, and then writes out the CR (or deletes it if the workload is being deleted). 243 // +kubebuilder:rbac:groups=oam.verrazzano.io,resources=verrazzanoweblogicworkloads,verbs=get;list;watch;create;update;patch;delete 244 // +kubebuilder:rbac:groups=oam.verrazzano.io,resources=verrazzanoweblogicworkloads/status,verbs=get;update;patch 245 func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 246 if ctx == nil { 247 return ctrl.Result{}, errors.New("context cannot be nil") 248 } 249 250 // We do not want any resource to get reconciled if it is in namespace kube-system 251 // This is due to a bug found in OKE, it should not affect functionality of any vz operators 252 // If this is the case then return success 253 if req.Namespace == vzconst.KubeSystem { 254 log := zap.S().With(vzlogInit.FieldResourceNamespace, req.Namespace, vzlogInit.FieldResourceName, req.Name, vzlogInit.FieldController, controllerName) 255 log.Infof("Weblogic workload resource %v should not be reconciled in kube-system namespace, ignoring", req.NamespacedName) 256 return reconcile.Result{}, nil 257 } 258 259 // fetch the workload and unwrap the WebLogic resource 260 workload, err := r.fetchWorkload(ctx, req.NamespacedName, zap.S()) 261 if err != nil { 262 return clusters.IgnoreNotFoundWithLog(err, zap.S()) 263 } 264 log, err := clusters.GetResourceLogger("verrazzanoweblogicworkload", req.NamespacedName, workload) 265 if err != nil { 266 zap.S().Errorf("Failed to create controller logger for weblogic workload resource: %v", err) 267 return clusters.NewRequeueWithDelay(), nil 268 } 269 log.Oncef("Reconciling WebLogic workload resource %v, generation %v", req.NamespacedName, workload.Generation) 270 271 res, err := r.doReconcile(ctx, workload, log) 272 if clusters.ShouldRequeue(res) { 273 return res, nil 274 } 275 // Never return an error since it has already been logged. We don't want the 276 // controller runtime to log again (with stack trace). Just re-queue if there is an error. 277 if err != nil { 278 return clusters.NewRequeueWithDelay(), nil 279 } 280 281 log.Oncef("Finished reconciling WebLogic workload %v", req.NamespacedName) 282 283 return ctrl.Result{}, nil 284 } 285 286 // doReconcile performs the reconciliation operations for the WebLogic workload 287 func (r *Reconciler) doReconcile(ctx context.Context, workload *vzapi.VerrazzanoWebLogicWorkload, log vzlog.VerrazzanoLogger) (ctrl.Result, error) { 288 // Make sure the last generation exists in the status 289 result, err := r.ensureLastGeneration(workload) 290 if err != nil || result.Requeue { 291 return result, err 292 } 293 294 u, err := r.initializeDomain(workload, log) 295 if err != nil { 296 return reconcile.Result{}, err 297 } 298 299 cus, err := r.initializeClusters(workload, log) 300 if err != nil { 301 return reconcile.Result{}, err 302 } 303 304 var existingDomain unstructured.Unstructured 305 existingDomain.SetAPIVersion(u.GetAPIVersion()) 306 existingDomain.SetKind(u.GetKind()) 307 domainExists := true 308 domainKey := types.NamespacedName{Name: u.GetName(), Namespace: workload.Namespace} 309 if err := r.Get(ctx, domainKey, &existingDomain); err != nil { 310 if k8serrors.IsNotFound(err) { 311 log.Debug("No existing domain found") 312 domainExists = false 313 } else { 314 log.Errorf("Failed trying to obtain an existing domain: %v", err) 315 return reconcile.Result{}, err 316 } 317 } 318 319 // If the domain already exists, make sure that the domain can be restarted. 320 // If the domain cannot be restarted, don't make any domain changes. 321 if domainExists && !r.isOkToRestartWebLogic(workload) { 322 log.Debug("There have been no changes to the WebLogic workload, nor has the restart annotation changed. The Domain will not be modified.") 323 return ctrl.Result{}, nil 324 } 325 326 // Add the Fluentd sidecar container required for logging to the Domain. If the image is old, update it 327 if err = r.addLogging(ctx, log, workload, u); err != nil { 328 return reconcile.Result{}, err 329 } 330 331 // Add logging traits to the Domain if they exist 332 if err = r.addLoggingTrait(ctx, log, workload, u); err != nil { 333 return reconcile.Result{}, err 334 } 335 336 // Add the monitoringExporter to the spec if not already present 337 if err = addDefaultMonitoringExporter(u); err != nil { 338 return reconcile.Result{}, err 339 } 340 341 // The istio.enabled field is no longer needed for v9 domains 342 if isV8(u) { 343 // Get the namespace resource that the VerrazzanoWebLogicWorkload resource is deployed to 344 namespace := &corev1.Namespace{} 345 if err = r.Client.Get(ctx, client.ObjectKey{Namespace: "", Name: workload.Namespace}, namespace); err != nil { 346 return reconcile.Result{}, err 347 } 348 349 // Set the domain resource configuration.istio.enabled value 350 if err = updateIstioEnabled(namespace.Labels, u); err != nil { 351 return reconcile.Result{}, err 352 } 353 } 354 355 // create the RuntimeEncryptionSecret if specified and the secret does not exist 356 secret, found, err := unstructured.NestedString(u.Object, specConfigurationRuntimeEncryptionSecret...) 357 if err != nil { 358 return reconcile.Result{}, err 359 } 360 if found { 361 nspace, _, err := unstructured.NestedString(u.Object, metadataField, "namespace") 362 if err != nil { 363 return reconcile.Result{}, err 364 } 365 err = r.createRuntimeEncryptionSecret(ctx, log, nspace, secret, workload.ObjectMeta.Labels) 366 if err != nil { 367 return reconcile.Result{}, err 368 } 369 } 370 371 // Set/Update the WDT config map with WeblogicPluginEnabled setting 372 if err = r.CreateOrUpdateWDTConfigMap(ctx, log, workload.Namespace, u, workload.ObjectMeta.Labels); err != nil { 373 return reconcile.Result{}, err 374 } 375 376 // Create or update Cluster resources 377 for i := range cus { 378 if err = r.createOrUpdateResource(ctx, workload, log, cus[i], func(specCopy interface{}) error { 379 if err := unstructured.SetNestedField(cus[i].Object, specCopy, specField); err != nil { 380 return err 381 } 382 383 return nil 384 }); err != nil { 385 log.Errorf("Failed creating or updating WebLogic cluster CR: %v", err) 386 return reconcile.Result{}, err 387 } 388 } 389 390 // Create or update Domain resource 391 if err = r.createOrUpdateResource(ctx, workload, log, u, func(specCopy interface{}) error { 392 // Set the new Domain spec fields from the copy first, so we can overlay the lifecycle fields/annotations after, 393 // otherwise they will be lost 394 if err := unstructured.SetNestedField(u.Object, specCopy, specField); err != nil { 395 return err 396 } 397 // If the domain already exists set any fields related to restart 398 if domainExists { 399 err = setDomainLifecycleFields(log, workload, u) 400 if err != nil { 401 return err 402 } 403 } 404 405 return nil 406 }); err != nil { 407 log.Errorf("Failed creating or updating WebLogic domain CR: %v", err) 408 return reconcile.Result{}, err 409 } 410 411 if err = r.updateStatusReconcileDone(ctx, workload); err != nil { 412 return reconcile.Result{}, err 413 } 414 415 log.Debug("Successfully reconcile the WebLogic workload") 416 return reconcile.Result{}, nil 417 } 418 419 func (r *Reconciler) initializeDomain(workload *vzapi.VerrazzanoWebLogicWorkload, log vzlog.VerrazzanoLogger) (*unstructured.Unstructured, error) { 420 var u unstructured.Unstructured 421 err := r.initializeResource(&u, workload, &workload.Spec.Template, log) 422 if err != nil { 423 return nil, err 424 } 425 426 if u.GetAPIVersion() == "" { 427 u.SetAPIVersion(APIVersionV8) 428 } 429 u.SetKind(DomainKind) 430 431 return &u, nil 432 } 433 434 func (r *Reconciler) initializeClusters(workload *vzapi.VerrazzanoWebLogicWorkload, log vzlog.VerrazzanoLogger) ([]*unstructured.Unstructured, error) { 435 var clus []*unstructured.Unstructured 436 for i := range workload.Spec.Clusters { 437 var u unstructured.Unstructured 438 err := r.initializeResource(&u, workload, &workload.Spec.Clusters[i], log) 439 if err != nil { 440 return nil, err 441 } 442 443 if u.GetAPIVersion() == "" { 444 u.SetAPIVersion(APIVersionV1) 445 } 446 u.SetKind(ClusterKind) 447 clus = append(clus, &u) 448 } 449 450 return clus, nil 451 } 452 453 func (r *Reconciler) initializeResource(u *unstructured.Unstructured, workload *vzapi.VerrazzanoWebLogicWorkload, resource *vzapi.VerrazzanoWebLogicWorkloadTemplate, log vzlog.VerrazzanoLogger) error { 454 spec, err := vznav.ConvertRawExtensionToUnstructured(&resource.Spec) 455 if err != nil { 456 return err 457 } 458 459 if resource.APIVersion != "" { 460 u.SetAPIVersion(resource.APIVersion) 461 } 462 463 if u.Object == nil { 464 u.Object = make(map[string]interface{}) 465 } 466 if err = unstructured.SetNestedField(u.Object, spec.Object, specField); err != nil { 467 return err 468 } 469 470 metadata, err := vznav.ConvertRawExtensionToUnstructured(&resource.Metadata) 471 if err != nil { 472 return err 473 } 474 475 if err = unstructured.SetNestedField(u.Object, metadata.Object, metadataField); err != nil { 476 return err 477 } 478 479 // make sure the namespace is set to the namespace of the component 480 if err = unstructured.SetNestedField(u.Object, workload.Namespace, metadataField, "namespace"); err != nil { 481 return err 482 } 483 484 // mutate the WebLogic domain resource, copy labels, add logging, etc. 485 if err = copyLabels(log, workload.ObjectMeta.GetLabels(), u); err != nil { 486 return err 487 } 488 489 return nil 490 } 491 492 func (r *Reconciler) createOrUpdateResource(ctx context.Context, workload *vzapi.VerrazzanoWebLogicWorkload, log vzlog.VerrazzanoLogger, u *unstructured.Unstructured, f func(interface{}) error) error { 493 // make a copy of the WebLogic spec since u.Object will get overwritten in CreateOrUpdate 494 // if the WebLogic CR exists 495 specCopy, _, err := unstructured.NestedFieldCopy(u.Object, specField) 496 if err != nil { 497 log.Errorf("Failed to make a copy of the WebLogic spec: %v", err) 498 return err 499 } 500 501 // set controller reference so the WebLogic domain CR gets deleted when the workload is deleted 502 if err = controllerutil.SetControllerReference(workload, u, r.Scheme); err != nil { 503 log.Errorf("Failed to set controller ref: %v", err) 504 return err 505 } 506 507 if y, err := yaml.Marshal(u); err != nil { 508 log.Debugf("Resource in raw format: %s ", u) 509 } else { 510 log.Debugf("Resource in YAML format: %s", string(y)) 511 } 512 513 // write out the WebLogic resource 514 _, err = controllerutil.CreateOrUpdate(ctx, r.Client, u, func() error { 515 return f(specCopy) 516 }) 517 if err != nil { 518 log.Errorf("Failed creating or updating WebLogic CR: %v", err) 519 return err 520 } 521 522 return nil 523 } 524 525 // fetchWorkload fetches the VerrazzanoWebLogicWorkload data given a namespaced name 526 func (r *Reconciler) fetchWorkload(ctx context.Context, name types.NamespacedName, log *zap.SugaredLogger) (*vzapi.VerrazzanoWebLogicWorkload, error) { 527 var workload vzapi.VerrazzanoWebLogicWorkload 528 if err := r.Get(ctx, name, &workload); err != nil { 529 if k8serrors.IsNotFound(err) { 530 log.Debugf("VerrazzanoWebLogicWorkload %s has been deleted", name.Name) 531 } else { 532 log.Errorf("Failed to fetch VerrazzanoWebLogicWorkload %s: %v", name.Name, err) 533 } 534 return nil, err 535 } 536 537 return &workload, nil 538 } 539 540 // Make sure that the last generation exists in the status 541 func (r *Reconciler) ensureLastGeneration(wl *vzapi.VerrazzanoWebLogicWorkload) (ctrl.Result, error) { 542 if len(wl.Status.LastGeneration) > 0 { 543 return ctrl.Result{}, nil 544 } 545 546 // Update the status generation and always requeue 547 wl.Status.LastGeneration = strconv.Itoa(int(wl.Generation)) 548 err := r.Status().Update(context.TODO(), wl) 549 return ctrl.Result{Requeue: true, RequeueAfter: 1}, err 550 } 551 552 // Make sure that it is OK to restart WebLogic 553 func (r *Reconciler) isOkToRestartWebLogic(wl *vzapi.VerrazzanoWebLogicWorkload) bool { 554 // Check if user created or changed the restart of lifecycle annotation 555 if wl.Annotations != nil { 556 if wl.Annotations[vzconst.RestartVersionAnnotation] != wl.Status.LastRestartVersion { 557 return true 558 } 559 if wl.Annotations[vzconst.LifecycleActionAnnotation] != wl.Status.LastLifecycleAction { 560 return true 561 } 562 } 563 if wl.Status.LastGeneration != strconv.Itoa(int(wl.Generation)) { 564 // The spec has changed ok to restart 565 return true 566 } 567 // nothing in the spec or lifecycle annotations has changed 568 return false 569 } 570 571 func isV8(weblogic *unstructured.Unstructured) bool { 572 return weblogic.GetAPIVersion() == APIVersionV8 573 } 574 575 // copyLabels copies specific labels from the Verrazzano workload to the contained WebLogic resource 576 func copyLabels(log vzlog.VerrazzanoLogger, workloadLabels map[string]string, weblogic *unstructured.Unstructured) error { 577 // the WebLogic domain spec/serverPod/labels field has labels that get propagated to the pods 578 labels, found, _ := unstructured.NestedStringMap(weblogic.Object, specServerPodLabelsFields...) 579 if !found { 580 labels = map[string]string{} 581 } 582 583 // the WebLogic domain spec/serverService/labels field has labels that get propagated to the service 584 svcLabels, found, _ := unstructured.NestedStringMap(weblogic.Object, specServerServiceLabelsFields...) 585 if !found { 586 svcLabels = map[string]string{} 587 } 588 589 // copy the oam component and app name labels 590 if componentName, ok := workloadLabels[oam.LabelAppComponent]; ok { 591 labels[oam.LabelAppComponent] = componentName 592 svcLabels[oam.LabelAppComponent] = componentName 593 } 594 595 if appName, ok := workloadLabels[oam.LabelAppName]; ok { 596 labels[oam.LabelAppName] = appName 597 svcLabels[oam.LabelAppName] = appName 598 } 599 600 // Set the label indicating this is WebLogic workload 601 labels[constants.LabelWorkloadType] = constants.WorkloadTypeWeblogic 602 603 err := unstructured.SetNestedStringMap(weblogic.Object, labels, specServerPodLabelsFields...) 604 if err != nil { 605 log.Errorf("Failed to set labels in spec serverPod: %v", err) 606 return err 607 } 608 err = unstructured.SetNestedStringMap(weblogic.Object, labels, specServerServiceLabelsFields...) 609 if err != nil { 610 log.Errorf("Failed to set labels in spec serverService: %v", err) 611 return err 612 } 613 return nil 614 } 615 616 // addLogging adds a FLUENTD sidecar and updates the WebLogic spec if there is an associated LogInfo 617 // If the Fluentd image changed during an upgrade, then the new image will be used 618 func (r *Reconciler) addLogging(ctx context.Context, log vzlog.VerrazzanoLogger, workload *vzapi.VerrazzanoWebLogicWorkload, weblogic *unstructured.Unstructured) error { 619 // extract just enough of the WebLogic data into concrete types, so we can merge with 620 // the FLUENTD data 621 var extracted containersMountsVolumes 622 if serverPod, found, _ := unstructured.NestedMap(weblogic.Object, specServerPodFields...); found { 623 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(serverPod, &extracted); err != nil { 624 return errors.New("unable to extract containers, volumes, and volume mounts from WebLogic spec") 625 } 626 } 627 628 name, found, _ := unstructured.NestedString(weblogic.Object, "metadata", "name") 629 if !found { 630 return errors.New("expected to find metadata name in WebLogic spec") 631 } 632 633 // get the existing logHome setting - if it's set we use it otherwise we'll generate a logs location 634 // using an emptydir volume 635 volumeMountPath := scratchVolMountPath 636 volumeName := storageVolumeName 637 foundVolumeMount := false 638 logHome, _, _ := unstructured.NestedString(weblogic.Object, specLogHomeFields...) 639 if logHome != "" { 640 // find the existing volume mount for the logHome - the Fluentd volume mount needs to match 641 for _, mount := range extracted.VolumeMounts { 642 if strings.HasPrefix(logHome, mount.MountPath) { 643 volumeMountPath = mount.MountPath 644 volumeName = mount.Name 645 foundVolumeMount = true 646 break 647 } 648 } 649 650 if !foundVolumeMount { 651 // user specified logHome, but it's not on any volume, Fluentd sidecar won't be able to collect logs 652 log.Info("Unable to find a volume mount for domain logHome, log collection will not work") 653 } 654 } 655 _, logHomeEnabledSet, _ := unstructured.NestedBool(weblogic.Object, specLogHomeEnabledFields...) 656 657 // fluentdPod starts with what's in the spec. We add in the FLUENTD things when Apply is 658 // called on the fluentdManager 659 fluentdPod := &logging.FluentdPod{ 660 Containers: extracted.Containers, 661 Volumes: extracted.Volumes, 662 VolumeMounts: extracted.VolumeMounts, 663 LogPath: getWLSLogPath(logHome, name), 664 HandlerEnv: getWlsSpecificContainerEnv(logHome, name), 665 } 666 fluentdManager := &logging.Fluentd{Context: ctx, 667 Log: zap.S(), 668 Client: r.Client, 669 ParseRules: WlsFluentdParsingRules, 670 StorageVolumeName: volumeName, 671 StorageVolumeMountPath: volumeMountPath, 672 WorkloadType: workloadType, 673 } 674 675 // fluentdManager.Apply wants a QRR, but it only cares about the namespace (at least for 676 // this use case) 677 resource := vzapi.QualifiedResourceRelation{Namespace: workload.Namespace} 678 679 // note that this call has the side effect of creating a FLUENTD config map if one 680 // does not already exist in the namespace 681 if err := fluentdManager.Apply(logging.NewLogInfo(), resource, fluentdPod); err != nil { 682 return err 683 } 684 685 // convert the containers, volumes, and mounts in fluentdPod to unstructured and set 686 // the values in the spec 687 fluentdPodUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(fluentdPod) 688 if err != nil { 689 return err 690 } 691 692 err = unstructured.SetNestedSlice(weblogic.Object, fluentdPodUnstructured["containers"].([]interface{}), specServerPodContainersFields...) 693 if err != nil { 694 log.Errorf("Failed to set serverPod containers: %v", err) 695 return err 696 } 697 err = unstructured.SetNestedSlice(weblogic.Object, fluentdPodUnstructured["volumes"].([]interface{}), specServerPodVolumesFields...) 698 if err != nil { 699 log.Errorf("Failed to set serverPod volumes: %v", err) 700 return err 701 } 702 err = unstructured.SetNestedField(weblogic.Object, fluentdPodUnstructured["volumeMounts"].([]interface{}), specServerPodVolumeMountsFields...) 703 if err != nil { 704 log.Errorf("Failed to set serverPod volumeMounts: %v", err) 705 return err 706 } 707 708 // set logHome if it was not already specified in the domain spec 709 if logHome == "" { 710 err = unstructured.SetNestedField(weblogic.Object, getWLSLogHome(name), specLogHomeFields...) 711 if err != nil { 712 log.Errorf("Failed to set logHome: %v", err) 713 return err 714 } 715 } 716 // set logHomeEnabled if it was not already specified in the domain spec 717 if !logHomeEnabledSet { 718 err = unstructured.SetNestedField(weblogic.Object, true, specLogHomeEnabledFields...) 719 if err != nil { 720 log.Errorf("Failed to set logHomeEnabled: %v", err) 721 return err 722 } 723 } 724 725 if !isV8(weblogic) { 726 err = unstructured.SetNestedField(weblogic.Object, "Flat", specLogHomeLayoutFields...) 727 if err != nil { 728 log.Errorf("Failed to set logHomeLayout: %v", err) 729 return err 730 } 731 } 732 733 return nil 734 } 735 736 // createRuntimeEncryptionSecret creates the runtimeEncryptionSecret specified in the domain spec if it does not exist. 737 func (r *Reconciler) createRuntimeEncryptionSecret(ctx context.Context, log vzlog.VerrazzanoLogger, namespaceName string, secretName string, workloadLabels map[string]string) error { 738 appName, ok := workloadLabels[oam.LabelAppName] 739 if !ok { 740 return errors.New("OAM app name label missing from metadata, unable to create owner reference to appconfig") 741 } 742 743 // Create the secret if it does not already exist 744 secret := &corev1.Secret{} 745 err := r.Get(ctx, client.ObjectKey{Namespace: namespaceName, Name: secretName}, secret) 746 if err != nil && k8serrors.IsNotFound(err) { 747 thePassword, err := genPassword(128) 748 if err != nil { 749 return err 750 } 751 secret = &corev1.Secret{ 752 TypeMeta: metav1.TypeMeta{ 753 APIVersion: "v1", 754 Kind: "Secret", 755 }, 756 ObjectMeta: metav1.ObjectMeta{ 757 Namespace: namespaceName, 758 Name: secretName, 759 }, 760 Data: map[string][]byte{ 761 "password": []byte(thePassword), 762 }, 763 } 764 765 // Set the owner reference. 766 appConfig := &v1alpha2.ApplicationConfiguration{} 767 err = r.Get(context.TODO(), types.NamespacedName{Namespace: namespaceName, Name: appName}, appConfig) 768 if err != nil { 769 return err 770 } 771 err = controllerutil.SetControllerReference(appConfig, secret, r.Scheme) 772 if err != nil { 773 return err 774 } 775 776 log.Debugf("Creating secret %s:%s", namespaceName, secretName) 777 err = r.Create(ctx, secret) 778 if err != nil { 779 return err 780 } 781 782 } else if err != nil { 783 return err 784 } 785 log.Debugf("Secret %s:%s already exist", namespaceName, secretName) 786 787 return nil 788 } 789 790 // Update the status field with life cyele information as needed 791 func (r *Reconciler) updateStatusReconcileDone(ctx context.Context, wl *vzapi.VerrazzanoWebLogicWorkload) error { 792 update := false 793 if wl.Status.LastGeneration != strconv.Itoa(int(wl.Generation)) { 794 wl.Status.LastGeneration = strconv.Itoa(int(wl.Generation)) 795 update = true 796 } 797 if wl.Annotations != nil { 798 if wl.Annotations[vzconst.RestartVersionAnnotation] != wl.Status.LastRestartVersion { 799 wl.Status.LastRestartVersion = wl.Annotations[vzconst.RestartVersionAnnotation] 800 update = true 801 } 802 if wl.Annotations[vzconst.LifecycleActionAnnotation] != wl.Status.LastLifecycleAction { 803 wl.Status.LastLifecycleAction = wl.Annotations[vzconst.LifecycleActionAnnotation] 804 update = true 805 } 806 } 807 if update { 808 return r.Status().Update(ctx, wl) 809 } 810 return nil 811 } 812 813 // GetConfigMapLocation gets the location of the configmap in the model. 814 func (r *Reconciler) GetConfigMapLocation(u *unstructured.Unstructured) ([]string, error) { 815 //Find domainHomeSourceType 816 domainHomeSourceType, exists, err := unstructured.NestedString(u.Object, specDomainSourceType...) 817 if err != nil { 818 return nil, err 819 } 820 if exists { 821 if domainHomeSourceType == "PersistentVolume" { 822 _, fnd, err := unstructured.NestedMap(u.Object, specConfigurationInitializeDomainOnPV...) 823 if err != nil { 824 return nil, err 825 826 } 827 if fnd { 828 return specConfigurationDomainOnPVConfigMap, err 829 } 830 } 831 } 832 return specConfigurationWDTConfigMap, err 833 } 834 835 // CreateOrUpdateWDTConfigMap creates a default WDT config map with WeblogicPluginEnabled setting if the 836 // WDT config map is not specified in the WebLogic spec. Otherwise, it updates the specified WDT config map 837 // with WeblogicPluginEnabled setting if not already done. 838 func (r *Reconciler) CreateOrUpdateWDTConfigMap(ctx context.Context, log vzlog.VerrazzanoLogger, namespaceName string, u *unstructured.Unstructured, workloadLabels map[string]string) error { 839 // Get the specified WDT config map name in the WebLogic spec 840 var location, err = r.GetConfigMapLocation(u) 841 if err != nil { 842 log.Errorf("Failed to get location of WDT configMap from WebLogic spec: %v", err) 843 return err 844 } 845 846 configMapName, found, err := unstructured.NestedString(u.Object, location...) 847 if err != nil { 848 log.Errorf("Failed to extract WDT configMap from WebLogic spec: %v", err) 849 return err 850 } 851 if !found { 852 domainUID, domainUIDFound, err := unstructured.NestedString(u.Object, specDomainUID...) 853 if err != nil { 854 log.Errorf("Failed to extract domainUID from the WebLogic spec: %v", err) 855 return err 856 } 857 if !domainUIDFound { 858 log.Errorf("Failed to find domainUID in WebLogic spec: %v", err) 859 return errors.New("unable to find domainUID in WebLogic spec") 860 } 861 // Create a default WDT config map 862 err = r.createDefaultWDTConfigMap(ctx, log, namespaceName, domainUID, workloadLabels) 863 if err != nil { 864 return err 865 } 866 // Set WDT config map field in WebLogic spec 867 err = unstructured.SetNestedField(u.Object, getWDTConfigMapName(domainUID), location...) 868 if err != nil { 869 log.Errorf("Failed to set WDT config map in WebLogic spec: %v", err) 870 return err 871 } 872 } else { 873 configMap, err := r.getConfigMap(ctx, u.GetNamespace(), configMapName) 874 if err != nil { 875 return err 876 } 877 if configMap == nil { 878 log.Errorf("Failed to find the specified WDT config map: %v", err) 879 return err 880 } 881 // Update WDT configMap configuration to add default WLS plugin configuration 882 v := configMap.Data[webLogicPluginConfigYamlKey] 883 if v == "" { 884 byt, err := yaml.JSONToYAML([]byte(defaultWDTConfigMapData)) 885 if err != nil { 886 return err 887 } 888 if configMap.Data == nil { 889 configMap.Data = map[string]string{} 890 } 891 configMap.Data[webLogicPluginConfigYamlKey] = string(byt) 892 err = r.Client.Update(ctx, configMap) 893 if err != nil { 894 return err 895 } 896 } 897 } 898 return nil 899 } 900 901 // createDefaultWDTConfigMap creates a default WDT config map with WeblogicPluginEnabled setting. 902 func (r *Reconciler) createDefaultWDTConfigMap(ctx context.Context, log vzlog.VerrazzanoLogger, namespaceName string, domainName string, workloadLabels map[string]string) error { 903 configMapName := getWDTConfigMapName(domainName) 904 // Create a configMap resource that will contain WeblogicPluginEnabled setting 905 configMap := &corev1.ConfigMap{ 906 TypeMeta: metav1.TypeMeta{ 907 APIVersion: "v1", 908 Kind: "ConfigMap", 909 }, 910 ObjectMeta: metav1.ObjectMeta{ 911 Name: configMapName, 912 Namespace: namespaceName, 913 Labels: map[string]string{ 914 webLogicDomainUIDLabel: domainName, 915 }, 916 }, 917 } 918 // Create the config map if it does not already exist 919 configMapFound := &corev1.ConfigMap{} 920 log.Debugf("Checking if WDT ConfigMap %s:%s exists", namespaceName, configMapName) 921 err := r.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: namespaceName}, configMapFound) 922 if err != nil && k8serrors.IsNotFound(err) { 923 // set controller reference so the WDT config map gets deleted when the app config is deleted 924 appName, ok := workloadLabels[oam.LabelAppName] 925 if !ok { 926 return errors.New("OAM app name label missing from metadata, unable to create WDT config map") 927 } 928 appConfig := &v1alpha2.ApplicationConfiguration{} 929 err = r.Get(context.TODO(), types.NamespacedName{Namespace: namespaceName, Name: appName}, appConfig) 930 if err != nil { 931 return err 932 } 933 if err = controllerutil.SetControllerReference(appConfig, configMap, r.Scheme); err != nil { 934 log.Errorf("Failed to set controller ref for WDT config map: %v", err) 935 return err 936 } 937 byt, err := yaml.JSONToYAML([]byte(defaultWDTConfigMapData)) 938 if err != nil { 939 return err 940 } 941 configMap.Data = map[string]string{webLogicPluginConfigYamlKey: string(byt)} 942 log.Debugf("Creating WDT ConfigMap %s:%s", namespaceName, configMapName) 943 err = r.Create(ctx, configMap) 944 if err != nil { 945 return err 946 } 947 return nil 948 } else if err != nil { 949 return err 950 } 951 log.Debugf("ConfigMap %s:%s already exists", namespaceName, configMapName) 952 return nil 953 } 954 955 // getConfigMap will get the ConfigMap for the given name 956 func (r *Reconciler) getConfigMap(ctx context.Context, namespace string, configMapName string) (*corev1.ConfigMap, error) { 957 var wdtConfigMap = &corev1.ConfigMap{} 958 err := r.Client.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: namespace}, wdtConfigMap) 959 if err != nil { 960 return nil, err 961 } 962 return wdtConfigMap, nil 963 } 964 965 // updateIstioEnabled sets the domain resource configuration.istio.enabled value based 966 // on the namespace label istio-injection 967 func updateIstioEnabled(labels map[string]string, u *unstructured.Unstructured) error { 968 istioEnabled := false 969 value, ok := labels["istio-injection"] 970 if ok && value == "enabled" { 971 istioEnabled = true 972 } 973 974 return unstructured.SetNestedField(u.Object, istioEnabled, specConfigurationIstioEnabledFields...) 975 } 976 977 func genPassword(passSize int) (string, error) { 978 const passwordChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 979 result := make([]byte, passSize) 980 for i := 0; i < passSize; i++ { 981 num, err := rand.Int(rand.Reader, big.NewInt(int64(len(passwordChars)))) 982 if err != nil { 983 return "", err 984 } 985 result[i] = passwordChars[num.Int64()] 986 } 987 return string(result), nil 988 } 989 990 // addDefaultMonitoringExporter adds monitoringExporter to the WebLogic spec if there is not one present 991 func addDefaultMonitoringExporter(weblogic *unstructured.Unstructured) error { 992 if _, found, _ := unstructured.NestedFieldNoCopy(weblogic.Object, specMonitoringExporterFields...); !found { 993 defaultMonitoringExporter, err := getDefaultMonitoringExporter() 994 if err != nil { 995 return err 996 } 997 err = unstructured.SetNestedField(weblogic.Object, defaultMonitoringExporter, specMonitoringExporterFields...) 998 if err != nil { 999 return err 1000 } 1001 } 1002 return nil 1003 } 1004 1005 func getDefaultMonitoringExporter() (interface{}, error) { 1006 // get ImageSetting 1007 imageSetting := "" 1008 if value := os.Getenv("WEBLOGIC_MONITORING_EXPORTER_IMAGE"); len(value) > 0 { 1009 imageSetting = fmt.Sprintf("\"image\": \"%s\",\n ", value) 1010 } 1011 1012 // Create the buffer and the cluster issuer data struct 1013 templateData := defaultMonitoringExporterTemplateData{ 1014 ImageSetting: imageSetting, 1015 } 1016 1017 // Parse the template string and create the template object 1018 templ, err := template.New("defaultMonitoringExporter").Parse(defaultMonitoringExporterTemplate) 1019 if err != nil { 1020 return nil, err 1021 } 1022 1023 // Execute the template object with the given data 1024 var buff bytes.Buffer 1025 err = templ.Execute(&buff, &templateData) 1026 if err != nil { 1027 return nil, err 1028 } 1029 1030 var monitoringExporter map[string]interface{} 1031 err = json.Unmarshal(buff.Bytes(), &monitoringExporter) 1032 if err != nil { 1033 return nil, err 1034 } 1035 result, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&monitoringExporter) 1036 if err != nil { 1037 return nil, err 1038 } 1039 return result, nil 1040 } 1041 1042 // addLoggingTrait adds the logging trait sidecar to the workload 1043 func (r *Reconciler) addLoggingTrait(ctx context.Context, log vzlog.VerrazzanoLogger, workload *vzapi.VerrazzanoWebLogicWorkload, weblogic *unstructured.Unstructured) error { 1044 loggingTrait, err := vznav.LoggingTraitFromWorkloadLabels(ctx, r.Client, log, workload.GetNamespace(), workload.ObjectMeta) 1045 if err != nil { 1046 return err 1047 } 1048 if loggingTrait == nil { 1049 return nil 1050 } 1051 configMapName := loggingNamePart + "-" + weblogic.GetName() + "-" + strings.ToLower(weblogic.GetKind()) 1052 configMap := &corev1.ConfigMap{} 1053 err = r.Get(ctx, client.ObjectKey{Namespace: weblogic.GetNamespace(), Name: loggingNamePart + "-" + weblogic.GetName() + "-" + strings.ToLower(weblogic.GetKind())}, configMap) 1054 if err != nil && k8serrors.IsNotFound(err) { 1055 data := make(map[string]string) 1056 data["custom.conf"] = loggingTrait.Spec.LoggingConfig 1057 configMap = &corev1.ConfigMap{ 1058 ObjectMeta: metav1.ObjectMeta{ 1059 Name: configMapName, 1060 Namespace: weblogic.GetNamespace(), 1061 Labels: weblogic.GetLabels(), 1062 }, 1063 Data: data, 1064 } 1065 err = controllerutil.SetControllerReference(workload, configMap, r.Scheme) 1066 if err != nil { 1067 return err 1068 } 1069 log.Debugf("Creating logging trait configmap %s:%s", weblogic.GetNamespace(), loggingNamePart+"-"+weblogic.GetName()+"-"+strings.ToLower(weblogic.GetKind())) 1070 err = r.Create(ctx, configMap) 1071 if err != nil { 1072 return err 1073 } 1074 } else if err != nil { 1075 return err 1076 } 1077 log.Debugf("logging trait configmap %s:%s already exist", weblogic.GetNamespace(), loggingNamePart+"-"+weblogic.GetName()+"-"+strings.ToLower(weblogic.GetKind())) 1078 1079 // extract just enough of the WebLogic data into concrete types, so we can merge with 1080 // the logging trait data 1081 var extract containersMountsVolumes 1082 if serverPod, found, _ := unstructured.NestedMap(weblogic.Object, specServerPodFields...); found { 1083 if err = runtime.DefaultUnstructuredConverter.FromUnstructured(serverPod, &extract); err != nil { 1084 return errors.New("unable to extract containers, volumes, and volume mounts from WebLogic spec") 1085 } 1086 } 1087 extracted := &containersMountsVolumes{ 1088 Containers: extract.Containers, 1089 VolumeMounts: extract.VolumeMounts, 1090 Volumes: extract.Volumes, 1091 } 1092 loggingVolumeMount := &corev1.VolumeMount{ 1093 MountPath: loggingMountPath, 1094 Name: configMapName, 1095 SubPath: loggingKey, 1096 ReadOnly: true, 1097 } 1098 vmIndex := -1 1099 for i, vm := range extracted.VolumeMounts { 1100 if reflect.DeepEqual(vm, *loggingVolumeMount) { 1101 vmIndex = i 1102 } 1103 } 1104 if vmIndex != -1 { 1105 extracted.VolumeMounts[vmIndex] = *loggingVolumeMount 1106 } else { 1107 extracted.VolumeMounts = append(extracted.VolumeMounts, *loggingVolumeMount) 1108 } 1109 1110 var image string 1111 if len(loggingTrait.Spec.LoggingImage) != 0 { 1112 image = loggingTrait.Spec.LoggingImage 1113 } else { 1114 image = os.Getenv("DEFAULT_FLUENTD_IMAGE") 1115 } 1116 envFluentd := &corev1.EnvVar{ 1117 Name: "FLUENTD_CONF", 1118 Value: "custom.conf", 1119 } 1120 loggingContainer := &corev1.Container{ 1121 Name: loggingNamePart, 1122 Image: image, 1123 ImagePullPolicy: corev1.PullPolicy(loggingTrait.Spec.ImagePullPolicy), 1124 VolumeMounts: extracted.VolumeMounts, 1125 Env: []corev1.EnvVar{*envFluentd}, 1126 } 1127 cIndex := -1 1128 for i, c := range extracted.Containers { 1129 if c.Name == loggingNamePart { 1130 cIndex = i 1131 } 1132 } 1133 if cIndex != -1 { 1134 extracted.Containers[cIndex] = *loggingContainer 1135 } else { 1136 extracted.Containers = append(extracted.Containers, *loggingContainer) 1137 } 1138 1139 loggingVolume := &corev1.Volume{ 1140 Name: configMapName, 1141 VolumeSource: corev1.VolumeSource{ 1142 ConfigMap: &corev1.ConfigMapVolumeSource{ 1143 LocalObjectReference: corev1.LocalObjectReference{ 1144 Name: configMapName, 1145 }, 1146 DefaultMode: func(mode int32) *int32 { 1147 return &mode 1148 }(defaultMode), 1149 }, 1150 }, 1151 } 1152 vIndex := -1 1153 for i, v := range extracted.Volumes { 1154 if v.Name == configMapName { 1155 vIndex = i 1156 } 1157 } 1158 if vIndex != -1 { 1159 extracted.Volumes[vIndex] = *loggingVolume 1160 } else { 1161 extracted.Volumes = append(extracted.Volumes, *loggingVolume) 1162 } 1163 1164 // convert the containers, volumes, and mounts in extracted to unstructured and set 1165 // the values in the spec 1166 extractedUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&extracted) 1167 if err != nil { 1168 return err 1169 } 1170 1171 err = unstructured.SetNestedSlice(weblogic.Object, extractedUnstructured["containers"].([]interface{}), specServerPodContainersFields...) 1172 if err != nil { 1173 log.Errorf("Failed to set serverPod containers: %v", err) 1174 return err 1175 } 1176 err = unstructured.SetNestedSlice(weblogic.Object, extractedUnstructured["volumes"].([]interface{}), specServerPodVolumesFields...) 1177 if err != nil { 1178 log.Errorf("Failed to set serverPod volumes: %v", err) 1179 return err 1180 } 1181 1182 return nil 1183 } 1184 1185 // If any domainlifecycle start, stop, or restart is requested, then set the appropriate field in the domain resource 1186 // Note that it is valid to a have new restartVersion value along with a lifecycle action change. This 1187 // will not result in additional restarts. 1188 func setDomainLifecycleFields(log vzlog.VerrazzanoLogger, wl *vzapi.VerrazzanoWebLogicWorkload, domain *unstructured.Unstructured) error { 1189 if len(wl.Annotations[vzconst.LifecycleActionAnnotation]) > 0 && wl.Annotations[vzconst.LifecycleActionAnnotation] != wl.Status.LastLifecycleAction { 1190 action := wl.Annotations[vzconst.LifecycleActionAnnotation] 1191 if strings.EqualFold(action, vzconst.LifecycleActionStart) { 1192 return startWebLogicDomain(log, domain) 1193 } 1194 if strings.EqualFold(action, vzconst.LifecycleActionStop) { 1195 return stopWebLogicDomain(log, domain) 1196 } 1197 } 1198 if wl.Annotations != nil && wl.Annotations[vzconst.RestartVersionAnnotation] != wl.Status.LastRestartVersion { 1199 return restartWebLogic(log, domain, wl.Annotations[vzconst.RestartVersionAnnotation]) 1200 } 1201 return nil 1202 } 1203 1204 // Set domain restart version. If it is changed from the previous value, then the WebLogic Operator will restart the domain 1205 func restartWebLogic(log vzlog.VerrazzanoLogger, domain *unstructured.Unstructured, version string) error { 1206 err := unstructured.SetNestedField(domain.Object, version, specRestartVersionFields...) 1207 if err != nil { 1208 log.Errorf("Failed setting restartVersion in domain: %v", err) 1209 return err 1210 } 1211 return nil 1212 } 1213 1214 // Set the serverStartPolicy to stop WebLogic domain, return the current serverStartPolicy 1215 func stopWebLogicDomain(log vzlog.VerrazzanoLogger, domain *unstructured.Unstructured) error { 1216 never := Never 1217 ifneeded := IfNeeded 1218 if isV8(domain) { 1219 never = NeverV8 1220 ifneeded = IfNeededV8 1221 } 1222 1223 // Return if serverStartPolicy is already never 1224 currentServerStartPolicy, _, _ := unstructured.NestedString(domain.Object, specServerStartPolicyFields...) 1225 if currentServerStartPolicy == never { 1226 return nil 1227 } 1228 1229 // Save the last policy so that it can be used when starting the domain 1230 if len(currentServerStartPolicy) == 0 { 1231 currentServerStartPolicy = ifneeded 1232 } 1233 annos, found, err := unstructured.NestedStringMap(domain.Object, metaAnnotationFields...) 1234 if err != nil { 1235 log.Errorf("Failed getting domain annotations: %v", err) 1236 return err 1237 } 1238 if !found { 1239 annos = map[string]string{} 1240 } 1241 annos[lastServerStartPolicyAnnotation] = currentServerStartPolicy 1242 err = unstructured.SetNestedStringMap(domain.Object, annos, metaAnnotationFields...) 1243 if err != nil { 1244 log.Errorf("Failed to set annotations in domain: %v", err) 1245 return err 1246 } 1247 1248 // set serverStartPolicy to "NEVER" to shut down the domain 1249 err = unstructured.SetNestedField(domain.Object, never, specServerStartPolicyFields...) 1250 if err != nil { 1251 log.Errorf("Failed to set serverStartPolicy in domain: %v", err) 1252 return err 1253 } 1254 return nil 1255 } 1256 1257 // Set the serverStartPolicy to start the WebLogic domain 1258 func startWebLogicDomain(log vzlog.VerrazzanoLogger, domain *unstructured.Unstructured) error { 1259 var startPolicy = IfNeeded 1260 if isV8(domain) { 1261 startPolicy = IfNeededV8 1262 } 1263 1264 // Get the last serverStartPolicy if it exists 1265 annos, found, err := unstructured.NestedStringMap(domain.Object, metaAnnotationFields...) 1266 if err != nil { 1267 log.Errorf("Failed getting domain annotations: %v", err) 1268 return err 1269 } 1270 if found { 1271 oldPolicy := annos[lastServerStartPolicyAnnotation] 1272 if len(oldPolicy) > 0 { 1273 startPolicy = oldPolicy 1274 } 1275 } 1276 err = unstructured.SetNestedField(domain.Object, startPolicy, specServerStartPolicyFields...) 1277 if err != nil { 1278 return err 1279 } 1280 return nil 1281 } 1282 1283 // getWDTConfigMapName builds a WDT config map name given a domain name 1284 func getWDTConfigMapName(domainName string) string { 1285 return fmt.Sprintf("%s%s", domainName, WDTConfigMapNameSuffix) 1286 }