github.com/verrazzano/verrazzano@v1.7.0/platform-operator/experimental/event/event.go (about)

     1  // Copyright (c) 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 event
     5  
     6  import (
     7  	"context"
     8  	"encoding/base64"
     9  	"fmt"
    10  	moduleapi "github.com/verrazzano/verrazzano-modules/module-operator/apis/platform/v1alpha1"
    11  	"github.com/verrazzano/verrazzano-modules/pkg/controller/result"
    12  	"github.com/verrazzano/verrazzano-modules/pkg/vzlog"
    13  	"github.com/verrazzano/verrazzano/platform-operator/constants"
    14  	corev1 "k8s.io/api/core/v1"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/types"
    17  	"sigs.k8s.io/controller-runtime/pkg/client"
    18  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    19  	"sigs.k8s.io/yaml"
    20  )
    21  
    22  // EventType is the type of event
    23  type EventType string
    24  
    25  // EventType constants
    26  const (
    27  	// IntegrateSingleRequestEvent is a request to integrate a single module
    28  	IntegrateSingleRequestEvent EventType = "integrate-single"
    29  
    30  	// IntegrateCascadeRequestEvent is an event request to integrate the other modules except the one
    31  	// in the event payload (since it will already have been integrated)
    32  	IntegrateCascadeRequestEvent EventType = "integrate-cascade"
    33  )
    34  
    35  // DataKey is a configmap data key
    36  type DataKey string
    37  
    38  // DataKey constants
    39  const (
    40  	EventDataKey DataKey = "event"
    41  )
    42  
    43  // Action is the lifecycle action
    44  type Action string
    45  
    46  // Action constants
    47  const (
    48  	Installed Action = "installed"
    49  	Upgraded  Action = "upgraded"
    50  	Deleted   Action = "deleted"
    51  )
    52  
    53  // ModuleIntegrationEvent contains the event data
    54  type ModuleIntegrationEvent struct {
    55  	EventType
    56  	Action
    57  
    58  	// Cascade true indicates that the single-module integration controller should potentially create an
    59  	// event to integrate other modules
    60  	Cascade         bool
    61  	ResourceNSN     types.NamespacedName
    62  	ModuleName      string
    63  	ModuleVersion   string
    64  	TargetNamespace string
    65  }
    66  
    67  // NewModuleIntegrationEvent creates a ModuleIntegrationEvent event struct for a module
    68  func NewModuleIntegrationEvent(module *moduleapi.Module, action Action, eventType EventType, cascade bool) *ModuleIntegrationEvent {
    69  	return &ModuleIntegrationEvent{
    70  		Cascade:         cascade,
    71  		EventType:       eventType,
    72  		Action:          action,
    73  		ResourceNSN:     types.NamespacedName{Namespace: module.Namespace, Name: module.Name},
    74  		ModuleName:      module.Spec.ModuleName,
    75  		ModuleVersion:   module.Spec.Version,
    76  		TargetNamespace: module.Spec.TargetNamespace,
    77  	}
    78  }
    79  
    80  // CreateModuleIntegrationEvent creates a ModuleIntegrationEvent event for a module
    81  func CreateModuleIntegrationEvent(log vzlog.VerrazzanoLogger, cli client.Client, module *moduleapi.Module, action Action) result.Result {
    82  	return createEvent(log, cli, NewModuleIntegrationEvent(module, action, IntegrateSingleRequestEvent, true))
    83  }
    84  
    85  // CreateModuleIntegrationCascadeEvent creates a ModuleIntegrationEvent event for a module to integrate other modules
    86  func CreateModuleIntegrationCascadeEvent(log vzlog.VerrazzanoLogger, cli client.Client, sourceEvent *ModuleIntegrationEvent) result.Result {
    87  	// Use the fields from the input event to create a new event of a different type
    88  	ev := *sourceEvent
    89  	ev.EventType = IntegrateCascadeRequestEvent
    90  	ev.Cascade = false
    91  	return createEvent(log, cli, &ev)
    92  }
    93  
    94  // CreateNonCascadingModuleIntegrationEvent creates a ModuleIntegrationEvent event for a module with no cascading
    95  func CreateNonCascadingModuleIntegrationEvent(log vzlog.VerrazzanoLogger, cli client.Client, module *moduleapi.Module, action Action) result.Result {
    96  	return createEvent(log, cli, NewModuleIntegrationEvent(module, action, IntegrateSingleRequestEvent, false))
    97  }
    98  
    99  // createEvent creates a lifecycle event
   100  func createEvent(log vzlog.VerrazzanoLogger, cli client.Client, ev *ModuleIntegrationEvent) result.Result {
   101  	y, err := yaml.Marshal(ev)
   102  	if err != nil {
   103  		log.ErrorfThrottled("Failed to unmarshal event configmap for module %s: %v", ev.ModuleName, err)
   104  		result.NewResultShortRequeueDelayWithError(err)
   105  	}
   106  	// convert to base64
   107  	encoded := base64.StdEncoding.EncodeToString(y)
   108  
   109  	cm := &corev1.ConfigMap{
   110  		ObjectMeta: metav1.ObjectMeta{
   111  			Name:      getEventResourceName(ev.ResourceNSN.Name, string(ev.Action), string(ev.EventType)),
   112  			Namespace: ev.ResourceNSN.Namespace,
   113  		},
   114  	}
   115  	_, err = controllerutil.CreateOrUpdate(context.TODO(), cli, cm, func() error {
   116  		if cm.Labels == nil {
   117  			cm.Labels = make(map[string]string)
   118  		}
   119  		// Always replace existing event data for this module-action
   120  		cm.Labels[constants.VerrazzanoModuleEventLabel] = string(ev.EventType)
   121  		cm.Data = make(map[string]string)
   122  		cm.Data[string(EventDataKey)] = encoded
   123  		return nil
   124  	})
   125  	if err != nil {
   126  		log.ErrorfThrottled("Failed to create or update event configmap for module %s: %v", ev.ModuleName, err)
   127  		return result.NewResultShortRequeueDelayWithError(err)
   128  	}
   129  
   130  	return result.NewResult()
   131  }
   132  
   133  // ConfigMapToModuleIntegrationEvent converts an event configmap to a ModuleIntegrationEvent
   134  func ConfigMapToModuleIntegrationEvent(log vzlog.VerrazzanoLogger, cm *corev1.ConfigMap) (*ModuleIntegrationEvent, error) {
   135  	decoded, err := base64.StdEncoding.DecodeString(cm.Data[string(EventDataKey)])
   136  	if err != nil {
   137  		return nil, err
   138  	}
   139  	ev := ModuleIntegrationEvent{}
   140  	if err := yaml.Unmarshal(decoded, &ev); err != nil {
   141  		return nil, err
   142  	}
   143  	return &ev, nil
   144  }
   145  
   146  func getEventResourceName(name string, action string, eventType string) string {
   147  	return fmt.Sprintf("event-%s-%s-%s", name, action, eventType)
   148  }