vendor latest go-tfe

Signed-off-by: Paul Thrasher <pthrasher@hashicorp.com>
This commit is contained in:
Paul Thrasher 2019-09-27 14:22:34 -07:00
parent d3fc3dee6e
commit d2eaffabea
No known key found for this signature in database
GPG Key ID: D4765F9CA4D82951
12 changed files with 404 additions and 146 deletions

View File

@ -21,6 +21,7 @@ import (
type mockClient struct {
Applies *mockApplies
ConfigurationVersions *mockConfigurationVersions
CostEstimates *mockCostEstimates
Organizations *mockOrganizations
Plans *mockPlans
PolicyChecks *mockPolicyChecks
@ -730,6 +731,11 @@ func (m *mockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*t
return nil, err
}
ce, err := m.client.CostEstimates.create(options.ConfigurationVersion.ID, options.Workspace.ID)
if err != nil {
return nil, err
}
p, err := m.client.Plans.create(options.ConfigurationVersion.ID, options.Workspace.ID)
if err != nil {
return nil, err
@ -741,13 +747,14 @@ func (m *mockRuns) Create(ctx context.Context, options tfe.RunCreateOptions) (*t
}
r := &tfe.Run{
ID: generateID("run-"),
Actions: &tfe.RunActions{IsCancelable: true},
Apply: a,
HasChanges: false,
Permissions: &tfe.RunPermissions{},
Plan: p,
Status: tfe.RunPending,
ID: generateID("run-"),
Actions: &tfe.RunActions{IsCancelable: true},
Apply: a,
CostEstimate: ce,
HasChanges: false,
Permissions: &tfe.RunPermissions{},
Plan: p,
Status: tfe.RunPending,
}
if pc != nil {
@ -1043,6 +1050,14 @@ func (m *mockWorkspaces) Read(ctx context.Context, organization, workspace strin
return w, nil
}
func (m *mockWorkspaces) ReadByID(ctx context.Context, workspaceID string) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID]
if !ok {
return nil, tfe.ErrResourceNotFound
}
return w, nil
}
func (m *mockWorkspaces) Update(ctx context.Context, organization, workspace string, options tfe.WorkspaceUpdateOptions) (*tfe.Workspace, error) {
w, ok := m.workspaceNames[workspace]
if !ok {
@ -1065,6 +1080,28 @@ func (m *mockWorkspaces) Update(ctx context.Context, organization, workspace str
return w, nil
}
func (m *mockWorkspaces) UpdateByID(ctx context.Context, workspaceID string, options tfe.WorkspaceUpdateOptions) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID]
if !ok {
return nil, tfe.ErrResourceNotFound
}
if options.Name != nil {
w.Name = *options.Name
}
if options.TerraformVersion != nil {
w.TerraformVersion = *options.TerraformVersion
}
if options.WorkingDirectory != nil {
w.WorkingDirectory = *options.WorkingDirectory
}
delete(m.workspaceNames, w.Name)
m.workspaceNames[w.Name] = w
return w, nil
}
func (m *mockWorkspaces) Delete(ctx context.Context, organization, workspace string) error {
if w, ok := m.workspaceNames[workspace]; ok {
delete(m.workspaceIDs, w.ID)
@ -1073,6 +1110,14 @@ func (m *mockWorkspaces) Delete(ctx context.Context, organization, workspace str
return nil
}
func (m *mockWorkspaces) DeleteByID(ctx context.Context, workspaceID string) error {
if w, ok := m.workspaceIDs[workspaceID]; ok {
delete(m.workspaceIDs, w.Name)
}
delete(m.workspaceIDs, workspaceID)
return nil
}
func (m *mockWorkspaces) RemoveVCSConnection(ctx context.Context, organization, workspace string) (*tfe.Workspace, error) {
w, ok := m.workspaceNames[workspace]
if !ok {
@ -1082,6 +1127,15 @@ func (m *mockWorkspaces) RemoveVCSConnection(ctx context.Context, organization,
return w, nil
}
func (m *mockWorkspaces) RemoveVCSConnectionByID(ctx context.Context, workspaceID string) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID]
if !ok {
return nil, tfe.ErrResourceNotFound
}
w.VCSRepo = nil
return w, nil
}
func (m *mockWorkspaces) Lock(ctx context.Context, workspaceID string, options tfe.WorkspaceLockOptions) (*tfe.Workspace, error) {
w, ok := m.workspaceIDs[workspaceID]
if !ok {

View File

@ -59,7 +59,7 @@ func TestRemote_planBasic(t *testing.T) {
t.Fatalf("expected remote backend header in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}
@ -113,7 +113,7 @@ func TestRemote_planLongLine(t *testing.T) {
t.Fatalf("expected remote backend header in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}
@ -374,7 +374,7 @@ func TestRemote_planNoChanges(t *testing.T) {
output := b.CLI.(*cli.MockUi).OutputWriter.String()
if !strings.Contains(output, "No changes. Infrastructure is up-to-date.") {
t.Fatalf("expected no changes in plan summery: %s", output)
t.Fatalf("expected no changes in plan summary: %s", output)
}
if !strings.Contains(output, "Sentinel Result: true") {
t.Fatalf("expected policy check result in output: %s", output)
@ -415,7 +415,7 @@ func TestRemote_planForceLocal(t *testing.T) {
t.Fatalf("unexpected remote backend header in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}
@ -446,7 +446,7 @@ func TestRemote_planWithoutOperationsEntitlement(t *testing.T) {
t.Fatalf("unexpected remote backend header in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}
@ -491,7 +491,7 @@ func TestRemote_planWorkspaceWithoutOperations(t *testing.T) {
t.Fatalf("unexpected remote backend header in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}
@ -562,7 +562,7 @@ func TestRemote_planLockTimeout(t *testing.T) {
t.Fatalf("expected lock timout error in output: %s", output)
}
if strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("unexpected plan summery in output: %s", output)
t.Fatalf("unexpected plan summary in output: %s", output)
}
}
@ -654,7 +654,7 @@ func TestRemote_planWithWorkingDirectory(t *testing.T) {
t.Fatalf("expected remote backend header in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}
@ -709,7 +709,7 @@ func TestRemote_planWithWorkingDirectoryFromCurrentPath(t *testing.T) {
t.Fatalf("expected remote backend header in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}
@ -777,7 +777,7 @@ func TestRemote_planPolicyPass(t *testing.T) {
t.Fatalf("expected policy check result in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}
@ -816,7 +816,7 @@ func TestRemote_planPolicyHardFail(t *testing.T) {
t.Fatalf("expected policy check result in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}
@ -855,7 +855,7 @@ func TestRemote_planPolicySoftFail(t *testing.T) {
t.Fatalf("expected policy check result in output: %s", output)
}
if !strings.Contains(output, "1 to add, 0 to change, 0 to destroy") {
t.Fatalf("expected plan summery in output: %s", output)
t.Fatalf("expected plan summary in output: %s", output)
}
}

View File

@ -115,6 +115,7 @@ func testBackend(t *testing.T, obj cty.Value) (*Remote, func()) {
b.CLI = cli.NewMockUi()
b.client.Applies = mc.Applies
b.client.ConfigurationVersions = mc.ConfigurationVersions
b.client.CostEstimates = mc.CostEstimates
b.client.Organizations = mc.Organizations
b.client.Plans = mc.Plans
b.client.PolicyChecks = mc.PolicyChecks

View File

@ -136,6 +136,7 @@ tests:
$ export TFE_ADDRESS=https://tfe.local
$ export TFE_TOKEN=xxxxxxxxxxxxxxxxxxx
$ export GITHUB_TOKEN=xxxxxxxxxxxxxxxx
$ export GITHUB_IDENTIFIER=xxxxxxxxxxx
```
In order for the tests relating to queuing and capacity to pass, FRQ should be

View File

@ -1,121 +0,0 @@
package tfe
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/url"
"time"
)
// Compile-time proof of interface implementation.
var _ CostEstimations = (*costEstimations)(nil)
// CostEstimations describes all the costEstimation related methods that
// the Terraform Enterprise API supports.
//
// TFE API docs: https://www.terraform.io/docs/enterprise/api/ (TBD)
type CostEstimations interface {
// Read a costEstimation by its ID.
Read(ctx context.Context, costEstimationID string) (*CostEstimation, error)
// Logs retrieves the logs of a costEstimation.
Logs(ctx context.Context, costEstimationID string) (io.Reader, error)
}
// costEstimations implements CostEstimations.
type costEstimations struct {
client *Client
}
// CostEstimationStatus represents a costEstimation state.
type CostEstimationStatus string
//List all available costEstimation statuses.
const (
CostEstimationCanceled CostEstimationStatus = "canceled"
CostEstimationErrored CostEstimationStatus = "errored"
CostEstimationFinished CostEstimationStatus = "finished"
CostEstimationQueued CostEstimationStatus = "queued"
)
// CostEstimation represents a Terraform Enterprise costEstimation.
type CostEstimation struct {
ID string `jsonapi:"primary,cost-estimations"`
ErrorMessage string `jsonapi:"attr,error-message"`
Status CostEstimationStatus `jsonapi:"attr,status"`
StatusTimestamps *CostEstimationStatusTimestamps `jsonapi:"attr,status-timestamps"`
}
// CostEstimationStatusTimestamps holds the timestamps for individual costEstimation statuses.
type CostEstimationStatusTimestamps struct {
CanceledAt time.Time `json:"canceled-at"`
ErroredAt time.Time `json:"errored-at"`
FinishedAt time.Time `json:"finished-at"`
QueuedAt time.Time `json:"queued-at"`
}
// Read a costEstimation by its ID.
func (s *costEstimations) Read(ctx context.Context, costEstimationID string) (*CostEstimation, error) {
if !validStringID(&costEstimationID) {
return nil, errors.New("invalid value for cost estimation ID")
}
u := fmt.Sprintf("cost-estimations/%s", url.QueryEscape(costEstimationID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
ce := &CostEstimation{}
err = s.client.do(ctx, req, ce)
if err != nil {
return nil, err
}
return ce, nil
}
// Logs retrieves the logs of a costEstimation.
func (s *costEstimations) Logs(ctx context.Context, costEstimationID string) (io.Reader, error) {
if !validStringID(&costEstimationID) {
return nil, errors.New("invalid value for cost estimation ID")
}
// Loop until the context is canceled or the cost estimation is finished
// running. The cost estimation logs are not streamed and so only available
// once the estimation is finished.
for {
// Get the costEstimation to make sure it exists.
ce, err := s.Read(ctx, costEstimationID)
if err != nil {
return nil, err
}
switch ce.Status {
case CostEstimationQueued:
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(500 * time.Millisecond):
continue
}
}
u := fmt.Sprintf("cost-estimations/%s/output", url.QueryEscape(costEstimationID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
logs := bytes.NewBuffer(nil)
err = s.client.do(ctx, req, logs)
if err != nil {
return nil, err
}
return logs, nil
}
}

View File

@ -55,6 +55,9 @@ type Plan struct {
ResourceDestructions int `jsonapi:"attr,resource-destructions"`
Status PlanStatus `jsonapi:"attr,status"`
StatusTimestamps *PlanStatusTimestamps `jsonapi:"attr,status-timestamps"`
// Relations
Exports []*PlanExport `jsonapi:"relation,exports"`
}
// PlanStatusTimestamps holds the timestamps for individual plan statuses.

175
vendor/github.com/hashicorp/go-tfe/plan_export.go generated vendored Normal file
View File

@ -0,0 +1,175 @@
package tfe
import (
"bytes"
"context"
"errors"
"fmt"
"net/url"
"time"
)
// Compile-time proof of interface implementation.
var _ PlanExports = (*planExports)(nil)
// PlanExports describes all the plan export related methods that the Terraform
// Enterprise API supports.
//
// TFE API docs: https://www.terraform.io/docs/enterprise/api/plan-exports.html
type PlanExports interface {
// Export a plan by its ID with the given options.
Create(ctx context.Context, options PlanExportCreateOptions) (*PlanExport, error)
// Read a plan export by its ID.
Read(ctx context.Context, planExportID string) (*PlanExport, error)
// Delete a plan export by its ID.
Delete(ctx context.Context, planExportID string) error
// Download the data of an plan export.
Download(ctx context.Context, planExportID string) ([]byte, error)
}
// planExports implements PlanExports.
type planExports struct {
client *Client
}
// PlanExportDataType represents the type of data exported from a plan.
type PlanExportDataType string
// List all available plan export data types.
const (
PlanExportSentinelMockBundleV0 PlanExportDataType = "sentinel-mock-bundle-v0"
)
// PlanExportStatus represents a plan export state.
type PlanExportStatus string
// List all available plan export statuses.
const (
PlanExportCanceled PlanExportStatus = "canceled"
PlanExportErrored PlanExportStatus = "errored"
PlanExportExpired PlanExportStatus = "expired"
PlanExportFinished PlanExportStatus = "finished"
PlanExportPending PlanExportStatus = "pending"
PlanExportQueued PlanExportStatus = "queued"
)
// PlanExportStatusTimestamps holds the timestamps for plan export statuses.
type PlanExportStatusTimestamps struct {
CanceledAt time.Time `json:"canceled-at"`
ErroredAt time.Time `json:"errored-at"`
ExpiredAt time.Time `json:"expired-at"`
FinishedAt time.Time `json:"finished-at"`
QueuedAt time.Time `json:"queued-at"`
}
// PlanExport represents an export of Terraform Enterprise plan data.
type PlanExport struct {
ID string `jsonapi:"primary,plan-exports"`
DataType PlanExportDataType `jsonapi:"attr,data-type"`
Status PlanExportStatus `jsonapi:"attr,status"`
StatusTimestamps *PlanExportStatusTimestamps `jsonapi:"attr,status-timestamps"`
}
// PlanExportCreateOptions represents the options for exporting data from a plan.
type PlanExportCreateOptions struct {
// For internal use only!
ID string `jsonapi:"primary,plan-exports"`
// The plan to export.
Plan *Plan `jsonapi:"relation,plan"`
// The name of the policy set.
DataType *PlanExportDataType `jsonapi:"attr,data-type"`
}
func (o PlanExportCreateOptions) valid() error {
if o.Plan == nil {
return errors.New("plan is required")
}
if o.DataType == nil {
return errors.New("data type is required")
}
return nil
}
func (s *planExports) Create(ctx context.Context, options PlanExportCreateOptions) (*PlanExport, error) {
if err := options.valid(); err != nil {
return nil, err
}
// Make sure we don't send a user provided ID.
options.ID = ""
req, err := s.client.newRequest("POST", "plan-exports", &options)
if err != nil {
return nil, err
}
pe := &PlanExport{}
err = s.client.do(ctx, req, pe)
if err != nil {
return nil, err
}
return pe, err
}
// Read a plan export by its ID.
func (s *planExports) Read(ctx context.Context, planExportID string) (*PlanExport, error) {
if !validStringID(&planExportID) {
return nil, errors.New("invalid value for plan export ID")
}
u := fmt.Sprintf("plan-exports/%s", url.QueryEscape(planExportID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
pe := &PlanExport{}
err = s.client.do(ctx, req, pe)
if err != nil {
return nil, err
}
return pe, nil
}
// Delete a plan export by ID.
func (s *planExports) Delete(ctx context.Context, planExportID string) error {
if !validStringID(&planExportID) {
return errors.New("invalid value for plan export ID")
}
u := fmt.Sprintf("plan-exports/%s", url.QueryEscape(planExportID))
req, err := s.client.newRequest("DELETE", u, nil)
if err != nil {
return err
}
return s.client.do(ctx, req, nil)
}
// Download a plan export's data. Data is exported in a .tar.gz format.
func (s *planExports) Download(ctx context.Context, planExportID string) ([]byte, error) {
if !validStringID(&planExportID) {
return nil, errors.New("invalid value for plan export ID")
}
u := fmt.Sprintf("plan-exports/%s/download", url.QueryEscape(planExportID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
var buf bytes.Buffer
err = s.client.do(ctx, req, &buf)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}

View File

@ -28,10 +28,12 @@ type PolicySets interface {
// Update an existing policy set.
Update(ctx context.Context, policySetID string, options PolicySetUpdateOptions) (*PolicySet, error)
// Add policies to a policy set.
// Add policies to a policy set. This function can only be used when
// there is no VCS repository associated with the policy set.
AddPolicies(ctx context.Context, policySetID string, options PolicySetAddPoliciesOptions) error
// Remove policies from a policy set.
// Remove policies from a policy set. This function can only be used
// when there is no VCS repository associated with the policy set.
RemovePolicies(ctx context.Context, policySetID string, options PolicySetRemovePoliciesOptions) error
// Add workspaces to a policy set.
@ -61,7 +63,9 @@ type PolicySet struct {
Name string `jsonapi:"attr,name"`
Description string `jsonapi:"attr,description"`
Global bool `jsonapi:"attr,global"`
PoliciesPath string `jsonapi:"attr,policies-path"`
PolicyCount int `jsonapi:"attr,policy-count"`
VCSRepo *VCSRepo `jsonapi:"attr,vcs-repo"`
WorkspaceCount int `jsonapi:"attr,workspace-count"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`
@ -115,9 +119,21 @@ type PolicySetCreateOptions struct {
// Whether or not the policy set is global.
Global *bool `jsonapi:"attr,global,omitempty"`
// The sub-path within the attached VCS repository to ingress. All
// files and directories outside of this sub-path will be ignored.
// This option may only be specified when a VCS repo is present.
PoliciesPath *string `jsonapi:"attr,policies-path,omitempty"`
// The initial members of the policy set.
Policies []*Policy `jsonapi:"relation,policies,omitempty"`
// VCS repository information. When present, the policies and
// configuration will be sourced from the specified VCS repository
// instead of being defined within the policy set itself. Note that
// this option is mutually exclusive with the Policies option and
// both cannot be used at the same time.
VCSRepo *VCSRepoOptions `jsonapi:"attr,vcs-repo,omitempty"`
// The initial list of workspaces for which the policy set should be enforced.
Workspaces []*Workspace `jsonapi:"relation,workspaces,omitempty"`
}

View File

@ -102,7 +102,7 @@ type Run struct {
// Relations
Apply *Apply `jsonapi:"relation,apply"`
ConfigurationVersion *ConfigurationVersion `jsonapi:"relation,configuration-version"`
CostEstimation *CostEstimation `jsonapi:"relation,cost-estimation"`
CostEstimate *CostEstimate `jsonapi:"relation,cost-estimate"`
Plan *Plan `jsonapi:"relation,plan"`
PolicyChecks []*PolicyCheck `jsonapi:"relation,policy-checks"`
Workspace *Workspace `jsonapi:"relation,workspace"`

View File

@ -32,6 +32,8 @@ const (
DefaultAddress = "https://app.terraform.io"
// DefaultBasePath on which the API is served.
DefaultBasePath = "/api/v2/"
// No-op API endpoint used to configure the rate limiter
PingEndpoint = "ping"
)
var (
@ -106,13 +108,14 @@ type Client struct {
Applies Applies
ConfigurationVersions ConfigurationVersions
CostEstimations CostEstimations
CostEstimates CostEstimates
NotificationConfigurations NotificationConfigurations
OAuthClients OAuthClients
OAuthTokens OAuthTokens
Organizations Organizations
OrganizationTokens OrganizationTokens
Plans Plans
PlanExports PlanExports
Policies Policies
PolicyChecks PolicyChecks
PolicySets PolicySets
@ -196,13 +199,14 @@ func NewClient(cfg *Config) (*Client, error) {
// Create the services.
client.Applies = &applies{client: client}
client.ConfigurationVersions = &configurationVersions{client: client}
client.CostEstimations = &costEstimations{client: client}
client.CostEstimates = &costEstimates{client: client}
client.NotificationConfigurations = &notificationConfigurations{client: client}
client.OAuthClients = &oAuthClients{client: client}
client.OAuthTokens = &oAuthTokens{client: client}
client.Organizations = &organizations{client: client}
client.OrganizationTokens = &organizationTokens{client: client}
client.Plans = &plans{client: client}
client.PlanExports = &planExports{client: client}
client.Policies = &policies{client: client}
client.PolicyChecks = &policyChecks{client: client}
client.PolicySets = &policySets{client: client}
@ -291,7 +295,11 @@ func rateLimitBackoff(min, max time.Duration, attemptNum int, resp *http.Respons
// configureLimiter configures the rate limiter.
func (c *Client) configureLimiter() error {
// Create a new request.
req, err := http.NewRequest("GET", c.baseURL.String(), nil)
u, err := c.baseURL.Parse(PingEndpoint)
if err != nil {
return err
}
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return err
}

View File

@ -40,6 +40,11 @@ func NotificationDestination(v NotificationDestinationType) *NotificationDestina
return &v
}
// PlanExportType returns a pointer to the given plan export data type.
func PlanExportType(v PlanExportDataType) *PlanExportDataType {
return &v
}
// ServiceProvider returns a pointer to the given service provider type.
func ServiceProvider(v ServiceProviderType) *ServiceProviderType {
return &v

View File

@ -25,15 +25,27 @@ type Workspaces interface {
// Read a workspace by its name.
Read(ctx context.Context, organization string, workspace string) (*Workspace, error)
// ReadByID reads a workspace by its ID.
ReadByID(ctx context.Context, workspaceID string) (*Workspace, error)
// Update settings of an existing workspace.
Update(ctx context.Context, organization string, workspace string, options WorkspaceUpdateOptions) (*Workspace, error)
// UpdateByID updates the settings of an existing workspace.
UpdateByID(ctx context.Context, workspaceID string, options WorkspaceUpdateOptions) (*Workspace, error)
// Delete a workspace by its name.
Delete(ctx context.Context, organization string, workspace string) error
// DeleteByID deletes a workspace by its ID.
DeleteByID(ctx context.Context, workspaceID string) error
// RemoveVCSConnection from a workspace.
RemoveVCSConnection(ctx context.Context, organization, workspace string) (*Workspace, error)
// RemoveVCSConnectionByID removes a VCS connection from a workspace.
RemoveVCSConnectionByID(ctx context.Context, workspaceID string) (*Workspace, error)
// Lock a workspace by its ID.
Lock(ctx context.Context, workspaceID string, options WorkspaceLockOptions) (*Workspace, error)
@ -69,6 +81,7 @@ type Workspace struct {
CanQueueDestroyPlan bool `jsonapi:"attr,can-queue-destroy-plan"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
Environment string `jsonapi:"attr,environment"`
FileTriggersEnabled bool `jsonapi:"attr,file-triggers-enabled"`
Locked bool `jsonapi:"attr,locked"`
MigrationEnvironment string `jsonapi:"attr,migration-environment"`
Name string `jsonapi:"attr,name"`
@ -76,6 +89,7 @@ type Workspace struct {
Permissions *WorkspacePermissions `jsonapi:"attr,permissions"`
QueueAllRuns bool `jsonapi:"attr,queue-all-runs"`
TerraformVersion string `jsonapi:"attr,terraform-version"`
TriggerPrefixes []string `jsonapi:"attr,trigger-prefixes"`
VCSRepo *VCSRepo `jsonapi:"attr,vcs-repo"`
WorkingDirectory string `jsonapi:"attr,working-directory"`
@ -149,6 +163,12 @@ type WorkspaceCreateOptions struct {
// Whether to automatically apply changes when a Terraform plan is successful.
AutoApply *bool `jsonapi:"attr,auto-apply,omitempty"`
// Whether to filter runs based on the changed files in a VCS push. If
// enabled, the working directory and trigger prefixes describe a set of
// paths which must contain changes for a VCS push to trigger a run. If
// disabled, any push will trigger a run.
FileTriggersEnabled *bool `jsonapi:"attr,file-triggers-enabled,omitempty"`
// The legacy TFE environment to use as the source of the migration, in the
// form organization/environment. Omit this unless you are migrating a legacy
// environment.
@ -167,6 +187,10 @@ type WorkspaceCreateOptions struct {
// workspace, the latest version is selected unless otherwise specified.
TerraformVersion *string `jsonapi:"attr,terraform-version,omitempty"`
// List of repository-root-relative paths which list all locations to be
// tracked for changes. See FileTriggersEnabled above for more details.
TriggerPrefixes []string `jsonapi:"attr,trigger-prefixes,omitempty"`
// Settings for the workspace's VCS repository. If omitted, the workspace is
// created without a VCS repo. If included, you must specify at least the
// oauth-token-id and identifier keys below.
@ -251,6 +275,27 @@ func (s *workspaces) Read(ctx context.Context, organization, workspace string) (
return w, nil
}
// ReadByID reads a workspace by its ID.
func (s *workspaces) ReadByID(ctx context.Context, workspaceID string) (*Workspace, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s", url.QueryEscape(workspaceID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
w := &Workspace{}
err = s.client.do(ctx, req, w)
if err != nil {
return nil, err
}
return w, nil
}
// WorkspaceUpdateOptions represents the options for updating a workspace.
type WorkspaceUpdateOptions struct {
// For internal use only!
@ -265,6 +310,12 @@ type WorkspaceUpdateOptions struct {
// API and UI.
Name *string `jsonapi:"attr,name,omitempty"`
// Whether to filter runs based on the changed files in a VCS push. If
// enabled, the working directory and trigger prefixes describe a set of
// paths which must contain changes for a VCS push to trigger a run. If
// disabled, any push will trigger a run.
FileTriggersEnabled *bool `jsonapi:"attr,file-triggers-enabled,omitempty"`
// Whether to queue all runs. Unless this is set to true, runs triggered by
// a webhook will not be queued until at least one run is manually queued.
QueueAllRuns *bool `jsonapi:"attr,queue-all-runs,omitempty"`
@ -272,6 +323,10 @@ type WorkspaceUpdateOptions struct {
// The version of Terraform to use for this workspace.
TerraformVersion *string `jsonapi:"attr,terraform-version,omitempty"`
// List of repository-root-relative paths which list all locations to be
// tracked for changes. See FileTriggersEnabled above for more details.
TriggerPrefixes []string `jsonapi:"attr,trigger-prefixes,omitempty"`
// To delete a workspace's existing VCS repo, specify null instead of an
// object. To modify a workspace's existing VCS repo, include whichever of
// the keys below you wish to modify. To add a new VCS repo to a workspace
@ -317,6 +372,30 @@ func (s *workspaces) Update(ctx context.Context, organization, workspace string,
return w, nil
}
// UpdateByID updates the settings of an existing workspace.
func (s *workspaces) UpdateByID(ctx context.Context, workspaceID string, options WorkspaceUpdateOptions) (*Workspace, 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", url.QueryEscape(workspaceID))
req, err := s.client.newRequest("PATCH", u, &options)
if err != nil {
return nil, err
}
w := &Workspace{}
err = s.client.do(ctx, req, w)
if err != nil {
return nil, err
}
return w, nil
}
// Delete a workspace by its name.
func (s *workspaces) Delete(ctx context.Context, organization, workspace string) error {
if !validStringID(&organization) {
@ -339,6 +418,21 @@ func (s *workspaces) Delete(ctx context.Context, organization, workspace string)
return s.client.do(ctx, req, nil)
}
// DeleteByID deletes a workspace by its ID.
func (s *workspaces) DeleteByID(ctx context.Context, workspaceID string) error {
if !validStringID(&workspaceID) {
return errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s", url.QueryEscape(workspaceID))
req, err := s.client.newRequest("DELETE", u, nil)
if err != nil {
return err
}
return s.client.do(ctx, req, nil)
}
// workspaceRemoveVCSConnectionOptions
type workspaceRemoveVCSConnectionOptions struct {
ID string `jsonapi:"primary,workspaces"`
@ -374,6 +468,28 @@ func (s *workspaces) RemoveVCSConnection(ctx context.Context, organization, work
return w, nil
}
// RemoveVCSConnectionByID removes a VCS connection from a workspace.
func (s *workspaces) RemoveVCSConnectionByID(ctx context.Context, workspaceID string) (*Workspace, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s", url.QueryEscape(workspaceID))
req, err := s.client.newRequest("PATCH", u, &workspaceRemoveVCSConnectionOptions{})
if err != nil {
return nil, err
}
w := &Workspace{}
err = s.client.do(ctx, req, w)
if err != nil {
return nil, err
}
return w, nil
}
// WorkspaceLockOptions represents the options for locking a workspace.
type WorkspaceLockOptions struct {
// Specifies the reason for locking the workspace.