terraform/vendor/github.com/hashicorp/go-tfe/configuration_version.go

209 lines
6.6 KiB
Go

package tfe
import (
"bytes"
"context"
"errors"
"fmt"
"net/url"
"os"
"time"
slug "github.com/hashicorp/go-slug"
)
// Compile-time proof of interface implementation.
var _ ConfigurationVersions = (*configurationVersions)(nil)
// ConfigurationVersions describes all the configuration version related
// methods that the Terraform Enterprise API supports.
//
// TFE API docs:
// https://www.terraform.io/docs/enterprise/api/configuration-versions.html
type ConfigurationVersions interface {
// List returns all configuration versions of a workspace.
List(ctx context.Context, workspaceID string, options ConfigurationVersionListOptions) (*ConfigurationVersionList, error)
// Create is used to create a new configuration version. The created
// configuration version will be usable once data is uploaded to it.
Create(ctx context.Context, workspaceID string, options ConfigurationVersionCreateOptions) (*ConfigurationVersion, error)
// Read a configuration version by its ID.
Read(ctx context.Context, cvID string) (*ConfigurationVersion, error)
// Upload packages and uploads Terraform configuration files. It requires
// the upload URL from a configuration version and the full path to the
// configuration files on disk.
Upload(ctx context.Context, url string, path string) error
}
// configurationVersions implements ConfigurationVersions.
type configurationVersions struct {
client *Client
}
// ConfigurationStatus represents a configuration version status.
type ConfigurationStatus string
//List all available configuration version statuses.
const (
ConfigurationErrored ConfigurationStatus = "errored"
ConfigurationPending ConfigurationStatus = "pending"
ConfigurationUploaded ConfigurationStatus = "uploaded"
)
// ConfigurationSource represents a source of a configuration version.
type ConfigurationSource string
// List all available configuration version sources.
const (
ConfigurationSourceAPI ConfigurationSource = "tfe-api"
ConfigurationSourceBitbucket ConfigurationSource = "bitbucket"
ConfigurationSourceGithub ConfigurationSource = "github"
ConfigurationSourceGitlab ConfigurationSource = "gitlab"
ConfigurationSourceTerraform ConfigurationSource = "terraform"
)
// ConfigurationVersionList represents a list of configuration versions.
type ConfigurationVersionList struct {
*Pagination
Items []*ConfigurationVersion
}
// ConfigurationVersion is a representation of an uploaded or ingressed
// Terraform configuration in TFE. A workspace must have at least one
// configuration version before any runs may be queued on it.
type ConfigurationVersion struct {
ID string `jsonapi:"primary,configuration-versions"`
AutoQueueRuns bool `jsonapi:"attr,auto-queue-runs"`
Error string `jsonapi:"attr,error"`
ErrorMessage string `jsonapi:"attr,error-message"`
Source ConfigurationSource `jsonapi:"attr,source"`
Speculative bool `jsonapi:"attr,speculative "`
Status ConfigurationStatus `jsonapi:"attr,status"`
StatusTimestamps *CVStatusTimestamps `jsonapi:"attr,status-timestamps"`
UploadURL string `jsonapi:"attr,upload-url"`
}
// CVStatusTimestamps holds the timestamps for individual configuration version
// statuses.
type CVStatusTimestamps struct {
FinishedAt time.Time `json:"finished-at"`
QueuedAt time.Time `json:"queued-at"`
StartedAt time.Time `json:"started-at"`
}
// ConfigurationVersionListOptions represents the options for listing
// configuration versions.
type ConfigurationVersionListOptions struct {
ListOptions
}
// List returns all configuration versions of a workspace.
func (s *configurationVersions) List(ctx context.Context, workspaceID string, options ConfigurationVersionListOptions) (*ConfigurationVersionList, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s/configuration-versions", url.QueryEscape(workspaceID))
req, err := s.client.newRequest("GET", u, &options)
if err != nil {
return nil, err
}
cvl := &ConfigurationVersionList{}
err = s.client.do(ctx, req, cvl)
if err != nil {
return nil, err
}
return cvl, nil
}
// ConfigurationVersionCreateOptions represents the options for creating a
// configuration version.
type ConfigurationVersionCreateOptions struct {
// For internal use only!
ID string `jsonapi:"primary,configuration-versions"`
// When true, runs are queued automatically when the configuration version
// is uploaded.
AutoQueueRuns *bool `jsonapi:"attr,auto-queue-runs,omitempty"`
// When true, this configuration version can only be used for planning.
Speculative *bool `jsonapi:"attr,speculative,omitempty"`
}
// Create is used to create a new configuration version. The created
// configuration version will be usable once data is uploaded to it.
func (s *configurationVersions) Create(ctx context.Context, workspaceID string, options ConfigurationVersionCreateOptions) (*ConfigurationVersion, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("invalid value for workspace ID")
}
// Make sure we don't send a user provided ID.
options.ID = ""
u := fmt.Sprintf("workspaces/%s/configuration-versions", url.QueryEscape(workspaceID))
req, err := s.client.newRequest("POST", u, &options)
if err != nil {
return nil, err
}
cv := &ConfigurationVersion{}
err = s.client.do(ctx, req, cv)
if err != nil {
return nil, err
}
return cv, nil
}
// Read a configuration version by its ID.
func (s *configurationVersions) Read(ctx context.Context, cvID string) (*ConfigurationVersion, error) {
if !validStringID(&cvID) {
return nil, errors.New("invalid value for configuration version ID")
}
u := fmt.Sprintf("configuration-versions/%s", url.QueryEscape(cvID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
cv := &ConfigurationVersion{}
err = s.client.do(ctx, req, cv)
if err != nil {
return nil, err
}
return cv, nil
}
// Upload packages and uploads Terraform configuration files. It requires the
// upload URL from a configuration version and the path to the configuration
// files on disk.
func (s *configurationVersions) Upload(ctx context.Context, url, path string) error {
file, err := os.Stat(path)
if err != nil {
return err
}
if !file.Mode().IsDir() {
return errors.New("path needs to be an existing directory")
}
body := bytes.NewBuffer(nil)
_, err = slug.Pack(path, body, true)
if err != nil {
return err
}
req, err := s.client.newRequest("PUT", url, body)
if err != nil {
return err
}
return s.client.do(ctx, req, nil)
}