Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 51 additions & 66 deletions stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Stacks interface {
List(ctx context.Context, organization string, options *StackListOptions) (*StackList, error)

// Read returns a stack by its ID.
Read(ctx context.Context, stackID string, options *StackReadOptions) (*Stack, error)
Read(ctx context.Context, stackID string) (*Stack, error)

// Create creates a new stack.
Create(ctx context.Context, options StackCreateOptions) (*Stack, error)
Expand All @@ -32,8 +32,8 @@ type Stacks interface {
// ForceDelete deletes a stack.
ForceDelete(ctx context.Context, stackID string) error

// UpdateConfiguration updates the configuration of a stack, triggering stack preparation.
UpdateConfiguration(ctx context.Context, stackID string) (*Stack, error)
// FetchConfiguration updates the configuration of a stack, triggering stack preparation.
FetchConfiguration(ctx context.Context, stackID string) (*Stack, error)
}

// stacks implements Stacks.
Expand Down Expand Up @@ -83,22 +83,27 @@ type StackVCSRepoOptions struct {
OAuthTokenID string `json:"oauth-token-id,omitempty"`
}

type LinkedStackConnections struct {
UpstreamCount int `jsonapi:"attr,upstream-count"`
DownstreamCount int `jsonapi:"attr,downstream-count"`
InputsCount int `jsonapi:"attr,inputs-count"`
OutputsCount int `jsonapi:"attr,outputs-count"`
}

// Stack represents a stack.
type Stack struct {
ID string `jsonapi:"primary,stacks"`
Name string `jsonapi:"attr,name"`
Description string `jsonapi:"attr,description"`
DeploymentNames []string `jsonapi:"attr,deployment-names"`
VCSRepo *StackVCSRepo `jsonapi:"attr,vcs-repo"`
ErrorsCount int `jsonapi:"attr,errors-count"`
WarningsCount int `jsonapi:"attr,warnings-count"`
SpeculativeEnabled bool `jsonapi:"attr,speculative-enabled"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`
ID string `jsonapi:"primary,stacks"`
Name string `jsonapi:"attr,name"`
Description string `jsonapi:"attr,description"`
VCSRepo *StackVCSRepo `jsonapi:"attr,vcs-repo"`
SpeculativeEnabled bool `jsonapi:"attr,speculative-enabled"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`
LinkedStackConnections *LinkedStackConnections `jsonapi:"attr,linked-stack-connections"`

// Relationships
AgentPool *AgentPool `jsonapi:"relation,agent-pool"`
Project *Project `jsonapi:"relation,project"`
AgentPool *AgentPool `jsonapi:"relation,agent-pool"`
LatestStackConfiguration *StackConfiguration `jsonapi:"relation,latest-stack-configuration"`
}

Expand All @@ -117,69 +122,49 @@ type StackComponent struct {
Name string `json:"name"`
Correlator string `json:"correlator"`
Expanded bool `json:"expanded"`
Removed bool `json:"removed"`
}

// StackConfiguration represents a stack configuration snapshot
type StackConfiguration struct {
// Attributes
ID string `jsonapi:"primary,stack-configurations"`
Status string `jsonapi:"attr,status"`
StatusTimestamps *StackConfigurationStatusTimestamps `jsonapi:"attr,status-timestamps"`
SequenceNumber int `jsonapi:"attr,sequence-number"`
DeploymentNames []string `jsonapi:"attr,deployment-names"`
ConvergedDeployments []string `jsonapi:"attr,converged-deployments"`
Components []*StackComponent `jsonapi:"attr,components"`
ErrorMessage *string `jsonapi:"attr,error-message"`
EventStreamURL string `jsonapi:"attr,event-stream-url"`
Diagnostics []*StackDiagnostic `jsonapi:"attr,diags"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`

Stack *Stack `jsonapi:"relation,stack"`
}

// StackDeployment represents a stack deployment, specified by configuration
type StackDeployment struct {
// Attributes
ID string `jsonapi:"primary,stack-deployments"`
Name string `jsonapi:"attr,name"`
Status string `jsonapi:"attr,status"`
DeployedAt time.Time `jsonapi:"attr,deployed-at,iso8601"`
ErrorsCount int `jsonapi:"attr,errors-count"`
WarningsCount int `jsonapi:"attr,warnings-count"`
PausedCount int `jsonapi:"attr,paused-count"`
ID string `jsonapi:"primary,stack-configurations"`
Status string `jsonapi:"attr,status"`
SequenceNumber int `jsonapi:"attr,sequence-number"`
Components []*StackComponent `jsonapi:"attr,components"`
PreparingEventStreamURL string `jsonapi:"attr,preparing-event-stream-url"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`
Speculative bool `jsonapi:"attr,speculative"`

// Relationships
CurrentStackState *StackState `jsonapi:"relation,current-stack-state"`
Stack *Stack `jsonapi:"relation,stack"`
IngressAttributes *IngressAttributes `jsonapi:"relation,ingress-attributes"`
}

// StackState represents a stack state
type StackState struct {
// Attributes
ID string `jsonapi:"primary,stack-states"`
}
ID string `jsonapi:"primary,stack-states"`
Description string `jsonapi:"attr,description"`
Generation int `jsonapi:"attr,generation"`
Status string `jsonapi:"attr,status"`
Deployment string `jsonapi:"attr,deployment"`
Components string `jsonapi:"attr,components"`
IsCurrent bool `jsonapi:"attr,is-current"`
ResourceInstanceCount int `jsonapi:"attr,resource-instance-count"`

// StackIncludeOpt represents the include options for a stack.
type StackIncludeOpt string

const (
StackIncludeOrganization StackIncludeOpt = "organization"
StackIncludeProject StackIncludeOpt = "project"
StackIncludeLatestStackConfiguration StackIncludeOpt = "latest_stack_configuration"
StackIncludeStackDiagnostics StackIncludeOpt = "stack_diagnostics"
)
// Relationships
Stack *Stack `jsonapi:"relation,stack"`
StackDeploymentRun *StackDeploymentRun `jsonapi:"relation,stack-deployment-run"`
}

