vendor latest go-tfe
Signed-off-by: Paul Thrasher <pthrasher@hashicorp.com>
This commit is contained in:
parent
d3fc3dee6e
commit
d2eaffabea
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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 = ¬ificationConfigurations{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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue