forked from hashicorp/go-tfe
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
44f5072
commit 0c64a8f
Showing
6 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
|
||
// Download the data of an plan export. | ||
Download(ctx context.Context, planExportID string) ([]byte, error) | ||
|
||
// Delete a plan export by its ID. | ||
Delete(ctx context.Context, planExportID string) 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 individual 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 | ||
} | ||
|
||
// 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 | ||
} | ||
|
||
// 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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package tfe | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestPlanExportsCreate(t *testing.T) { | ||
client := testClient(t) | ||
ctx := context.Background() | ||
|
||
rTest, rTestCleanup := createPlannedRun(t, client, nil) | ||
defer rTestCleanup() | ||
|
||
pTest, err := client.Plans.Read(ctx, rTest.Plan.ID) | ||
assert.NoError(t, err) | ||
|
||
t.Run("with valid options", func(t *testing.T) { | ||
options := PlanExportCreateOptions{ | ||
Plan: pTest, | ||
DataType: PlanExportType(PlanExportSentinelMockBundleV0), | ||
} | ||
|
||
pe, err := client.PlanExports.Create(ctx, options) | ||
assert.NoError(t, err) | ||
assert.NotEmpty(t, pe.ID) | ||
assert.Equal(t, PlanExportSentinelMockBundleV0, pe.DataType) | ||
}) | ||
|
||
t.Run("without a plan", func(t *testing.T) { | ||
options := PlanExportCreateOptions{ | ||
Plan: nil, | ||
DataType: PlanExportType(PlanExportSentinelMockBundleV0), | ||
} | ||
|
||
pe, err := client.PlanExports.Create(ctx, options) | ||
assert.Nil(t, pe) | ||
assert.EqualError(t, err, "plan is required") | ||
}) | ||
|
||
t.Run("without a data type", func(t *testing.T) { | ||
options := PlanExportCreateOptions{ | ||
Plan: pTest, | ||
DataType: nil, | ||
} | ||
|
||
pe, err := client.PlanExports.Create(ctx, options) | ||
assert.Nil(t, pe) | ||
assert.EqualError(t, err, "data type is required") | ||
}) | ||
} | ||
|
||
func TestPlanExportsRead(t *testing.T) { | ||
client := testClient(t) | ||
ctx := context.Background() | ||
|
||
peTest, peTestCleanup := createPlanExport(t, client, nil) | ||
defer peTestCleanup() | ||
|
||
t.Run("without a valid ID", func(t *testing.T) { | ||
_, err := client.PlanExports.Read(ctx, badIdentifier) | ||
assert.EqualError(t, err, "invalid value for plan export ID") | ||
}) | ||
|
||
t.Run("with a valid ID", func(t *testing.T) { | ||
pe, err := client.PlanExports.Read(ctx, peTest.ID) | ||
assert.NoError(t, err) | ||
assert.Equal(t, peTest.ID, pe.ID) | ||
assert.Equal(t, peTest.DataType, pe.DataType) | ||
}) | ||
} | ||
|
||
func TestPlanExportsDownload(t *testing.T) { | ||
client := testClient(t) | ||
ctx := context.Background() | ||
|
||
t.Run("without a valid ID", func(t *testing.T) { | ||
_, err := client.PlanExports.Download(ctx, badIdentifier) | ||
assert.EqualError(t, err, "invalid value for plan export ID") | ||
}) | ||
} | ||
|
||
func TestPlanExportsDelete(t *testing.T) { | ||
client := testClient(t) | ||
ctx := context.Background() | ||
|
||
peTest, peTestCleanup := createPlanExport(t, client, nil) | ||
defer peTestCleanup() | ||
|
||
t.Run("when the export does not exist", func(t *testing.T) { | ||
err := client.Policies.Delete(ctx, "pe-doesntexist") | ||
assert.Equal(t, err, ErrResourceNotFound) | ||
}) | ||
|
||
t.Run("without a valid ID", func(t *testing.T) { | ||
err := client.PlanExports.Delete(ctx, badIdentifier) | ||
assert.EqualError(t, err, "invalid value for plan export ID") | ||
}) | ||
|
||
t.Run("with a valid ID", func(t *testing.T) { | ||
err := client.PlanExports.Delete(ctx, peTest.ID) | ||
assert.NoError(t, err) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters