github.com/leg100/ots@v0.0.7-0.20210919080622-034055ced4bd/apply.go (about) 1 package ots 2 3 import ( 4 "fmt" 5 6 tfe "github.com/leg100/go-tfe" 7 "gorm.io/gorm" 8 ) 9 10 type ApplyService interface { 11 Get(id string) (*Apply, error) 12 } 13 14 type Apply struct { 15 ID string 16 17 gorm.Model 18 19 ResourceAdditions int 20 ResourceChanges int 21 ResourceDestructions int 22 Status tfe.ApplyStatus 23 StatusTimestamps *tfe.ApplyStatusTimestamps 24 25 // Logs is the blob ID for the log output from a terraform apply 26 LogsBlobID string 27 } 28 29 // ApplyFinishOptions represents the options for finishing an apply. 30 type ApplyFinishOptions struct { 31 // Type is a public field utilized by JSON:API to set the resource type via 32 // the field tag. It is not a user-defined value and does not need to be 33 // set. https://jsonapi.org/format/#crud-creating 34 Type string `jsonapi:"primary,applies"` 35 36 ResourceAdditions int `jsonapi:"attr,resource-additions"` 37 ResourceChanges int `jsonapi:"attr,resource-changes"` 38 ResourceDestructions int `jsonapi:"attr,resource-destructions"` 39 } 40 41 func newApply() *Apply { 42 return &Apply{ 43 ID: GenerateID("apply"), 44 StatusTimestamps: &tfe.ApplyStatusTimestamps{}, 45 LogsBlobID: NewBlobID(), 46 } 47 } 48 49 func (a *Apply) GetLogsBlobID() string { 50 return a.LogsBlobID 51 } 52 53 func (a *Apply) Do(run *Run, exe *Executor) error { 54 if err := exe.RunFunc(run.downloadPlanFile); err != nil { 55 return err 56 } 57 58 if err := exe.RunCLI("sh", "-c", fmt.Sprintf("terraform apply -no-color %s | tee %s", PlanFilename, ApplyOutputFilename)); err != nil { 59 return err 60 } 61 62 if err := exe.RunFunc(run.uploadState); err != nil { 63 return err 64 } 65 66 return nil 67 } 68 69 // UpdateResources parses the output from terraform apply to determine the 70 // number and type of resource changes applied and updates the apply object 71 // accordingly. 72 func (a *Apply) UpdateResources(bs BlobStore) error { 73 logs, err := bs.Get(a.LogsBlobID) 74 if err != nil { 75 return err 76 } 77 78 resources, err := parseApplyOutput(string(logs)) 79 if err != nil { 80 return err 81 } 82 83 a.ResourceAdditions = resources.adds 84 a.ResourceChanges = resources.changes 85 a.ResourceDestructions = resources.deletions 86 87 return nil 88 } 89 90 func (a *Apply) UpdateStatus(status tfe.ApplyStatus) { 91 a.Status = status 92 a.setTimestamp(status) 93 } 94 95 func (a *Apply) setTimestamp(status tfe.ApplyStatus) { 96 switch status { 97 case tfe.ApplyCanceled: 98 a.StatusTimestamps.CanceledAt = TimeNow() 99 case tfe.ApplyErrored: 100 a.StatusTimestamps.ErroredAt = TimeNow() 101 case tfe.ApplyFinished: 102 a.StatusTimestamps.FinishedAt = TimeNow() 103 case tfe.ApplyQueued: 104 a.StatusTimestamps.QueuedAt = TimeNow() 105 case tfe.ApplyRunning: 106 a.StatusTimestamps.StartedAt = TimeNow() 107 } 108 }