Skip to content
Draft
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
2 changes: 2 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ var (

ErrRequiredProjectID = errors.New("project ID is required")

ErrRequiredStackID = errors.New("stack ID is required")

ErrWorkspacesRequired = errors.New("workspaces is required")

ErrWorkspaceMinLimit = errors.New("must provide at least one workspace")
Expand Down
80 changes: 80 additions & 0 deletions variable_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,17 @@ type VariableSets interface {
// Remove variable set from projects in the supplied list.
RemoveFromProjects(ctx context.Context, variableSetID string, options VariableSetRemoveFromProjectsOptions) error

// Apply variable set to stacks in the supplied list.
ApplyToStacks(ctx context.Context, variableSetID string, options *VariableSetApplyToStacksOptions) error

// Remove variable set from stacks in the supplied list.
RemoveFromStacks(ctx context.Context, variableSetID string, options *VariableSetRemoveFromStacksOptions) error

// Update list of workspaces to which the variable set is applied to match the supplied list.
UpdateWorkspaces(ctx context.Context, variableSetID string, options *VariableSetUpdateWorkspacesOptions) (*VariableSet, error)

// Update list of stacks to which the variable set is applied to match the supplied list.
// UpdateStacks(ctx context.Context, variableSetID string, options *VariableSetUpdateStacksOptions) (*VariableSet, error)
}

// variableSets implements VariableSets.
Expand Down Expand Up @@ -87,6 +96,7 @@ type VariableSet struct {
Parent *Parent `jsonapi:"polyrelation,parent"`
Workspaces []*Workspace `jsonapi:"relation,workspaces,omitempty"`
Projects []*Project `jsonapi:"relation,projects,omitempty"`
Stacks []*Stack `jsonapi:"relation,stacks,omitempty"`
Variables []*VariableSetVariable `jsonapi:"relation,vars,omitempty"`
}

Expand All @@ -98,6 +108,7 @@ const (
VariableSetWorkspaces VariableSetIncludeOpt = "workspaces"
VariableSetProjects VariableSetIncludeOpt = "projects"
VariableSetVars VariableSetIncludeOpt = "vars"
VariableSetStacks VariableSetIncludeOpt = "stacks"
)

// VariableSetListOptions represents the options for listing variable sets.
Expand Down Expand Up @@ -191,6 +202,18 @@ type VariableSetRemoveFromProjectsOptions struct {
Projects []*Project
}

// VariableSetApplyToStacksOptions represents the options for applying variable sets to stacks.
type VariableSetApplyToStacksOptions struct {
// The stacks to apply the variable set to (additive).
Stacks []*Stack
}

// VariableSetRemoveFromStacksOptions represents the options for removing variable sets from stacks.
type VariableSetRemoveFromStacksOptions struct {
// The stacks to remove the variable set from.
Stacks []*Stack
}

// VariableSetUpdateWorkspacesOptions represents a subset of update options specifically for applying variable sets to workspaces
type VariableSetUpdateWorkspacesOptions struct {
// Type is a public field utilized by JSON:API to
Expand Down Expand Up @@ -444,6 +467,45 @@ func (s variableSets) RemoveFromProjects(ctx context.Context, variableSetID stri
return req.Do(ctx, nil)
}

// ApplyToStacks applies the variable set to stacks in the supplied list.
// This method will return an error if the variable set has global = true.
func (s *variableSets) ApplyToStacks(ctx context.Context, variableSetID string, options *VariableSetApplyToStacksOptions) error {
if !validStringID(&variableSetID) {
return ErrInvalidVariableSetID
}
if err := options.valid(); err != nil {
return err
}

u := fmt.Sprintf("varsets/%s/relationships/stacks", url.PathEscape(variableSetID))
req, err := s.client.NewRequest("POST", u, options.Stacks)
if err != nil {
return err
}
a := req.Do(ctx, nil)
fmt.Printf("a: %v\n", a)
return a
}

// RemoveFromStacks removes the variable set from stacks in the supplied list.
// This method will return an error if the variable set has global = true.
func (s *variableSets) RemoveFromStacks(ctx context.Context, variableSetID string, options *VariableSetRemoveFromStacksOptions) error {
if !validStringID(&variableSetID) {
return ErrInvalidVariableSetID
}
if err := options.valid(); err != nil {
return err
}

u := fmt.Sprintf("varsets/%s/relationships/stacks", url.PathEscape(variableSetID))
req, err := s.client.NewRequest("DELETE", u, options.Stacks)
if err != nil {
return err
}

return req.Do(ctx, nil)
}

// Update variable set to be applied to only the workspaces in the supplied list.
func (s *variableSets) UpdateWorkspaces(ctx context.Context, variableSetID string, options *VariableSetUpdateWorkspacesOptions) (*VariableSet, error) {
if err := options.valid(); err != nil {
Expand Down Expand Up @@ -525,6 +587,24 @@ func (o VariableSetRemoveFromProjectsOptions) valid() error {
return nil
}

func (o VariableSetApplyToStacksOptions) valid() error {
for _, s := range o.Stacks {
if !validStringID(&s.ID) {
return ErrRequiredStackID
}
}
return nil
}

func (o VariableSetRemoveFromStacksOptions) valid() error {
for _, s := range o.Stacks {
if !validStringID(&s.ID) {
return ErrRequiredStackID
}
}
return nil
}

func (o *VariableSetUpdateWorkspacesOptions) valid() error {
if o == nil || o.Workspaces == nil {
return ErrRequiredWorkspacesList
Expand Down
152 changes: 152 additions & 0 deletions variable_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -564,6 +565,157 @@
})
}

func TestVariableSetsApplyToAndRemoveFromStacks(t *testing.T) {
client := testClient(t)
ctx := context.Background()

t.Log("Creating organization...")
orgTest, orgTestCleanup := createOrganization(t, client)
t.Cleanup(orgTestCleanup)
t.Logf("Created org: %s", orgTest.Name)

t.Log("Creating variable set...")
vsTest, vsTestCleanup := createVariableSet(t, client, orgTest, VariableSetCreateOptions{})
t.Cleanup(vsTestCleanup)
t.Logf("Created variable set: %s", vsTest.ID)

t.Log("Creating OAuth client...")
oauthClient, cleanup := createOAuthClient(t, client, orgTest, nil)
t.Cleanup(cleanup)
t.Log("Created OAuth client")

t.Log("Creating first stack...")
stackTest1, err := client.Stacks.Create(ctx, StackCreateOptions{
Name: "test-stack-1",
VCSRepo: &StackVCSRepoOptions{
Identifier: "nithishravindra/pet-nulls-stack",
OAuthTokenID: oauthClient.OAuthTokens[0].ID,
},
Project: &Project{
ID: orgTest.DefaultProject.ID,
},
})
require.NoError(t, err)
t.Logf("Created stack 1: %s", stackTest1.ID)
t.Cleanup(func() {
if err := client.Stacks.Delete(ctx, stackTest1.ID); err != nil {
t.Logf("Failed to cleanup stack %s: %v", stackTest1.ID, err)
}
})

// Wait for stack to be ready by triggering configuration update
_, err = client.Stacks.UpdateConfiguration(ctx, stackTest1.ID)

Check failure on line 607 in variable_set_test.go

View workflow job for this annotation

GitHub Actions / Lint

ineffectual assignment to err (ineffassign)
// Don't require this to succeed as it might not be needed

stackTest2, err := client.Stacks.Create(ctx, StackCreateOptions{
Name: "test-stack-2",
VCSRepo: &StackVCSRepoOptions{
Identifier: "nithishravindra/pet-nulls-stack",
OAuthTokenID: oauthClient.OAuthTokens[0].ID,
},
Project: &Project{
ID: orgTest.DefaultProject.ID,
},
})
require.NoError(t, err)
t.Cleanup(func() {
if err := client.Stacks.Delete(ctx, stackTest2.ID); err != nil {
t.Logf("Failed to cleanup stack %s: %v", stackTest2.ID, err)
}
})

// Wait for stack to be ready by triggering configuration update
_, err = client.Stacks.UpdateConfiguration(ctx, stackTest2.ID)
// Don't require this to succeed as it might not be needed

t.Run("with first stack added", func(t *testing.T) {
options := VariableSetApplyToStacksOptions{
Stacks: []*Stack{{ID: stackTest1.ID}}, // Use minimal stack object with just ID
}
fmt.Printf("ctx: %v\n", ctx)
fmt.Printf("vsTest.ID: %v\n", vsTest.ID)
fmt.Printf("vsTest: %v\n", vsTest)
fmt.Printf("options: %v\n", options)
err = client.VariableSets.ApplyToStacks(ctx, vsTest.ID, &options)
fmt.Printf("err: %v\n", err)
require.NoError(t, err)

readOpts := &VariableSetReadOptions{Include: &[]VariableSetIncludeOpt{VariableSetStacks}}
vsAfter, err := client.VariableSets.Read(ctx, vsTest.ID, readOpts)
require.NoError(t, err)

assert.Equal(t, 1, len(vsAfter.Stacks))
assert.Equal(t, stackTest1.ID, vsAfter.Stacks[0].ID)
})

t.Run("with second stack added", func(t *testing.T) {
options := VariableSetApplyToStacksOptions{
Stacks: []*Stack{stackTest2},
}

err := client.VariableSets.ApplyToStacks(ctx, vsTest.ID, &options)
require.NoError(t, err)

readOpts := &VariableSetReadOptions{Include: &[]VariableSetIncludeOpt{VariableSetStacks}}
vsAfter, err := client.VariableSets.Read(ctx, vsTest.ID, readOpts)
require.NoError(t, err)

assert.Equal(t, 2, len(vsAfter.Stacks))
stackIDs := []string{vsAfter.Stacks[0].ID, vsAfter.Stacks[1].ID}
assert.Contains(t, stackIDs, stackTest1.ID)
assert.Contains(t, stackIDs, stackTest2.ID)
})

t.Run("with first stack removed", func(t *testing.T) {
options := VariableSetRemoveFromStacksOptions{
Stacks: []*Stack{stackTest1},
}

err := client.VariableSets.RemoveFromStacks(ctx, vsTest.ID, &options)
require.NoError(t, err)

readOpts := &VariableSetReadOptions{Include: &[]VariableSetIncludeOpt{VariableSetStacks}}
vsAfter, err := client.VariableSets.Read(ctx, vsTest.ID, readOpts)
require.NoError(t, err)

assert.Equal(t, 1, len(vsAfter.Stacks))
assert.Equal(t, stackTest2.ID, vsAfter.Stacks[0].ID)
})

t.Run("when variable set ID is invalid", func(t *testing.T) {
applyOptions := VariableSetApplyToStacksOptions{
Stacks: []*Stack{stackTest1},
}
err := client.VariableSets.ApplyToStacks(ctx, badIdentifier, &applyOptions)
assert.EqualError(t, err, ErrInvalidVariableSetID.Error())

removeOptions := VariableSetRemoveFromStacksOptions{
Stacks: []*Stack{stackTest1},
}
err = client.VariableSets.RemoveFromStacks(ctx, badIdentifier, &removeOptions)
assert.EqualError(t, err, ErrInvalidVariableSetID.Error())
})

t.Run("when stack ID is invalid", func(t *testing.T) {
badStack := &Stack{
ID: badIdentifier,
}

applyOptions := VariableSetApplyToStacksOptions{
Stacks: []*Stack{badStack},
}
err := client.VariableSets.ApplyToStacks(ctx, vsTest.ID, &applyOptions)
assert.EqualError(t, err, ErrRequiredStackID.Error())

removeOptions := VariableSetRemoveFromStacksOptions{
Stacks: []*Stack{badStack},
}
err = client.VariableSets.RemoveFromStacks(ctx, vsTest.ID, &removeOptions)
assert.EqualError(t, err, ErrRequiredStackID.Error())
})

}

Check failure on line 717 in variable_set_test.go

View workflow job for this annotation

GitHub Actions / Lint

unnecessary trailing newline (whitespace)

func TestVariableSetsUpdateWorkspaces(t *testing.T) {
client := testClient(t)
ctx := context.Background()
Expand Down
Loading