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
Binary file modified bun.lockb
Binary file not shown.
15 changes: 15 additions & 0 deletions cmd/sst/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ var CmdDeploy = &cli.Command{
"sst deploy --dev",
"```",
"The `--dev` flag will deploy your resources as if you were running `sst dev`.",
"",
"```bash frame=\"none\"",
"sst deploy --refresh",
"```",
"Pass --refresh flag to refresh your state resources before deploying any changes.",
"This is useful for making sure your state reflects your cloud resources accurately before applying updates.",
}, "\n"),
},
Flags: []cli.Flag{
Expand All @@ -105,6 +111,14 @@ var CmdDeploy = &cli.Command{
Long: "Deploy resources like `sst dev` would.",
},
},
{
Name: "refresh",
Type: "bool",
Description: cli.Description{
Short: "Refresh state before deploying",
Long: "Refresh your state before deploying.",
},
},
},
Examples: []cli.Example{
{
Expand Down Expand Up @@ -156,6 +170,7 @@ var CmdDeploy = &cli.Command{
ServerPort: s.Port,
Verbose: c.Bool("verbose"),
Continue: c.Bool("continue"),
Refresh: c.Bool("refresh"),
})
if err != nil {
return err
Expand Down
95 changes: 95 additions & 0 deletions pkg/project/preflight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package project

import (
"strings"

"github.com/Masterminds/semver/v3"
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
)

type migrationNotice struct {
providerName string
fromVersion string
toVersion string
message string
}

var migrationNotices = []migrationNotice{
{
providerName: "aws",
fromVersion: "6.0.0",
toVersion: "7.0.0",
message: "Detected AWS provider upgrade to v7.\n\nUpgrading from v6 to v7 introduces some breaking changes and requires state migration before deploying. Refer to the SST release notes and the pulumi migration guide to make the relevent changes before deploying: https://www.pulumi.com/registry/packages/aws/how-to-guides/7-0-migration \n\nThis notice will clear after migrating your state",
},
}

func (p *Project) checkProviderUpgrade(resources []apitype.ResourceV3) []string {
providerVersions := make(map[string]string)

// We iterate backwards over the slice b/c when multiple provider versions are present (i.e. just after refreshing but before deploying)
// the old provider version appears at the end of the array, and we want to override the version checkpoint with the new version.
for i := len(resources) - 1; i >= 0; i-- {
v := resources[i]
name := strings.TrimPrefix(string(v.Type), "pulumi:providers:")
if name == string(v.Type) {
continue
}
versionOutput, ok := v.Outputs["version"].(string)
if !ok {
continue
}
providerVersions[name] = versionOutput
}

var messages []string

for _, entry := range p.lock {
currentVersionStr, ok := providerVersions[entry.Name]
if !ok {
continue
}

currentVersion, err := semver.NewVersion(currentVersionStr)
if err != nil {
continue
}

targetVersion, err := semver.NewVersion(entry.Version)
if err != nil {
continue
}

// Check if this is an upgrade
if !currentVersion.LessThan(targetVersion) {
continue
}

// Check against all applicable upgrade rules
for _, rule := range migrationNotices {
if rule.providerName != entry.Name {
continue
}

ruleFrom, err := semver.NewVersion(rule.fromVersion)
if err != nil {
continue
}

ruleTo, err := semver.NewVersion(rule.toVersion)
if err != nil {
continue
}

// Check if the upgrade path crosses this rule's version range
// Current version must be >= fromVersion AND < toVersion
// Target version must be >= toVersion
if currentVersion.Compare(ruleFrom) >= 0 &&
currentVersion.Compare(ruleTo) < 0 &&
targetVersion.Compare(ruleTo) >= 0 {
messages = append(messages, rule.message)
}
}
}

return messages
}
11 changes: 10 additions & 1 deletion pkg/project/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ func (p *Project) RunNext(ctx context.Context, input *StackInput) error {
}

if input.Command == "deploy" || input.Command == "diff" {
upgradeMsgs := p.checkProviderUpgrade(completed.Resources)

if len(upgradeMsgs) > 0 && !input.Refresh {
return util.NewReadableError(nil, strings.Join(upgradeMsgs, "\n\n"))
}

for provider, opts := range p.app.Providers {
for key, value := range opts.(map[string]interface{}) {
switch v := value.(type) {
Expand All @@ -315,9 +321,12 @@ func (p *Project) RunNext(ctx context.Context, input *StackInput) error {
case "diff":
args = append([]string{"preview"}, args...)
case "refresh":
args = append([]string{"refresh", "--yes"}, args...)
args = append([]string{"refresh", "--yes", "--run-program"}, args...)
case "deploy":
args = append([]string{"up", "--yes", "-f"}, args...)
if input.Refresh {
args = append(args, "--refresh", "--run-program")
}
case "remove":
args = append([]string{"destroy", "--yes", "-f"}, args...)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/project/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type StackInput struct {
Verbose bool
Continue bool
SkipHash string
Refresh bool
}

type ConcurrentUpdateEvent struct{}
Expand Down
2 changes: 1 addition & 1 deletion platform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@aws-sdk/client-ssm": "3.478.0",
"@aws-sdk/client-sts": "3.478.0",
"@aws-sdk/middleware-retry": "3.374.0",
"@pulumi/aws": "6.66.2",
"@pulumi/aws": "7.12.0",
"@pulumi/cloudflare": "6.10.0",
"@pulumi/command": "1.0.1",
"@pulumi/docker-build": "0.0.8",
Expand Down
178 changes: 89 additions & 89 deletions platform/src/components/aws/apigatewayv1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,73 +539,73 @@ export interface ApiGatewayV1RouteArgs {
auth?: Input<
| false
| {
/**
* Enable IAM authorization for a given API route.
*
* When IAM auth is enabled, clients need to use Signature Version 4 to sign their requests with their AWS credentials.
*/
iam?: Input<boolean>;
/**
* Enable custom Lambda authorization for a given API route. Pass in the authorizer ID.
* @example
* ```js
* {
* auth: {
* custom: myAuthorizer.id
* }
* }
* ```
*
* Where `myAuthorizer` is:
*
* ```js
* const userPool = new aws.cognito.UserPool();
* const myAuthorizer = api.addAuthorizer({
* name: "MyAuthorizer",
* userPools: [userPool.arn]
* });
* ```
*/
custom?: Input<string>;
/**
* Enable Cognito User Pool authorization for a given API route.
*
* @example
* You can configure JWT auth.
*
* ```js
* {
* auth: {
* cognito: {
* authorizer: myAuthorizer.id,
* scopes: ["read:profile", "write:profile"]
* }
* }
* }
* ```
*
* Where `myAuthorizer` is:
*
* ```js
* const userPool = new aws.cognito.UserPool();
*
* const myAuthorizer = api.addAuthorizer({
* name: "MyAuthorizer",
* userPools: [userPool.arn]
* });
* ```
*/
cognito?: Input<{
/**
* Authorizer ID of the Cognito User Pool authorizer.
* Enable IAM authorization for a given API route.
*
* When IAM auth is enabled, clients need to use Signature Version 4 to sign their requests with their AWS credentials.
*/
authorizer: Input<string>;
iam?: Input<boolean>;
/**
* Defines the permissions or access levels that the authorization token grants.
* Enable custom Lambda authorization for a given API route. Pass in the authorizer ID.
* @example
* ```js
* {
* auth: {
* custom: myAuthorizer.id
* }
* }
* ```
*
* Where `myAuthorizer` is:
*
* ```js
* const userPool = new aws.cognito.UserPool();
* const myAuthorizer = api.addAuthorizer({
* name: "MyAuthorizer",
* userPools: [userPool.arn]
* });
* ```
*/
scopes?: Input<Input<string>[]>;
}>;
}
custom?: Input<string>;
/**
* Enable Cognito User Pool authorization for a given API route.
*
* @example
* You can configure JWT auth.
*
* ```js
* {
* auth: {
* cognito: {
* authorizer: myAuthorizer.id,
* scopes: ["read:profile", "write:profile"]
* }
* }
* }
* ```
*
* Where `myAuthorizer` is:
*
* ```js
* const userPool = new aws.cognito.UserPool();
*
* const myAuthorizer = api.addAuthorizer({
* name: "MyAuthorizer",
* userPools: [userPool.arn]
* });
* ```
*/
cognito?: Input<{
/**
* Authorizer ID of the Cognito User Pool authorizer.
*/
authorizer: Input<string>;
/**
* Defines the permissions or access levels that the authorization token grants.
*/
scopes?: Input<Input<string>[]>;
}>;
}
>;
/**
* Specify if an API key is required for the route. By default, an API key is not
Expand Down Expand Up @@ -782,7 +782,7 @@ export class ApiGatewayV1 extends Component implements Link.Linkable {
this.endpointType = endpoint.types;

function normalizeRegion() {
return getRegionOutput(undefined, { parent }).name;
return getRegionOutput(undefined, { parent }).region;
}

function normalizeEndpoint() {
Expand All @@ -798,9 +798,9 @@ export class ApiGatewayV1 extends Component implements Link.Linkable {
? { types: "REGIONAL" as const }
: endpoint.type === "private"
? {
types: "PRIVATE" as const,
vpcEndpointIds: endpoint.vpcEndpointIds,
}
types: "PRIVATE" as const,
vpcEndpointIds: endpoint.vpcEndpointIds,
}
: { types: "EDGE" as const };
});
}
Expand All @@ -825,9 +825,9 @@ export class ApiGatewayV1 extends Component implements Link.Linkable {
public get url() {
return this.apigDomain && this.apiMapping
? all([this.apigDomain.domainName, this.apiMapping.basePath]).apply(
([domain, key]) =>
key ? `https://${domain}/${key}/` : `https://${domain}`,
)
([domain, key]) =>
key ? `https://${domain}/${key}/` : `https://${domain}`,
)
: interpolate`https://${this.api.id}.execute-api.${this.region}.amazonaws.com/${$app.stage}/`;
}

Expand Down Expand Up @@ -1577,28 +1577,28 @@ export class ApiGatewayV1 extends Component implements Link.Linkable {
return all([domain, endpointType]).apply(([domain, endpointType]) =>
domain.nameId
? apigateway.DomainName.get(
`${name}DomainName`,
domain.nameId,
{},
{ parent },
)
: new apigateway.DomainName(
...transform(
args.transform?.domainName,
`${name}DomainName`,
{
domainName: domain?.name,
endpointConfiguration: { types: endpointType },
...(endpointType === "REGIONAL"
? {
regionalCertificateArn:
certificateArn as Output<string>,
}
: { certificateArn: certificateArn as Output<string> }),
},
domain.nameId,
{},
{ parent },
)
: new apigateway.DomainName(
...transform(
args.transform?.domainName,
`${name}DomainName`,
{
domainName: domain?.name,
endpointConfiguration: { types: endpointType },
...(endpointType === "REGIONAL"
? {
regionalCertificateArn:
certificateArn as Output<string>,
}
: { certificateArn: certificateArn as Output<string> }),
},
{ parent },
),
),
),
);
}

Expand Down
Loading