// StackListOptions represents the options for listing stacks.
type StackListOptions struct {
ListOptions
ProjectID string `url:"filter[project[id]],omitempty"`
Sort StackSortColumn `url:"sort,omitempty"`
SearchByName string `url:"search[name],omitempty"`
Include []StackIncludeOpt `url:"include,omitempty"`
}

type StackReadOptions struct {
Include []StackIncludeOpt `url:"include,omitempty"`
ProjectID string `url:"filter[project[id]],omitempty"`
Sort StackSortColumn `url:"sort,omitempty"`
SearchByName string `url:"search[name],omitempty"`
}

// StackCreateOptions represents the options for creating a stack. The project
Expand Down Expand Up @@ -217,8 +202,8 @@ type WaitForStatusResult struct {
const minimumPollingIntervalMs = 3000
const maximumPollingIntervalMs = 5000

// UpdateConfiguration fetches the latest configuration of a stack from VCS, triggering stack operations
func (s *stacks) UpdateConfiguration(ctx context.Context, stackID string) (*Stack, error) {
// FetchConfiguration fetches the latest configuration of a stack from VCS, triggering stack operations
func (s *stacks) FetchConfiguration(ctx context.Context, stackID string) (*Stack, error) {
req, err := s.client.NewRequest("POST", fmt.Sprintf("stacks/%s/fetch-latest-from-vcs", url.PathEscape(stackID)), nil)
if err != nil {
return nil, err
Expand Down Expand Up @@ -254,8 +239,8 @@ func (s stacks) List(ctx context.Context, organization string, options *StackLis
}

// Read returns a stack by its ID.
func (s stacks) Read(ctx context.Context, stackID string, options *StackReadOptions) (*Stack, error) {
req, err := s.client.NewRequest("GET", fmt.Sprintf("stacks/%s", url.PathEscape(stackID)), options)
func (s stacks) Read(ctx context.Context, stackID string) (*Stack, error) {
req, err := s.client.NewRequest("GET", fmt.Sprintf("stacks/%s", url.PathEscape(stackID)), nil)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -307,7 +292,7 @@ func (s stacks) Update(ctx context.Context, stackID string, options StackUpdateO

// Delete deletes a stack.
func (s stacks) Delete(ctx context.Context, stackID string) error {
req, err := s.client.NewRequest("POST", fmt.Sprintf("stacks/%s/delete", url.PathEscape(stackID)), nil)
req, err := s.client.NewRequest("DELETE", fmt.Sprintf("stacks/%s", url.PathEscape(stackID)), nil)
if err != nil {
return err
}
Expand All @@ -317,7 +302,7 @@ func (s stacks) Delete(ctx context.Context, stackID string) error {

// ForceDelete deletes a stack that still has deployments.
func (s stacks) ForceDelete(ctx context.Context, stackID string) error {
req, err := s.client.NewRequest("POST", fmt.Sprintf("stacks/%s/force-delete", url.PathEscape(stackID)), nil)
req, err := s.client.NewRequest("DELETE", fmt.Sprintf("stacks/%s?force=true", url.PathEscape(stackID)), nil)
if err != nil {
return err
}
Expand Down
4 changes: 1 addition & 3 deletions stack_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ const (
StackConfigurationStatusQueued StackConfigurationStatus = "queued"
StackConfigurationStatusPreparing StackConfigurationStatus = "preparing"
StackConfigurationStatusEnqueueing StackConfigurationStatus = "enqueueing"
StackConfigurationStatusConverged StackConfigurationStatus = "converged"
StackConfigurationStatusConverging StackConfigurationStatus = "converging"
StackConfigurationStatusErrored StackConfigurationStatus = "errored"
StackConfigurationStatusCanceled StackConfigurationStatus = "canceled"
StackConfigurationStatusCompleted StackConfigurationStatus = "completed"
Expand Down Expand Up @@ -117,7 +115,7 @@ func (s stackConfigurations) AwaitCompleted(ctx context.Context, stackConfigurat
}

return stackConfiguration.Status, nil
}, []string{StackConfigurationStatusConverged.String(), StackConfigurationStatusConverging.String(), StackConfigurationStatusCompleted.String(), StackConfigurationStatusErrored.String(), StackConfigurationStatusCanceled.String()})
}, []string{StackConfigurationStatusCompleted.String(), StackConfigurationStatusErrored.String(), StackConfigurationStatusCanceled.String()})
}

// AwaitStatus generates a channel that will receive the status of the stack configuration as it progresses.
Expand Down
6 changes: 3 additions & 3 deletions stack_configuration_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestStackConfigurationList(t *testing.T) {
stack, err := client.Stacks.Create(ctx, StackCreateOptions{
Name: "test-stack-list",
VCSRepo: &StackVCSRepoOptions{
Identifier: "hashicorp-guides/pet-nulls-stack",
Identifier: "ctrombley/linked-stacks-demo-network",
OAuthTokenID: oauthClient.OAuthTokens[0].ID,
},
Project: &Project{
Expand All @@ -37,12 +37,12 @@ func TestStackConfigurationList(t *testing.T) {
require.NoError(t, err)

// Trigger first stack configuration by updating configuration
_, err = client.Stacks.UpdateConfiguration(ctx, stack.ID)
_, err = client.Stacks.FetchConfiguration(ctx, stack.ID)
require.NoError(t, err)

// Wait a bit and trigger second stack configuration
time.Sleep(2 * time.Second)
_, err = client.Stacks.UpdateConfiguration(ctx, stack.ID)
_, err = client.Stacks.FetchConfiguration(ctx, stack.ID)
require.NoError(t, err)

list, err := client.StackConfigurations.List(ctx, stack.ID, nil)
Expand Down
8 changes: 4 additions & 4 deletions stack_configuration_summary_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestStackConfigurationSummaryList(t *testing.T) {
stack, err := client.Stacks.Create(ctx, StackCreateOptions{
Name: "aa-test-stack",
VCSRepo: &StackVCSRepoOptions{
Identifier: "hashicorp-guides/pet-nulls-stack",
Identifier: "ctrombley/linked-stacks-demo-network",
OAuthTokenID: oauthClient.OAuthTokens[0].ID,
},
Project: &Project{
Expand All @@ -39,7 +39,7 @@ func TestStackConfigurationSummaryList(t *testing.T) {
stack2, err := client.Stacks.Create(ctx, StackCreateOptions{
Name: "bb-test-stack",
VCSRepo: &StackVCSRepoOptions{
Identifier: "hashicorp-guides/pet-nulls-stack",
Identifier: "ctrombley/linked-stacks-demo-network",
OAuthTokenID: oauthClient.OAuthTokens[0].ID,
},
Project: &Project{
Expand All @@ -50,12 +50,12 @@ func TestStackConfigurationSummaryList(t *testing.T) {
require.NotNil(t, stack2)

// Trigger first stack configuration by updating configuration
_, err = client.Stacks.UpdateConfiguration(ctx, stack2.ID)
_, err = client.Stacks.FetchConfiguration(ctx, stack2.ID)
require.NoError(t, err)

// Wait a bit and trigger second stack configuration
time.Sleep(2 * time.Second)
_, err = client.Stacks.UpdateConfiguration(ctx, stack2.ID)
_, err = client.Stacks.FetchConfiguration(ctx, stack2.ID)
require.NoError(t, err)

t.Run("Successful empty list", func(t *testing.T) {
Expand Down
13 changes: 8 additions & 5 deletions stack_deployment_groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@ type StackDeploymentGroups interface {
type DeploymentGroupStatus string

const (
DeploymentGroupStatusPending DeploymentGroupStatus = "pending"
DeploymentGroupStatusDeploying DeploymentGroupStatus = "deploying"
DeploymentGroupStatusSucceeded DeploymentGroupStatus = "succeeded"
DeploymentGroupStatusFailed DeploymentGroupStatus = "failed"
DeploymentGroupStatusAbandoned DeploymentGroupStatus = "abandoned"
DeploymentGroupStatusPending DeploymentGroupStatus = "pending"
DeploymentGroupStatusPreDeploying DeploymentGroupStatus = "pre-deploying"
DeploymentGroupStatusPreDeployingPendingOperator DeploymentGroupStatus = "pending-operator"
DeploymentGroupStatusAcquiringLock DeploymentGroupStatus = "acquiring-lock"
DeploymentGroupStatusDeploying DeploymentGroupStatus = "deploying"
DeploymentGroupStatusSucceeded DeploymentGroupStatus = "succeeded"
DeploymentGroupStatusFailed DeploymentGroupStatus = "failed"
DeploymentGroupStatusAbandoned DeploymentGroupStatus = "abandoned"
)

// stackDeploymentGroups implements StackDeploymentGroups.
Expand Down
35 changes: 17 additions & 18 deletions stack_deployment_groups_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestStackDeploymentGroupsList(t *testing.T) {
stack, err := client.Stacks.Create(ctx, StackCreateOptions{
Name: "test-stack",
VCSRepo: &StackVCSRepoOptions{
Identifier: "hashicorp-guides/pet-nulls-stack",
Identifier: "ctrombley/linked-stacks-demo-network",
OAuthTokenID: oauthClient.OAuthTokens[0].ID,
},
Project: &Project{
Expand All @@ -36,13 +36,12 @@ func TestStackDeploymentGroupsList(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, stack)

stackUpdated, err := client.Stacks.UpdateConfiguration(ctx, stack.ID)
stackUpdated, err := client.Stacks.FetchConfiguration(ctx, stack.ID)
require.NoError(t, err)
require.NotNil(t, stackUpdated)
require.NotEmpty(t, stackUpdated.LatestStackConfiguration.ID)

stackUpdated = pollStackDeployments(t, ctx, client, stackUpdated.ID)
require.NotNil(t, stackUpdated.LatestStackConfiguration)
stackUpdated = pollStackDeploymentGroups(t, ctx, client, stackUpdated.ID)
require.NotEmpty(t, stackUpdated.LatestStackConfiguration.ID)

t.Run("List with valid stack configuration ID", func(t *testing.T) {
sdgl, err := client.StackDeploymentGroups.List(ctx, stackUpdated.LatestStackConfiguration.ID, nil)
Expand Down Expand Up @@ -91,7 +90,7 @@ func TestStackDeploymentGroupsRead(t *testing.T) {
stack, err := client.Stacks.Create(ctx, StackCreateOptions{
Name: "test-stack",
VCSRepo: &StackVCSRepoOptions{
Identifier: "hashicorp-guides/pet-nulls-stack",
Identifier: "ctrombley/linked-stacks-demo-network",
OAuthTokenID: oauthClient.OAuthTokens[0].ID,
},
Project: &Project{
Expand All @@ -102,11 +101,11 @@ func TestStackDeploymentGroupsRead(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, stack)

stackUpdated, err := client.Stacks.UpdateConfiguration(ctx, stack.ID)
stackUpdated, err := client.Stacks.FetchConfiguration(ctx, stack.ID)
require.NoError(t, err)
require.NotNil(t, stackUpdated)

stackUpdated = pollStackDeployments(t, ctx, client, stackUpdated.ID)
stackUpdated = pollStackDeploymentGroups(t, ctx, client, stackUpdated.ID)
require.NotNil(t, stackUpdated.LatestStackConfiguration)

sdgl, err := client.StackDeploymentGroups.List(ctx, stackUpdated.LatestStackConfiguration.ID, nil)
Expand Down Expand Up @@ -143,7 +142,7 @@ func TestStackDeploymentGroupsApproveAllPlans(t *testing.T) {
stack, err := client.Stacks.Create(ctx, StackCreateOptions{
Name: "test-stack",
VCSRepo: &StackVCSRepoOptions{
Identifier: "hashicorp-guides/pet-nulls-stack",
Identifier: "ctrombley/linked-stacks-demo-network",
OAuthTokenID: oauthClient.OAuthTokens[0].ID,
Branch: "main",
},
Expand All @@ -154,15 +153,15 @@ func TestStackDeploymentGroupsApproveAllPlans(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, stack)

stackUpdated, err := client.Stacks.UpdateConfiguration(ctx, stack.ID)
stackUpdated, err := client.Stacks.FetchConfiguration(ctx, stack.ID)
require.NoError(t, err)
require.NotNil(t, stackUpdated)

stack = pollStackDeployments(t, ctx, client, stackUpdated.ID)
require.NotNil(t, stack.LatestStackConfiguration)
stackUpdated = pollStackDeploymentGroups(t, ctx, client, stackUpdated.ID)
require.NotNil(t, stackUpdated.LatestStackConfiguration)

// Get the deployment group ID from the stack configuration
deploymentGroups, err := client.StackDeploymentGroups.List(ctx, stack.LatestStackConfiguration.ID, nil)
deploymentGroups, err := client.StackDeploymentGroups.List(ctx, stackUpdated.LatestStackConfiguration.ID, nil)
require.NoError(t, err)
require.NotNil(t, deploymentGroups)
require.NotEmpty(t, deploymentGroups.Items)
Expand Down Expand Up @@ -190,7 +189,7 @@ func TestStackDeploymentGroupsRerun(t *testing.T) {
stack, err := client.Stacks.Create(ctx, StackCreateOptions{
Name: "test-stack",
VCSRepo: &StackVCSRepoOptions{
Identifier: "hashicorp-guides/pet-nulls-stack",
Identifier: "ctrombley/linked-stacks-demo-network",
OAuthTokenID: oauthClient.OAuthTokens[0].ID,
Branch: "main",
},
Expand All @@ -201,14 +200,14 @@ func TestStackDeploymentGroupsRerun(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, stack)

stackUpdated, err := client.Stacks.UpdateConfiguration(ctx, stack.ID)
stackUpdated, err := client.Stacks.FetchConfiguration(ctx, stack.ID)
require.NoError(t, err)
require.NotNil(t, stackUpdated)

stack = pollStackDeployments(t, ctx, client, stackUpdated.ID)
require.NotNil(t, stack.LatestStackConfiguration)
stackUpdated = pollStackDeploymentGroups(t, ctx, client, stackUpdated.ID)
require.NotNil(t, stackUpdated.LatestStackConfiguration)

deploymentGroups, err := client.StackDeploymentGroups.List(ctx, stack.LatestStackConfiguration.ID, nil)
deploymentGroups, err := client.StackDeploymentGroups.List(ctx, stackUpdated.LatestStackConfiguration.ID, nil)
require.NoError(t, err)
require.NotNil(t, deploymentGroups)
require.NotEmpty(t, deploymentGroups.Items)
Expand Down
Loading
Loading