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  }