Merge pull request #18914 from hashicorp/f-update-deps
govendor: update `go-tfe`
This commit is contained in:
commit
27b720113e
|
@ -0,0 +1,131 @@
|
|||
package tfe
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Compile-time proof of interface implementation.
|
||||
var _ Applies = (*applies)(nil)
|
||||
|
||||
// Applies describes all the apply related methods that the Terraform
|
||||
// Enterprise API supports.
|
||||
//
|
||||
// TFE API docs: https://www.terraform.io/docs/enterprise/api/apply.html
|
||||
type Applies interface {
|
||||
// Read an apply by its ID.
|
||||
Read(ctx context.Context, applyID string) (*Apply, error)
|
||||
|
||||
// Logs retrieves the logs of an apply.
|
||||
Logs(ctx context.Context, applyID string) (io.Reader, error)
|
||||
}
|
||||
|
||||
// applies implements Applys.
|
||||
type applies struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ApplyStatus represents an apply state.
|
||||
type ApplyStatus string
|
||||
|
||||
//List all available apply statuses.
|
||||
const (
|
||||
ApplyCanceled ApplyStatus = "canceled"
|
||||
ApplyCreated ApplyStatus = "created"
|
||||
ApplyErrored ApplyStatus = "errored"
|
||||
ApplyFinished ApplyStatus = "finished"
|
||||
ApplyMFAWaiting ApplyStatus = "mfa_waiting"
|
||||
ApplyPending ApplyStatus = "pending"
|
||||
ApplyQueued ApplyStatus = "queued"
|
||||
ApplyRunning ApplyStatus = "running"
|
||||
)
|
||||
|
||||
// Apply represents a Terraform Enterprise apply.
|
||||
type Apply struct {
|
||||
ID string `jsonapi:"primary,applies"`
|
||||
LogReadURL string `jsonapi:"attr,log-read-url"`
|
||||
ResourceAdditions int `jsonapi:"attr,resource-additions"`
|
||||
ResourceChanges int `jsonapi:"attr,resource-changes"`
|
||||
ResourceDestructions int `jsonapi:"attr,resource-destructions"`
|
||||
Status ApplyStatus `jsonapi:"attr,status"`
|
||||
StatusTimestamps *ApplyStatusTimestamps `jsonapi:"attr,status-timestamps"`
|
||||
}
|
||||
|
||||
// ApplyStatusTimestamps holds the timestamps for individual apply statuses.
|
||||
type ApplyStatusTimestamps struct {
|
||||
CanceledAt time.Time `json:"canceled-at"`
|
||||
ErroredAt time.Time `json:"errored-at"`
|
||||
FinishedAt time.Time `json:"finished-at"`
|
||||
ForceCanceledAt time.Time `json:"force-canceled-at"`
|
||||
QueuedAt time.Time `json:"queued-at"`
|
||||
StartedAt time.Time `json:"started-at"`
|
||||
}
|
||||
|
||||
// Read an apply by its ID.
|
||||
func (s *applies) Read(ctx context.Context, applyID string) (*Apply, error) {
|
||||
if !validStringID(&applyID) {
|
||||
return nil, errors.New("Invalid value for apply ID")
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("applies/%s", url.QueryEscape(applyID))
|
||||
req, err := s.client.newRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a := &Apply{}
|
||||
err = s.client.do(ctx, req, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Logs retrieves the logs of an apply.
|
||||
func (s *applies) Logs(ctx context.Context, applyID string) (io.Reader, error) {
|
||||
if !validStringID(&applyID) {
|
||||
return nil, errors.New("Invalid value for apply ID")
|
||||
}
|
||||
|
||||
// Get the apply to make sure it exists.
|
||||
a, err := s.Read(ctx, applyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return an error if the log URL is empty.
|
||||
if a.LogReadURL == "" {
|
||||
return nil, fmt.Errorf("Apply %s does not have a log URL", applyID)
|
||||
}
|
||||
|
||||
u, err := url.Parse(a.LogReadURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid log URL: %v", err)
|
||||
}
|
||||
|
||||
done := func() (bool, error) {
|
||||
a, err := s.Read(ctx, a.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch a.Status {
|
||||
case ApplyCanceled, ApplyErrored, ApplyFinished:
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &LogReader{
|
||||
client: s.client,
|
||||
ctx: ctx,
|
||||
done: done,
|
||||
logURL: u,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package tfe
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogReader implements io.Reader for streaming logs.
|
||||
type LogReader struct {
|
||||
client *Client
|
||||
ctx context.Context
|
||||
done func() (bool, error)
|
||||
logURL *url.URL
|
||||
offset int64
|
||||
}
|
||||
|
||||
func (r *LogReader) Read(l []byte) (int, error) {
|
||||
if written, err := r.read(l); err != io.ErrNoProgress {
|
||||
return written, err
|
||||
}
|
||||
|
||||
// Loop until we can any data, the context is canceled or the
|
||||
// run is finsished. If we would return right away without any
|
||||
// data, we could and up causing a io.ErrNoProgress error.
|
||||
for {
|
||||
select {
|
||||
case <-r.ctx.Done():
|
||||
return 0, r.ctx.Err()
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
if written, err := r.read(l); err != io.ErrNoProgress {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LogReader) read(l []byte) (int, error) {
|
||||
// Update the query string.
|
||||
r.logURL.RawQuery = fmt.Sprintf("limit=%d&offset=%d", len(l), r.offset)
|
||||
|
||||
// Create a new request.
|
||||
req, err := http.NewRequest("GET", r.logURL.String(), nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
req = req.WithContext(r.ctx)
|
||||
|
||||
// Retrieve the next chunk.
|
||||
resp, err := r.client.http.Do(req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Basic response checking.
|
||||
if err := checkResponseCode(resp); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Read the retrieved chunk.
|
||||
written, err := resp.Body.Read(l)
|
||||
if err != nil && err != io.EOF {
|
||||
// Ignore io.EOF errors returned when reading from the response
|
||||
// body as this indicates the end of the chunk and not the end
|
||||
// of the logfile.
|
||||
return written, err
|
||||
}
|
||||
|
||||
// Check if we need to continue the loop and wait 500 miliseconds
|
||||
// before checking if there is a new chunk available or that the
|
||||
// run is finished and we are done reading all chunks.
|
||||
if written == 0 {
|
||||
done, err := r.done()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if done {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return 0, io.ErrNoProgress
|
||||
}
|
||||
|
||||
// Update the offset for the next read.
|
||||
r.offset += int64(written)
|
||||
|
||||
return written, nil
|
||||
}
|
|
@ -17,8 +17,17 @@ var _ OAuthClients = (*oAuthClients)(nil)
|
|||
// TFE API docs:
|
||||
// https://www.terraform.io/docs/enterprise/api/oauth-clients.html
|
||||
type OAuthClients interface {
|
||||
// Create a VCS connection between an organization and a VCS provider.
|
||||
// List all the OAuth clients for a given organization.
|
||||
List(ctx context.Context, organization string, options OAuthClientListOptions) (*OAuthClientList, error)
|
||||
|
||||
// Create an OAuth client to connect an organization and a VCS provider.
|
||||
Create(ctx context.Context, organization string, options OAuthClientCreateOptions) (*OAuthClient, error)
|
||||
|
||||
// Read an OAuth client by its ID.
|
||||
Read(ctx context.Context, oAuthClientID string) (*OAuthClient, error)
|
||||
|
||||
// Delete an OAuth client by its ID.
|
||||
Delete(ctx context.Context, oAuthClientID string) error
|
||||
}
|
||||
|
||||
// oAuthClients implements OAuthClients.
|
||||
|
@ -40,6 +49,12 @@ const (
|
|||
ServiceProviderGitlabEE ServiceProviderType = "gitlab_enterprise_edition"
|
||||
)
|
||||
|
||||
// OAuthClientList represents a list of OAuth clients.
|
||||
type OAuthClientList struct {
|
||||
*Pagination
|
||||
Items []*OAuthClient
|
||||
}
|
||||
|
||||
// OAuthClient represents a connection between an organization and a VCS
|
||||
// provider.
|
||||
type OAuthClient struct {
|
||||
|
@ -56,7 +71,34 @@ type OAuthClient struct {
|
|||
|
||||
// Relations
|
||||
Organization *Organization `jsonapi:"relation,organization"`
|
||||
OAuthToken []*OAuthToken `jsonapi:"relation,oauth-token"`
|
||||
OAuthTokens []*OAuthToken `jsonapi:"relation,oauth-tokens"`
|
||||
}
|
||||
|
||||
// OAuthClientListOptions represents the options for listing
|
||||
// OAuth clients.
|
||||
type OAuthClientListOptions struct {
|
||||
ListOptions
|
||||
}
|
||||
|
||||
// List all the OAuth clients for a given organization.
|
||||
func (s *oAuthClients) List(ctx context.Context, organization string, options OAuthClientListOptions) (*OAuthClientList, error) {
|
||||
if !validStringID(&organization) {
|
||||
return nil, errors.New("Invalid value for organization")
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("organizations/%s/oauth-clients", url.QueryEscape(organization))
|
||||
req, err := s.client.newRequest("GET", u, &options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ocl := &OAuthClientList{}
|
||||
err = s.client.do(ctx, req, ocl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ocl, nil
|
||||
}
|
||||
|
||||
// OAuthClientCreateOptions represents the options for creating an OAuth client.
|
||||
|
@ -70,11 +112,8 @@ type OAuthClientCreateOptions struct {
|
|||
// The homepage of your VCS provider.
|
||||
HTTPURL *string `jsonapi:"attr,http-url"`
|
||||
|
||||
// The key you were given by your VCS provider.
|
||||
Key *string `jsonapi:"attr,key"`
|
||||
|
||||
// The secret you were given by your VCS provider.
|
||||
Secret *string `jsonapi:"attr,secret"`
|
||||
// The token string you were given by your VCS provider.
|
||||
OAuthToken *string `jsonapi:"attr,oauth-token-string"`
|
||||
|
||||
// The VCS provider being connected with.
|
||||
ServiceProvider *ServiceProviderType `jsonapi:"attr,service-provider"`
|
||||
|
@ -87,11 +126,8 @@ func (o OAuthClientCreateOptions) valid() error {
|
|||
if !validString(o.HTTPURL) {
|
||||
return errors.New("HTTPURL is required")
|
||||
}
|
||||
if !validString(o.Key) {
|
||||
return errors.New("Key is required")
|
||||
}
|
||||
if !validString(o.Secret) {
|
||||
return errors.New("Secret is required")
|
||||
if !validString(o.OAuthToken) {
|
||||
return errors.New("OAuthToken is required")
|
||||
}
|
||||
if o.ServiceProvider == nil {
|
||||
return errors.New("ServiceProvider is required")
|
||||
|
@ -99,7 +135,7 @@ func (o OAuthClientCreateOptions) valid() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Create a VCS connection between an organization and a VCS provider.
|
||||
// Create an OAuth client to connect an organization and a VCS provider.
|
||||
func (s *oAuthClients) Create(ctx context.Context, organization string, options OAuthClientCreateOptions) (*OAuthClient, error) {
|
||||
if !validStringID(&organization) {
|
||||
return nil, errors.New("Invalid value for organization")
|
||||
|
@ -125,3 +161,39 @@ func (s *oAuthClients) Create(ctx context.Context, organization string, options
|
|||
|
||||
return oc, nil
|
||||
}
|
||||
|
||||
// Read an OAuth client by its ID.
|
||||
func (s *oAuthClients) Read(ctx context.Context, oAuthClientID string) (*OAuthClient, error) {
|
||||
if !validStringID(&oAuthClientID) {
|
||||
return nil, errors.New("Invalid value for OAuth client ID")
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("oauth-clients/%s", url.QueryEscape(oAuthClientID))
|
||||
req, err := s.client.newRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oc := &OAuthClient{}
|
||||
err = s.client.do(ctx, req, oc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return oc, err
|
||||
}
|
||||
|
||||
// Delete an OAuth client by its ID.
|
||||
func (s *oAuthClients) Delete(ctx context.Context, oAuthClientID string) error {
|
||||
if !validStringID(&oAuthClientID) {
|
||||
return errors.New("Invalid value for OAuth client ID")
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("oauth-clients/%s", url.QueryEscape(oAuthClientID))
|
||||
req, err := s.client.newRequest("DELETE", u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.client.do(ctx, req, nil)
|
||||
}
|
||||
|
|
|
@ -17,8 +17,16 @@ var _ OAuthTokens = (*oAuthTokens)(nil)
|
|||
// TFE API docs:
|
||||
// https://www.terraform.io/docs/enterprise/api/oauth-tokens.html
|
||||
type OAuthTokens interface {
|
||||
// List all the OAuth Tokens for a given organization.
|
||||
// List all the OAuth tokens for a given organization.
|
||||
List(ctx context.Context, organization string, options OAuthTokenListOptions) (*OAuthTokenList, error)
|
||||
// Read a OAuth token by its ID.
|
||||
Read(ctx context.Context, oAuthTokenID string) (*OAuthToken, error)
|
||||
|
||||
// Update an existing OAuth token.
|
||||
Update(ctx context.Context, oAuthTokenID string, options OAuthTokenUpdateOptions) (*OAuthToken, error)
|
||||
|
||||
// Delete a OAuth token by its ID.
|
||||
Delete(ctx context.Context, oAuthTokenID string) error
|
||||
}
|
||||
|
||||
// oAuthTokens implements OAuthTokens.
|
||||
|
@ -71,3 +79,72 @@ func (s *oAuthTokens) List(ctx context.Context, organization string, options OAu
|
|||
|
||||
return otl, nil
|
||||
}
|
||||
|
||||
// Read an OAuth token by its ID.
|
||||
func (s *oAuthTokens) Read(ctx context.Context, oAuthTokenID string) (*OAuthToken, error) {
|
||||
if !validStringID(&oAuthTokenID) {
|
||||
return nil, errors.New("Invalid value for OAuth token ID")
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("oauth-tokens/%s", url.QueryEscape(oAuthTokenID))
|
||||
req, err := s.client.newRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ot := &OAuthToken{}
|
||||
err = s.client.do(ctx, req, ot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ot, err
|
||||
}
|
||||
|
||||
// OAuthTokenUpdateOptions represents the options for updating an OAuth token.
|
||||
type OAuthTokenUpdateOptions struct {
|
||||
// For internal use only!
|
||||
ID string `jsonapi:"primary,oauth-tokens"`
|
||||
|
||||
// A private SSH key to be used for git clone operations.
|
||||
PrivateSSHKey *string `jsonapi:"attr,ssh-key"`
|
||||
}
|
||||
|
||||
// Update an existing OAuth token.
|
||||
func (s *oAuthTokens) Update(ctx context.Context, oAuthTokenID string, options OAuthTokenUpdateOptions) (*OAuthToken, error) {
|
||||
if !validStringID(&oAuthTokenID) {
|
||||
return nil, errors.New("Invalid value for OAuth token ID")
|
||||
}
|
||||
|
||||
// Make sure we don't send a user provided ID.
|
||||
options.ID = ""
|
||||
|
||||
u := fmt.Sprintf("oauth-tokens/%s", url.QueryEscape(oAuthTokenID))
|
||||
req, err := s.client.newRequest("PATCH", u, &options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ot := &OAuthToken{}
|
||||
err = s.client.do(ctx, req, ot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ot, err
|
||||
}
|
||||
|
||||
// Delete an OAuth token by its ID.
|
||||
func (s *oAuthTokens) Delete(ctx context.Context, oAuthTokenID string) error {
|
||||
if !validStringID(&oAuthTokenID) {
|
||||
return errors.New("Invalid value for OAuth token ID")
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("oauth-tokens/%s", url.QueryEscape(oAuthTokenID))
|
||||
req, err := s.client.newRequest("DELETE", u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.client.do(ctx, req, nil)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
@ -47,23 +46,24 @@ const (
|
|||
|
||||
// Plan represents a Terraform Enterprise plan.
|
||||
type Plan struct {
|
||||
ID string `jsonapi:"primary,plans"`
|
||||
HasChanges bool `jsonapi:"attr,has-changes"`
|
||||
LogReadURL string `jsonapi:"attr,log-read-url"`
|
||||
Status PlanStatus `jsonapi:"attr,status"`
|
||||
StatusTimestamps *PlanStatusTimestamps `jsonapi:"attr,status-timestamps"`
|
||||
ID string `jsonapi:"primary,plans"`
|
||||
HasChanges bool `jsonapi:"attr,has-changes"`
|
||||
LogReadURL string `jsonapi:"attr,log-read-url"`
|
||||
ResourceAdditions int `jsonapi:"attr,resource-additions"`
|
||||
ResourceChanges int `jsonapi:"attr,resource-changes"`
|
||||
ResourceDestructions int `jsonapi:"attr,resource-destructions"`
|
||||
Status PlanStatus `jsonapi:"attr,status"`
|
||||
StatusTimestamps *PlanStatusTimestamps `jsonapi:"attr,status-timestamps"`
|
||||
}
|
||||
|
||||
// PlanStatusTimestamps holds the timestamps for individual plan statuses.
|
||||
type PlanStatusTimestamps struct {
|
||||
CanceledAt time.Time `json:"canceled-at"`
|
||||
CreatedAt time.Time `json:"created-at"`
|
||||
ErroredAt time.Time `json:"errored-at"`
|
||||
FinishedAt time.Time `json:"finished-at"`
|
||||
MFAWaitingAt time.Time `json:"mfa_waiting-at"`
|
||||
PendingAt time.Time `json:"pending-at"`
|
||||
QueuedAt time.Time `json:"queued-at"`
|
||||
RunningAt time.Time `json:"running-at"`
|
||||
CanceledAt time.Time `json:"canceled-at"`
|
||||
ErroredAt time.Time `json:"errored-at"`
|
||||
FinishedAt time.Time `json:"finished-at"`
|
||||
ForceCanceledAt time.Time `json:"force-canceled-at"`
|
||||
QueuedAt time.Time `json:"queued-at"`
|
||||
StartedAt time.Time `json:"started-at"`
|
||||
}
|
||||
|
||||
// Read a plan by its ID.
|
||||
|
@ -109,98 +109,24 @@ func (s *plans) Logs(ctx context.Context, planID string) (io.Reader, error) {
|
|||
return nil, fmt.Errorf("Invalid log URL: %v", err)
|
||||
}
|
||||
|
||||
done := func() (bool, error) {
|
||||
p, err := s.Read(ctx, p.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch p.Status {
|
||||
case PlanCanceled, PlanErrored, PlanFinished:
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &LogReader{
|
||||
client: s.client,
|
||||
ctx: ctx,
|
||||
done: done,
|
||||
logURL: u,
|
||||
plan: p,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LogReader implements io.Reader for streaming plan logs.
|
||||
type LogReader struct {
|
||||
client *Client
|
||||
ctx context.Context
|
||||
logURL *url.URL
|
||||
offset int64
|
||||
plan *Plan
|
||||
reads uint64
|
||||
}
|
||||
|
||||
func (r *LogReader) Read(l []byte) (int, error) {
|
||||
if written, err := r.read(l); err != io.ErrNoProgress {
|
||||
return written, err
|
||||
}
|
||||
|
||||
// Loop until we can any data, the context is canceled or the plan
|
||||
// is finsished running. If we would return right away without any
|
||||
// data, we could and up causing a io.ErrNoProgress error.
|
||||
for {
|
||||
select {
|
||||
case <-r.ctx.Done():
|
||||
return 0, r.ctx.Err()
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
if written, err := r.read(l); err != io.ErrNoProgress {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *LogReader) read(l []byte) (int, error) {
|
||||
// Update the query string.
|
||||
r.logURL.RawQuery = fmt.Sprintf("limit=%d&offset=%d", len(l), r.offset)
|
||||
|
||||
// Create a new request.
|
||||
req, err := http.NewRequest("GET", r.logURL.String(), nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
req = req.WithContext(r.ctx)
|
||||
|
||||
// Retrieve the next chunk.
|
||||
resp, err := r.client.http.Do(req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Basic response checking.
|
||||
if err := checkResponseCode(resp); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Read the retrieved chunk.
|
||||
written, err := resp.Body.Read(l)
|
||||
if err != nil && err != io.EOF {
|
||||
// Ignore io.EOF errors returned when reading from the response
|
||||
// body as this indicates the end of the chunk and not the end
|
||||
// of the logfile.
|
||||
return written, err
|
||||
}
|
||||
|
||||
// Check if we need to continue the loop and wait 500 miliseconds
|
||||
// before checking if there is a new chunk available or that the
|
||||
// plan is finished and we are done reading all chunks.
|
||||
if written == 0 {
|
||||
if r.reads%2 == 0 {
|
||||
r.plan, err = r.client.Plans.Read(r.ctx, r.plan.ID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
switch r.plan.Status {
|
||||
case PlanCanceled, PlanErrored, PlanFinished:
|
||||
return 0, io.EOF
|
||||
default:
|
||||
r.reads++
|
||||
return 0, io.ErrNoProgress
|
||||
}
|
||||
}
|
||||
|
||||
// Update the offset for the next read.
|
||||
r.offset += int64(written)
|
||||
|
||||
return written, nil
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package tfe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
@ -20,8 +22,14 @@ type PolicyChecks interface {
|
|||
// List all policy checks of the given run.
|
||||
List(ctx context.Context, runID string, options PolicyCheckListOptions) (*PolicyCheckList, error)
|
||||
|
||||
// Read a policy check by its ID.
|
||||
Read(ctx context.Context, policyCheckID string) (*PolicyCheck, error)
|
||||
|
||||
// Override a soft-mandatory or warning policy.
|
||||
Override(ctx context.Context, policyCheckID string) (*PolicyCheck, error)
|
||||
|
||||
// Logs retrieves the logs of a policy check.
|
||||
Logs(ctx context.Context, policyCheckID string) (io.Reader, error)
|
||||
}
|
||||
|
||||
// policyChecks implements PolicyChecks.
|
||||
|
@ -64,7 +72,7 @@ type PolicyCheck struct {
|
|||
Actions *PolicyActions `jsonapi:"attr,actions"`
|
||||
Permissions *PolicyPermissions `jsonapi:"attr,permissions"`
|
||||
Result *PolicyResult `jsonapi:"attr,result"`
|
||||
Scope PolicyScope `jsonapi:"attr,source"`
|
||||
Scope PolicyScope `jsonapi:"attr,scope"`
|
||||
Status PolicyStatus `jsonapi:"attr,status"`
|
||||
StatusTimestamps *PolicyStatusTimestamps `jsonapi:"attr,status-timestamps"`
|
||||
}
|
||||
|
@ -127,6 +135,27 @@ func (s *policyChecks) List(ctx context.Context, runID string, options PolicyChe
|
|||
return pcl, nil
|
||||
}
|
||||
|
||||
// Read a policy check by its ID.
|
||||
func (s *policyChecks) Read(ctx context.Context, policyCheckID string) (*PolicyCheck, error) {
|
||||
if !validStringID(&policyCheckID) {
|
||||
return nil, errors.New("Invalid value for policy check ID")
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("policy-checks/%s", url.QueryEscape(policyCheckID))
|
||||
req, err := s.client.newRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pc := &PolicyCheck{}
|
||||
err = s.client.do(ctx, req, pc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
// Override a soft-mandatory or warning policy.
|
||||
func (s *policyChecks) Override(ctx context.Context, policyCheckID string) (*PolicyCheck, error) {
|
||||
if !validStringID(&policyCheckID) {
|
||||
|
@ -147,3 +176,44 @@ func (s *policyChecks) Override(ctx context.Context, policyCheckID string) (*Pol
|
|||
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
// Logs retrieves the logs of a policy check.
|
||||
func (s *policyChecks) Logs(ctx context.Context, policyCheckID string) (io.Reader, error) {
|
||||
if !validStringID(&policyCheckID) {
|
||||
return nil, errors.New("Invalid value for policy check ID")
|
||||
}
|
||||
|
||||
// Loop until the context is canceled or the policy check is finished
|
||||
// running. The policy check logs are not streamed and so only available
|
||||
// once the check is finished.
|
||||
for {
|
||||
pc, err := s.Read(ctx, policyCheckID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch pc.Status {
|
||||
case PolicyPending, PolicyQueued:
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("policy-checks/%s/output", url.QueryEscape(policyCheckID))
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,15 +89,17 @@ type Run struct {
|
|||
StatusTimestamps *RunStatusTimestamps `jsonapi:"attr,status-timestamps"`
|
||||
|
||||
// Relations
|
||||
Apply *Apply `jsonapi:"relation,apply"`
|
||||
ConfigurationVersion *ConfigurationVersion `jsonapi:"relation,configuration-version"`
|
||||
Plan *Plan `jsonapi:"relation,plan"`
|
||||
PolicyChecks []*PolicyCheck `jsonapi:"relation,policy-checks"`
|
||||
Workspace *Workspace `jsonapi:"relation,workspace"`
|
||||
}
|
||||
|
||||
// RunActions represents the run actions.
|
||||
type RunActions struct {
|
||||
IsCancelable bool `json:"is-cancelable"`
|
||||
IsComfirmable bool `json:"is-comfirmable"`
|
||||
IsConfirmable bool `json:"is-confirmable"`
|
||||
IsDiscardable bool `json:"is-discardable"`
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ func DefaultConfig() *Config {
|
|||
Address: os.Getenv("TFE_ADDRESS"),
|
||||
BasePath: DefaultBasePath,
|
||||
Token: os.Getenv("TFE_TOKEN"),
|
||||
HTTPClient: cleanhttp.DefaultClient(),
|
||||
HTTPClient: cleanhttp.DefaultPooledClient(),
|
||||
}
|
||||
|
||||
// Set the default address if none is given.
|
||||
|
@ -77,6 +77,7 @@ type Client struct {
|
|||
http *http.Client
|
||||
userAgent string
|
||||
|
||||
Applies Applies
|
||||
ConfigurationVersions ConfigurationVersions
|
||||
OAuthClients OAuthClients
|
||||
OAuthTokens OAuthTokens
|
||||
|
@ -142,6 +143,7 @@ func NewClient(cfg *Config) (*Client, error) {
|
|||
}
|
||||
|
||||
// Create the services.
|
||||
client.Applies = &applies{client: client}
|
||||
client.ConfigurationVersions = &configurationVersions{client: client}
|
||||
client.OAuthClients = &oAuthClients{client: client}
|
||||
client.OAuthTokens = &oAuthTokens{client: client}
|
||||
|
|
|
@ -21,6 +21,9 @@ type Variables interface {
|
|||
// Create is used to create a new variable.
|
||||
Create(ctx context.Context, options VariableCreateOptions) (*Variable, error)
|
||||
|
||||
// Read a variable by its ID.
|
||||
Read(ctx context.Context, variableID string) (*Variable, error)
|
||||
|
||||
// Update values of an existing variable.
|
||||
Update(ctx context.Context, variableID string, options VariableUpdateOptions) (*Variable, error)
|
||||
|
||||
|
@ -161,6 +164,27 @@ func (s *variables) Create(ctx context.Context, options VariableCreateOptions) (
|
|||
return v, nil
|
||||
}
|
||||
|
||||
// Read a variable by its ID.
|
||||
func (s *variables) Read(ctx context.Context, variableID string) (*Variable, error) {
|
||||
if !validStringID(&variableID) {
|
||||
return nil, errors.New("Invalid value for variable ID")
|
||||
}
|
||||
|
||||
u := fmt.Sprintf("vars/%s", url.QueryEscape(variableID))
|
||||
req, err := s.client.newRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Variable{}
|
||||
err = s.client.do(ctx, req, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
// VariableUpdateOptions represents the options for updating a variable.
|
||||
type VariableUpdateOptions struct {
|
||||
// For internal use only!
|
||||
|
|
|
@ -1804,10 +1804,10 @@
|
|||
"revisionTime": "2018-07-12T07:51:27Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "Xo/jovk4kg+1xHsdyfTtBhcLkXo=",
|
||||
"checksumSHA1": "V2A92CHPEiPIsU4Wepl0ukznka8=",
|
||||
"path": "github.com/hashicorp/go-tfe",
|
||||
"revision": "6781009f2a64d61df9aff58f17427f0ef43abad0",
|
||||
"revisionTime": "2018-09-08T08:19:18Z"
|
||||
"revision": "cca0c15746d89219f9732f6c07d267827fef25cd",
|
||||
"revisionTime": "2018-09-20T19:42:22Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "85XUnluYJL7F55ptcwdmN8eSOsk=",
|
||||
|
|
Loading…
Reference in New Issue