github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/webhooks/metrics_trait_defaulter.go (about)

     1  // Copyright (c) 2020, 2022, 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 webhooks
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  
    11  	oamv1 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
    12  	"github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1"
    13  	"github.com/verrazzano/verrazzano/application-operator/controllers/metricstrait"
    14  	vznav "github.com/verrazzano/verrazzano/application-operator/controllers/navigation"
    15  	"github.com/verrazzano/verrazzano/pkg/log/vzlog"
    16  	"go.uber.org/zap"
    17  	"k8s.io/apimachinery/pkg/runtime"
    18  	"k8s.io/apimachinery/pkg/types"
    19  	"sigs.k8s.io/controller-runtime/pkg/client"
    20  )
    21  
    22  // MetricsTraitDefaulter supplies default MetricsTrait
    23  type MetricsTraitDefaulter struct {
    24  	Client client.Client
    25  }
    26  
    27  var (
    28  	apiVersion          = v1alpha1.SchemeGroupVersion.String()
    29  	defaultMetricsTrait = []byte(fmt.Sprintf(traitTemp, apiVersion, v1alpha1.MetricsTraitKind))
    30  )
    31  
    32  const traitTemp = `{
    33  	"apiVersion": "%s",
    34  	"kind": "%s"
    35   }`
    36  
    37  func (m *MetricsTraitDefaulter) findMetricsTrait(component *oamv1.ApplicationConfigurationComponent) bool {
    38  	for _, trait := range component.Traits {
    39  		var rawTrait map[string]interface{}
    40  		json.Unmarshal(trait.Trait.Raw, &rawTrait)
    41  		if rawTrait["apiVersion"] == apiVersion && rawTrait["kind"] == v1alpha1.MetricsTraitKind {
    42  			return true
    43  		}
    44  	}
    45  	return false
    46  }
    47  
    48  func (m *MetricsTraitDefaulter) addDefaultTrait(component *oamv1.ApplicationConfigurationComponent) {
    49  	rawTrait := runtime.RawExtension{Raw: defaultMetricsTrait}
    50  	componentTrait := oamv1.ComponentTrait{Trait: rawTrait}
    51  	component.Traits = append(component.Traits, componentTrait)
    52  }
    53  
    54  // Default method adds default MetricsTrait to ApplicationConfiguration
    55  func (m *MetricsTraitDefaulter) Default(appConfig *oamv1.ApplicationConfiguration, dryRun bool, log *zap.SugaredLogger) error {
    56  	for i := range appConfig.Spec.Components {
    57  		appConfigComponent := &appConfig.Spec.Components[i]
    58  		if m.shouldDefaultTraitBeAdded(appConfig, appConfigComponent, log) {
    59  			m.addDefaultTrait(appConfigComponent)
    60  		}
    61  	}
    62  	return nil
    63  }
    64  
    65  // Cleanup is not used by the metrics trait defaulter
    66  func (m *MetricsTraitDefaulter) Cleanup(appConfig *oamv1.ApplicationConfiguration, dryRun bool, log *zap.SugaredLogger) error {
    67  	return nil
    68  }
    69  
    70  // shouldDefaultTraitBeAdded method verifies whether a trait should be applied to the component
    71  func (m *MetricsTraitDefaulter) shouldDefaultTraitBeAdded(appConfig *oamv1.ApplicationConfiguration, appConfigComponent *oamv1.ApplicationConfigurationComponent, log *zap.SugaredLogger) bool {
    72  	found := m.findMetricsTrait(appConfigComponent)
    73  	if found {
    74  		return false
    75  	}
    76  
    77  	var component oamv1.Component
    78  	err := m.Client.Get(context.TODO(), types.NamespacedName{Namespace: appConfig.GetNamespace(), Name: appConfigComponent.ComponentName}, &component)
    79  	if err != nil {
    80  		log.Debugf("Unable to get component info for component: %s of application configuration: %s/%s, error: %v, adding default metric trait", appConfigComponent.ComponentName, appConfig.GetNamespace(), appConfig.GetName(), err)
    81  		return true
    82  
    83  	}
    84  
    85  	componentUnstructured, err := vznav.ConvertRawExtensionToUnstructured(&component.Spec.Workload)
    86  	if err != nil || componentUnstructured == nil {
    87  		log.Debugf("Unable to convert workload spec for component: %s of application configuration: %s/%s, error: %v, adding default metric trait", appConfigComponent.ComponentName, appConfig.GetNamespace(), appConfig.GetName(), err)
    88  		return true
    89  	}
    90  
    91  	if componentUnstructured.GetNamespace() == "" {
    92  		componentUnstructured.SetNamespace(component.GetNamespace())
    93  	}
    94  
    95  	workload, err := vznav.FetchWorkloadResource(context.TODO(), m.Client, vzlog.DefaultLogger(), componentUnstructured)
    96  	if err != nil || workload == nil {
    97  		log.Debugf("Unable to get workload resource for component: %s of application configuration: %s/%s, error: %v, adding default metric trait", appConfigComponent.ComponentName, appConfig.GetNamespace(), appConfig.GetName(), err)
    98  		return true
    99  	}
   100  
   101  	apiVerKind, err := vznav.GetAPIVersionKindOfUnstructured(workload)
   102  	if err != nil || apiVerKind == "" {
   103  		log.Debugf("Unable to determine api version and kind for workload of component: %s of application configuration: %s/%s, error: %v, adding default metric trait", appConfigComponent.ComponentName, appConfig.GetNamespace(), appConfig.GetName(), err)
   104  		return true
   105  	}
   106  
   107  	workloadType := metricstrait.GetSupportedWorkloadType(apiVerKind)
   108  	if workloadType != "" {
   109  		log.Infof("Adding default metrics trait for supported component: %s of type %s of application configuration: %s/%s", appConfigComponent.ComponentName, workloadType, appConfig.GetNamespace(), appConfig.GetName())
   110  		return true
   111  	}
   112  
   113  	return false
   114  }