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  }