github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/cd-service/pkg/event/event.go (about)

     1  /*This file is part of kuberpult.
     2  
     3  Kuberpult is free software: you can redistribute it and/or modify
     4  it under the terms of the Expat(MIT) License as published by
     5  the Free Software Foundation.
     6  
     7  Kuberpult is distributed in the hope that it will be useful,
     8  but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  MIT License for more details.
    11  
    12  You should have received a copy of the MIT License
    13  along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
    14  
    15  Copyright 2023 freiheit.com*/
    16  
    17  package event
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"io/fs"
    23  	"slices"
    24  
    25  	"github.com/freiheit-com/kuberpult/pkg/api/v1"
    26  	"github.com/freiheit-com/kuberpult/pkg/uuid"
    27  	"github.com/go-git/go-billy/v5"
    28  	"github.com/onokonem/sillyQueueServer/timeuuid"
    29  )
    30  
    31  type eventType struct {
    32  	EventType string `fs:"eventType"`
    33  }
    34  
    35  // NewRelease is an event that denotes that a commit has been released
    36  // for the first time.
    37  type NewRelease struct {
    38  	Environments map[string]struct{} `fs:"environments"`
    39  }
    40  
    41  func (_ *NewRelease) eventType() string {
    42  	return "new-release"
    43  }
    44  
    45  func (ev *NewRelease) toProto(trg *api.Event) {
    46  	envs := make([]string, 0, len(ev.Environments))
    47  	for env := range ev.Environments {
    48  		envs = append(envs, env)
    49  	}
    50  	slices.Sort(envs)
    51  	trg.EventType = &api.Event_CreateReleaseEvent{
    52  		CreateReleaseEvent: &api.CreateReleaseEvent{
    53  			EnvironmentNames: envs,
    54  		},
    55  	}
    56  }
    57  
    58  // Deployment is an event that denotes that an application of a commit
    59  // has been released to an environment.
    60  type Deployment struct {
    61  	Application                 string  `fs:"application"`
    62  	Environment                 string  `fs:"environment"`
    63  	SourceTrainEnvironmentGroup *string `fs:"source_train_environment_group"`
    64  	SourceTrainUpstream         *string `fs:"source_train_upstream"`
    65  }
    66  
    67  func (_ *Deployment) eventType() string {
    68  	return "deployment"
    69  }
    70  
    71  func (ev *Deployment) toProto(trg *api.Event) {
    72  	var releaseTrainSource *api.DeploymentEvent_ReleaseTrainSource
    73  	if ev.SourceTrainEnvironmentGroup != nil {
    74  		releaseTrainSource = &api.DeploymentEvent_ReleaseTrainSource{
    75  			UpstreamEnvironment:    "",
    76  			TargetEnvironmentGroup: ev.SourceTrainEnvironmentGroup,
    77  		}
    78  	}
    79  	if ev.SourceTrainUpstream != nil {
    80  		if releaseTrainSource == nil {
    81  			releaseTrainSource = new(api.DeploymentEvent_ReleaseTrainSource)
    82  		}
    83  		releaseTrainSource.UpstreamEnvironment = *ev.SourceTrainUpstream
    84  	}
    85  	trg.EventType = &api.Event_DeploymentEvent{
    86  		DeploymentEvent: &api.DeploymentEvent{
    87  			Application:        ev.Application,
    88  			TargetEnvironment:  ev.Environment,
    89  			ReleaseTrainSource: releaseTrainSource,
    90  		},
    91  	}
    92  }
    93  
    94  type LockPreventedDeployment struct {
    95  	Application string `fs:"application"`
    96  	Environment string `fs:"environment"`
    97  	LockMessage string `fs:"lock_message"`
    98  	LockType    string `fs:"lock_type"`
    99  }
   100  
   101  func (_ *LockPreventedDeployment) eventType() string {
   102  	return "lock-prevented-deployment"
   103  }
   104  
   105  func (ev *LockPreventedDeployment) toProto(trg *api.Event) {
   106  	var lockType api.LockPreventedDeploymentEvent_LockType
   107  	switch ev.LockType {
   108  	case "application":
   109  		lockType = api.LockPreventedDeploymentEvent_LOCK_TYPE_APP
   110  	case "environment":
   111  		lockType = api.LockPreventedDeploymentEvent_LOCK_TYPE_ENV
   112  	default:
   113  		lockType = api.LockPreventedDeploymentEvent_LOCK_TYPE_UNKNOWN
   114  	}
   115  	trg.EventType = &api.Event_LockPreventedDeploymentEvent{
   116  		LockPreventedDeploymentEvent: &api.LockPreventedDeploymentEvent{
   117  			Application: ev.Application,
   118  			Environment: ev.Environment,
   119  			LockMessage: ev.LockMessage,
   120  			LockType:    lockType,
   121  		},
   122  	}
   123  }
   124  
   125  type ReplacedBy struct {
   126  	Application       string `fs:"application"`
   127  	Environment       string `fs:"environment"`
   128  	CommitIDtoReplace string `fs:"commit"`
   129  }
   130  
   131  func (_ *ReplacedBy) eventType() string {
   132  	return "replaced-by"
   133  }
   134  
   135  func (ev *ReplacedBy) toProto(trg *api.Event) {
   136  	trg.EventType = &api.Event_ReplacedByEvent{
   137  		ReplacedByEvent: &api.ReplacedByEvent{
   138  			Application:        ev.Application,
   139  			Environment:        ev.Environment,
   140  			ReplacedByCommitId: ev.CommitIDtoReplace,
   141  		},
   142  	}
   143  }
   144  
   145  // Event is a commit-releated event
   146  type Event interface {
   147  	eventType() string
   148  	toProto(*api.Event)
   149  }
   150  
   151  // Read an event from a filesystem.
   152  func Read(fs billy.Filesystem, eventDir string) (Event, error) {
   153  	var tp eventType
   154  	if err := read(fs, eventDir, &tp); err != nil {
   155  		return nil, err
   156  	}
   157  	var result Event
   158  	switch tp.EventType {
   159  	case "new-release":
   160  		//exhaustruct:ignore
   161  		result = &NewRelease{}
   162  	case "deployment":
   163  		//exhaustruct:ignore
   164  		result = &Deployment{}
   165  	case "lock-prevented-deployment":
   166  		//exhaustruct:ignore
   167  		result = &LockPreventedDeployment{}
   168  	case "replaced-by":
   169  		//exhaustruct:ignore
   170  		result = &ReplacedBy{}
   171  	default:
   172  		return nil, fmt.Errorf("unknown event type: %q", tp.EventType)
   173  	}
   174  	if err := read(fs, eventDir, result); err != nil {
   175  		return nil, err
   176  	}
   177  	return result, nil
   178  }
   179  
   180  // Write an event to a filesystem
   181  func Write(filesystem billy.Filesystem, eventDir string, event Event) error {
   182  	_, err := filesystem.Stat(eventDir)
   183  	if !errors.Is(err, fs.ErrNotExist) {
   184  		return fmt.Errorf("event file already exists: %w", err)
   185  	}
   186  	if err := write(filesystem, eventDir, eventType{
   187  		EventType: event.eventType(),
   188  	}); err != nil {
   189  		return err
   190  	}
   191  	return write(filesystem, eventDir, event)
   192  }
   193  
   194  // Convert an event to its protobuf representation
   195  func ToProto(eventID timeuuid.UUID, ev Event) *api.Event {
   196  	result := &api.Event{
   197  		EventType: nil,
   198  		CreatedAt: uuid.GetTime(&eventID),
   199  		Uuid:      eventID.String(),
   200  	}
   201  	ev.toProto(result)
   202  	return result
   203  }