Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable deployment settings in source control #15945

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions pkg/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ type Backend interface {
// SupportsProgress tells whether the backend supports showing whether an operation is currently in progress
SupportsProgress() bool

// SupportsDeployments tells whether it is possible to configure deployments in this backend.
SupportsDeployments() bool

// ParseStackReference takes a string representation and parses it to a reference which may be used for other
// methods in this backend.
ParseStackReference(s string) (StackReference, error)
Expand Down Expand Up @@ -209,6 +212,12 @@ type Backend interface {
// UpdateStackTags updates the stacks's tags, replacing all existing tags.
UpdateStackTags(ctx context.Context, stack Stack, tags map[apitype.StackTagName]string) error

// UpdateStackDeployment updates the stacks's deployment settings.
EncryptStackDeploymentSecret(ctx context.Context, stack Stack, secret string) (string, error)
UpdateStackDeployment(ctx context.Context, stack Stack, deployment apitype.DeploymentSettings) error
GetStackDeployment(ctx context.Context, stack Stack) (*apitype.DeploymentSettings, error)
DestroyStackDeployment(ctx context.Context, stack Stack) error

// ExportDeployment exports the deployment for the given stack as an opaque JSON message.
ExportDeployment(ctx context.Context, stack Stack) (*apitype.UntypedDeployment, error)
// ImportDeployment imports the given deployment into the indicated stack.
Expand Down
31 changes: 31 additions & 0 deletions pkg/backend/diy/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,10 @@ func (b *diyBackend) SupportsProgress() bool {
return false
}

func (b *diyBackend) SupportsDeployments() bool {
return false
}

func (b *diyBackend) ParseStackReference(stackRef string) (backend.StackReference, error) {
return b.parseStackReference(stackRef)
}
Expand Down Expand Up @@ -1350,6 +1354,33 @@ func (b *diyBackend) UpdateStackTags(ctx context.Context,
return errors.New("stack tags not supported in diy mode")
}

// UpdateStackDeployment updates the stacks's deployment settings.
func (b *diyBackend) EncryptStackDeploymentSecret(ctx context.Context,
stack backend.Stack, secret string,
) (string, error) {
// The local backend does not currently persist tags.
return "", errors.New("stack deployments not supported with diy backends")
}

func (b *diyBackend) UpdateStackDeployment(ctx context.Context, stack backend.Stack,
deployment apitype.DeploymentSettings,
) error {
// The local backend does not currently persist tags.
return errors.New("stack deployments not supported with diy backends")
}

func (b *diyBackend) DestroyStackDeployment(ctx context.Context, stack backend.Stack) error {
// The local backend does not currently persist tags.
return errors.New("stack deployments not supported in --local mode")
}

func (b *diyBackend) GetStackDeployment(ctx context.Context,
stack backend.Stack,
) (*apitype.DeploymentSettings, error) {
// The local backend does not currently persist tags.
return nil, errors.New("stack deployments not supported in --local mode")
}

func (b *diyBackend) CancelCurrentUpdate(ctx context.Context, stackRef backend.StackReference) error {
// Try to delete ALL the lock files
allFiles, err := listBucket(ctx, b.bucket, stackLockDir(stackRef.FullyQualifiedName()))
Expand Down
47 changes: 47 additions & 0 deletions pkg/backend/httpstate/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,10 @@ func (b *cloudBackend) SupportsProgress() bool {
return true
}

func (b *cloudBackend) SupportsDeployments() bool {
return true
}

// qualifiedStackReference describes a qualified stack on the Pulumi Service. The Owner or Project
// may be "" if unspecified, e.g. "pulumi/production" specifies the Owner and Name, but not the
// Project. We infer the missing data and try to make things work as best we can in ParseStackReference.
Expand Down Expand Up @@ -1847,6 +1851,49 @@ func (b *cloudBackend) UpdateStackTags(ctx context.Context,
return b.client.UpdateStackTags(ctx, stackID, tags)
}

// UpdateStackDeployment updates the stacks's deployment settings.
func (b *cloudBackend) EncryptStackDeploymentSecret(ctx context.Context,
stack backend.Stack, secret string,
) (string, error) {
stackID, err := b.getCloudStackIdentifier(stack.Ref())
if err != nil {
return "", err
}

return b.client.EncryptStackDeploymentSecret(ctx, stackID, secret)
}

func (b *cloudBackend) UpdateStackDeployment(ctx context.Context, stack backend.Stack,
deployment apitype.DeploymentSettings,
) error {
stackID, err := b.getCloudStackIdentifier(stack.Ref())
if err != nil {
return err
}

return b.client.UpdateStackDeployment(ctx, stackID, deployment)
}

func (b *cloudBackend) DestroyStackDeployment(ctx context.Context, stack backend.Stack) error {
stackID, err := b.getCloudStackIdentifier(stack.Ref())
if err != nil {
return err
}

return b.client.DestroyStackDeployment(ctx, stackID)
}

func (b *cloudBackend) GetStackDeployment(ctx context.Context,
stack backend.Stack,
) (*apitype.DeploymentSettings, error) {
stackID, err := b.getCloudStackIdentifier(stack.Ref())
if err != nil {
return nil, err
}

return b.client.GetStackDeployment(ctx, stackID)
}

const pulumiOperationHeader = "Pulumi operation"

func (b *cloudBackend) RunDeployment(ctx context.Context, stackRef backend.StackReference,
Expand Down
31 changes: 31 additions & 0 deletions pkg/backend/httpstate/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,37 @@ func (pc *Client) UpdateStackTags(
return pc.restCall(ctx, "PATCH", getStackPath(stack, "tags"), nil, tags, nil)
}

func (pc *Client) UpdateStackDeployment(ctx context.Context, stack StackIdentifier,
deployment apitype.DeploymentSettings,
) error {
return pc.restCall(ctx, "POST", getStackPath(stack, "deployments", "settings"), nil, deployment, nil)
}

func (pc *Client) EncryptStackDeploymentSecret(ctx context.Context,
stack StackIdentifier, secret string,
) (string, error) {
request := apitype.SecretValue{}
response := apitype.SecretValue{}
err := pc.restCall(ctx, "POST", getStackPath(stack, "deployments", "settings", "encrypt"), nil, &request, &response)
if err != nil {
return "", err
}

return response.Value, nil
}

func (pc *Client) DestroyStackDeployment(ctx context.Context, stack StackIdentifier) error {
return pc.restCall(ctx, "DELETE", getStackPath(stack, "deployments", "settings"), nil, nil, nil)
}

func (pc *Client) GetStackDeployment(ctx context.Context, stack StackIdentifier) (*apitype.DeploymentSettings, error) {
var response apitype.DeploymentSettings

err := pc.restCall(ctx, "GET", getStackPath(stack, "deployments", "settings"), nil, nil, &response)

return &response, err
}

func getDeploymentPath(stack StackIdentifier, components ...string) string {
prefix := fmt.Sprintf("/api/stacks/%s/%s/%s/deployments", stack.Owner, stack.Project, stack.Stack)
return path.Join(append([]string{prefix}, components...)...)
Expand Down
64 changes: 54 additions & 10 deletions pkg/backend/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type MockBackend struct {
SupportsTagsF func() bool
SupportsOrganizationsF func() bool
SupportsProgressF func() bool
SupportsDeploymentsF func() bool
ParseStackReferenceF func(s string) (StackReference, error)
ValidateStackNameF func(s string) error
DoesProjectExistF func(context.Context, string, string) (bool, error)
Expand All @@ -53,16 +54,20 @@ type MockBackend struct {
RemoveStackF func(context.Context, Stack, bool) (bool, error)
ListStacksF func(context.Context, ListStacksFilter, ContinuationToken) (
[]StackSummary, ContinuationToken, error)
RenameStackF func(context.Context, Stack, tokens.QName) (StackReference, error)
GetStackCrypterF func(StackReference) (config.Crypter, error)
QueryF func(context.Context, QueryOperation) error
GetLatestConfigurationF func(context.Context, Stack) (config.Map, error)
GetHistoryF func(context.Context, StackReference, int, int) ([]UpdateInfo, error)
UpdateStackTagsF func(context.Context, Stack, map[apitype.StackTagName]string) error
ExportDeploymentF func(context.Context, Stack) (*apitype.UntypedDeployment, error)
ImportDeploymentF func(context.Context, Stack, *apitype.UntypedDeployment) error
CurrentUserF func() (string, []string, *workspace.TokenInformation, error)
PreviewF func(context.Context, Stack,
RenameStackF func(context.Context, Stack, tokens.QName) (StackReference, error)
GetStackCrypterF func(StackReference) (config.Crypter, error)
QueryF func(context.Context, QueryOperation) error
GetLatestConfigurationF func(context.Context, Stack) (config.Map, error)
GetHistoryF func(context.Context, StackReference, int, int) ([]UpdateInfo, error)
UpdateStackTagsF func(context.Context, Stack, map[apitype.StackTagName]string) error
ExportDeploymentF func(context.Context, Stack) (*apitype.UntypedDeployment, error)
ImportDeploymentF func(context.Context, Stack, *apitype.UntypedDeployment) error
EncryptStackDeploymentSecretF func(ctx context.Context, stack Stack, secret string) (string, error)
UpdateStackDeploymentF func(context.Context, Stack, apitype.DeploymentSettings) error
DestroyStackDeploymentF func(ctx context.Context, stack Stack) error
GetStackDeploymentF func(context.Context, Stack) (*apitype.DeploymentSettings, error)
CurrentUserF func() (string, []string, *workspace.TokenInformation, error)
PreviewF func(context.Context, Stack,
UpdateOperation) (*deploy.Plan, sdkDisplay.ResourceChanges, result.Result)
UpdateF func(context.Context, Stack,
UpdateOperation) (sdkDisplay.ResourceChanges, result.Result)
Expand Down Expand Up @@ -146,6 +151,13 @@ func (be *MockBackend) SupportsProgress() bool {
panic("not implemented")
}

func (be *MockBackend) SupportsDeployments() bool {
if be.SupportsOrganizationsF != nil {
return be.SupportsDeploymentsF()
}
panic("not implemented")
}

func (be *MockBackend) ParseStackReference(s string) (StackReference, error) {
if be.ParseStackReferenceF != nil {
return be.ParseStackReferenceF(s)
Expand Down Expand Up @@ -374,6 +386,38 @@ func (be *MockBackend) CancelCurrentUpdate(ctx context.Context, stackRef StackRe
panic("not implemented")
}

func (be *MockBackend) EncryptStackDeploymentSecret(ctx context.Context, stack Stack, secret string) (string, error) {
if be.UpdateStackTagsF != nil {
return be.EncryptStackDeploymentSecretF(ctx, stack, secret)
}
panic("not implemented")
}

func (be *MockBackend) UpdateStackDeployment(ctx context.Context, stack Stack,
deployment apitype.DeploymentSettings,
) error {
if be.UpdateStackTagsF != nil {
return be.UpdateStackDeploymentF(ctx, stack, deployment)
}
panic("not implemented")
}

func (be *MockBackend) GetStackDeployment(ctx context.Context,
stack Stack,
) (*apitype.DeploymentSettings, error) {
if be.UpdateStackTagsF != nil {
return be.GetStackDeploymentF(ctx, stack)
}
panic("not implemented")
}

func (be *MockBackend) DestroyStackDeployment(ctx context.Context, stack Stack) error {
if be.UpdateStackTagsF != nil {
return be.DestroyStackDeploymentF(ctx, stack)
}
panic("not implemented")
}

var _ = EnvironmentsBackend((*MockEnvironmentsBackend)(nil))

type MockEnvironmentsBackend struct {
Expand Down
19 changes: 18 additions & 1 deletion pkg/cmd/pulumi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,10 @@ func parseKeyValuePair(pair string) (config.Key, string, error) {
return key, value, nil
}

var stackConfigFile string
var (
stackConfigFile string
stackDeploymentConfigFile string
)

func getProjectStackPath(stack backend.Stack) (string, error) {
if stackConfigFile == "" {
Expand All @@ -822,6 +825,20 @@ func saveProjectStack(stack backend.Stack, ps *workspace.ProjectStack) error {
return ps.Save(stackConfigFile)
}

func loadProjectStackDeployment(stack backend.Stack) (*workspace.ProjectStackDeployment, error) {
if stackDeploymentConfigFile == "" {
return workspace.DetectProjectStackDeployment(stack.Ref().Name().Q())
}
return workspace.LoadProjectStackDeployment(stackDeploymentConfigFile)
}

func saveProjectStackDeployment(psd *workspace.ProjectStackDeployment, stack backend.Stack) error {
if stackDeploymentConfigFile == "" {
return workspace.SaveProjectStackDeployment(stack.Ref().Name().Q(), psd)
}
return psd.Save(stackDeploymentConfigFile)
}

func parseConfigKey(key string) (config.Key, error) {
// As a convenience, we'll treat any key with no delimiter as if:
// <program-name>:<key> had been written instead
Expand Down