github.com/leg100/ots@v0.0.7-0.20210919080622-034055ced4bd/workspace.go (about) 1 package ots 2 3 import ( 4 "errors" 5 "time" 6 7 tfe "github.com/leg100/go-tfe" 8 "gorm.io/gorm" 9 ) 10 11 const ( 12 DefaultAllowDestroyPlan = true 13 DefaultFileTriggersEnabled = true 14 DefaultTerraformVersion = "0.15.4" 15 DefaultExecutionMode = "remote" 16 ) 17 18 var ( 19 ErrWorkspaceAlreadyLocked = errors.New("workspace already locked") 20 ErrWorkspaceAlreadyUnlocked = errors.New("workspace already unlocked") 21 ErrInvalidWorkspaceSpecifier = errors.New("invalid workspace specifier options") 22 ) 23 24 // Workspace represents a Terraform Enterprise workspace. 25 type Workspace struct { 26 ID string 27 28 gorm.Model 29 30 AllowDestroyPlan bool 31 AutoApply bool 32 CanQueueDestroyPlan bool 33 Description string 34 Environment string 35 ExecutionMode string 36 FileTriggersEnabled bool 37 GlobalRemoteState bool 38 Locked bool 39 MigrationEnvironment string 40 Name string 41 Permissions *tfe.WorkspacePermissions 42 QueueAllRuns bool 43 SpeculativeEnabled bool 44 SourceName string 45 SourceURL string 46 StructuredRunOutputEnabled bool 47 TerraformVersion string 48 VCSRepo *tfe.VCSRepo 49 WorkingDirectory string 50 ResourceCount int 51 ApplyDurationAverage time.Duration 52 PlanDurationAverage time.Duration 53 PolicyCheckFailures int 54 RunFailures int 55 RunsCount int 56 57 TriggerPrefixes []string 58 59 // Relations AgentPool *tfe.AgentPool CurrentRun *Run 60 61 // Workspace belongs to an organization 62 Organization *Organization 63 64 //SSHKey *tfe.SSHKey 65 } 66 67 // WorkspaceList represents a list of Workspaces. 68 type WorkspaceList struct { 69 *tfe.Pagination 70 Items []*Workspace 71 } 72 73 type WorkspaceService interface { 74 Create(org string, opts *tfe.WorkspaceCreateOptions) (*Workspace, error) 75 Get(spec WorkspaceSpecifier) (*Workspace, error) 76 List(opts WorkspaceListOptions) (*WorkspaceList, error) 77 Update(spec WorkspaceSpecifier, opts *tfe.WorkspaceUpdateOptions) (*Workspace, error) 78 Lock(id string, opts tfe.WorkspaceLockOptions) (*Workspace, error) 79 Unlock(id string) (*Workspace, error) 80 Delete(spec WorkspaceSpecifier) error 81 } 82 83 type WorkspaceStore interface { 84 Create(ws *Workspace) (*Workspace, error) 85 Get(spec WorkspaceSpecifier) (*Workspace, error) 86 List(opts WorkspaceListOptions) (*WorkspaceList, error) 87 Update(spec WorkspaceSpecifier, fn func(*Workspace) error) (*Workspace, error) 88 Delete(spec WorkspaceSpecifier) error 89 } 90 91 // WorkspaceSpecifier is used for identifying an individual workspace. Either ID 92 // *or* both Name and OrganizationName must be specfiied. 93 type WorkspaceSpecifier struct { 94 // Specify workspace using its ID 95 ID *string 96 97 // Specify workspace using its name and organization 98 Name *string 99 OrganizationName *string 100 } 101 102 // WorkspaceListOptions are options for paginating and filtering a list of 103 // Workspaces 104 type WorkspaceListOptions struct { 105 // Pagination 106 tfe.ListOptions 107 108 // Optionally filter workspaces with name matching prefix 109 Prefix *string `schema:"search[name],omitempty"` 110 111 // OrganizationName filters workspaces by organization name 112 OrganizationName *string 113 114 // A list of relations to include. See available resources https://www.terraform.io/docs/cloud/api/workspaces.html#available-related-resources 115 Include *string `schema:"include"` 116 } 117 118 func NewWorkspace(opts *tfe.WorkspaceCreateOptions, org *Organization) *Workspace { 119 ws := Workspace{ 120 ID: GenerateID("ws"), 121 Name: *opts.Name, 122 AllowDestroyPlan: DefaultAllowDestroyPlan, 123 ExecutionMode: "local", // Default until remote ops is officially supported 124 FileTriggersEnabled: DefaultFileTriggersEnabled, 125 GlobalRemoteState: true, // Only global remote state is supported 126 TerraformVersion: DefaultTerraformVersion, 127 Permissions: &tfe.WorkspacePermissions{ 128 CanDestroy: true, 129 CanForceUnlock: true, 130 CanLock: true, 131 CanUnlock: true, 132 CanQueueApply: true, 133 CanQueueDestroy: true, 134 CanQueueRun: true, 135 CanReadSettings: true, 136 CanUpdate: true, 137 CanUpdateVariable: true, 138 }, 139 SpeculativeEnabled: true, 140 Organization: org, 141 } 142 143 // TODO: ExecutionMode and Operations are mututally exclusive options, this 144 // should be enforced. 145 if opts.ExecutionMode != nil { 146 ws.ExecutionMode = *opts.ExecutionMode 147 } 148 // Operations is deprecated in favour of ExecutionMode. 149 if opts.Operations != nil { 150 if *opts.Operations { 151 ws.ExecutionMode = "remote" 152 } else { 153 ws.ExecutionMode = "local" 154 } 155 } 156 157 if opts.AllowDestroyPlan != nil { 158 ws.AllowDestroyPlan = *opts.AllowDestroyPlan 159 } 160 if opts.AutoApply != nil { 161 ws.AutoApply = *opts.AutoApply 162 } 163 if opts.Description != nil { 164 ws.Description = *opts.Description 165 } 166 if opts.FileTriggersEnabled != nil { 167 ws.FileTriggersEnabled = *opts.FileTriggersEnabled 168 } 169 if opts.QueueAllRuns != nil { 170 ws.QueueAllRuns = *opts.QueueAllRuns 171 } 172 if opts.SourceName != nil { 173 ws.SourceName = *opts.SourceName 174 } 175 if opts.SourceURL != nil { 176 ws.SourceURL = *opts.SourceURL 177 } 178 if opts.SpeculativeEnabled != nil { 179 ws.SpeculativeEnabled = *opts.SpeculativeEnabled 180 } 181 if opts.StructuredRunOutputEnabled != nil { 182 ws.StructuredRunOutputEnabled = *opts.StructuredRunOutputEnabled 183 } 184 if opts.TerraformVersion != nil { 185 ws.TerraformVersion = *opts.TerraformVersion 186 } 187 if opts.TriggerPrefixes != nil { 188 ws.TriggerPrefixes = opts.TriggerPrefixes 189 } 190 if opts.WorkingDirectory != nil { 191 ws.WorkingDirectory = *opts.WorkingDirectory 192 } 193 194 return &ws 195 } 196 197 func UpdateWorkspace(ws *Workspace, opts *tfe.WorkspaceUpdateOptions) (*Workspace, error) { 198 if opts.Name != nil { 199 ws.Name = *opts.Name 200 } 201 202 if opts.AllowDestroyPlan != nil { 203 ws.AllowDestroyPlan = *opts.AllowDestroyPlan 204 } 205 if opts.AutoApply != nil { 206 ws.AutoApply = *opts.AutoApply 207 } 208 if opts.Description != nil { 209 ws.Description = *opts.Description 210 } 211 if opts.ExecutionMode != nil { 212 ws.ExecutionMode = *opts.ExecutionMode 213 } 214 if opts.FileTriggersEnabled != nil { 215 ws.FileTriggersEnabled = *opts.FileTriggersEnabled 216 } 217 if opts.QueueAllRuns != nil { 218 ws.QueueAllRuns = *opts.QueueAllRuns 219 } 220 if opts.SpeculativeEnabled != nil { 221 ws.SpeculativeEnabled = *opts.SpeculativeEnabled 222 } 223 if opts.StructuredRunOutputEnabled != nil { 224 ws.StructuredRunOutputEnabled = *opts.StructuredRunOutputEnabled 225 } 226 if opts.TerraformVersion != nil { 227 ws.TerraformVersion = *opts.TerraformVersion 228 } 229 if opts.TriggerPrefixes != nil { 230 ws.TriggerPrefixes = opts.TriggerPrefixes 231 } 232 if opts.WorkingDirectory != nil { 233 ws.WorkingDirectory = *opts.WorkingDirectory 234 } 235 236 return ws, nil 237 } 238 239 func (ws *Workspace) Actions() *tfe.WorkspaceActions { 240 return &tfe.WorkspaceActions{ 241 IsDestroyable: false, 242 } 243 } 244 245 func (ws *Workspace) ToggleLock(lock bool) error { 246 if lock && ws.Locked { 247 return ErrWorkspaceAlreadyLocked 248 } 249 if !lock && !ws.Locked { 250 return ErrWorkspaceAlreadyUnlocked 251 } 252 253 ws.Locked = lock 254 255 return nil 256 }