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 }