Decouple Deployment from Stage in aws.apigateway

The raw AWS API and CloudFormation projection allow
a stage to be created as part of creating a deployment.
This leads to difficulties tracking the ownership of this
extra stage, since it is neither created and owned
seperately, nor is it discoverable after the fact from the
deployment.

We can keep the API simpler by not projecting this feature
of the AWS API into the Lumi resource.  The stage will have
to be created seperately in Lumi, and it's lifecycle is well
understood as a separate Lumi resource.

Fixes #202.
This commit is contained in:
Luke Hoban 2017-06-04 21:34:51 -07:00
parent c2f2fbd2ff
commit 09f2968d18
5 changed files with 9 additions and 148 deletions

View file

@ -24,58 +24,12 @@ import (
type Deployment struct {
idl.NamedResource
// restAPI is the RestAPI resource to deploy.
RestAPI *RestAPI `lumi:"restAPI"`
RestAPI *RestAPI `lumi:"restAPI,replaces"`
// description is a description of the purpose of the API Gateway deployment.
Description *string `lumi:"description,optional"`
// stageDescription configures the stage that API Gateway creates with this deployment.
StageDescription *StageDescription `lumi:"stageDescription,optional"`
// stageName is a name for the stage that API Gateway creates with this deployment. Use only alphanumeric
// characters.
StageName *string `lumi:"stageName,optional"`
// The identifier for the deployment resource.
ID string `lumi:"id,out"`
// The date and time that the deployment resource was created.
CreatedDate string `lumi:"createdDate,out"`
}
type StageDescription struct {
// Indicates whether cache clustering is enabled for the stage.
CacheClusterEnabled *bool `lumi:"cacheClusterEnabled,optional"`
// The size of the stage's cache cluster.
CacheClusterSize *string `lumi:"cacheClusterSize,optional"`
// Indicates whether the cached responses are encrypted.
CacheDataEncrypted *bool `lumi:"cacheDataEncrypted,optional"`
// The time-to-live (TTL) period, in seconds, that specifies how long API Gateway caches responses.
CacheTTLInSeconds *float64 `lumi:"cacheTTLInSeconds,optional"`
// Indicates whether responses are cached and returned for requests. You must enable a cache cluster on the stage
// to cache responses. For more information, see
// http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html.
CachingEnabled *bool `lumi:"cachingEnabled,optional"`
// The client certificate that API Gateway uses to call your integration endpoints in the stage.
ClientCertificate *ClientCertificate `lumi:"clientCertificate,optional"`
// Indicates whether data trace logging is enabled for methods in the stage. API Gateway pushes these logs to Amazon
// CloudWatch Logs.
DataTraceEnabled *bool `lumi:"dataTraceEnabled,optional"`
// A description of the purpose of the stage.
Description *string `lumi:"description,optional"`
// The logging level for this method.
LoggingLevel *LoggingLevel `lumi:"loggingLevel,optional"`
// Configures settings for all of the stage's methods.
MethodSettings *[]MethodSetting `lumi:"methodSettings,optional"`
// Indicates whether Amazon CloudWatch metrics are enabled for methods in the stage.
MetricsEnabled *bool `lumi:"metricsEnabled,optional"`
// The name of the stage, which API Gateway uses as the first path segment in the invoke URI.
StageName *string `lumi:"stageName,optional"`
// The number of burst requests per second that API Gateway permits across all APIs, stages, and methods in your
// AWS account. For more information, see
// http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html.
ThrottlingBurstLimit *float64 `lumi:"throttlingBurstLimit,optional"`
// The number of steady-state requests per second that API Gateway permits across all APIs, stages, and methods in
// your AWS account. For more information, see
// http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html.
ThrottlingRateLimit *float64 `lumi:"throttlingRateLimit,optional"`
// A map that defines the stage variables. Variable names must consist of alphanumeric characters, and the values
// must match the following regular expression: `[A-Za-z0-9-._~:/?#&=,]+`.
Variables *map[string]string `lumi:"variables,optional"`
}

View file

@ -3,16 +3,12 @@
import * as lumi from "@lumi/lumi";
import {ClientCertificate} from "./clientCertificate";
import {LoggingLevel, MethodSetting} from "./method";
import {RestAPI} from "./restAPI";
export class Deployment extends lumi.Resource implements DeploymentArgs {
public readonly name: string;
public restAPI: RestAPI;
public readonly restAPI: RestAPI;
public description?: string;
public stageDescription?: StageDescription;
public stageName?: string;
public id: string;
public createdDate: string;
@ -27,34 +23,12 @@ export class Deployment extends lumi.Resource implements DeploymentArgs {
}
this.restAPI = args.restAPI;
this.description = args.description;
this.stageDescription = args.stageDescription;
this.stageName = args.stageName;
}
}
export interface DeploymentArgs {
restAPI: RestAPI;
readonly restAPI: RestAPI;
description?: string;
stageDescription?: StageDescription;
stageName?: string;
}
export interface StageDescription {
cacheClusterEnabled?: boolean;
cacheClusterSize?: string;
cacheDataEncrypted?: boolean;
cacheTTLInSeconds?: number;
cachingEnabled?: boolean;
clientCertificate?: ClientCertificate;
dataTraceEnabled?: boolean;
description?: string;
loggingLevel?: LoggingLevel;
methodSettings?: MethodSetting[];
metricsEnabled?: boolean;
stageName?: string;
throttlingBurstLimit?: number;
throttlingRateLimit?: number;
variables?: {[key: string]: string};
}

View file

@ -97,6 +97,7 @@ export class API {
}
// TODO[pulumi/lumi#90]: Once we suport output properties, we can use `lambda.lambda.arn` as input
// to constructing this apigateway lambda invocation uri.
// this.swaggerSpec.paths[path][swaggerMethod] = createPathSpec(lambda.lambda.arn);
this.swaggerSpec.paths[path][swaggerMethod] = createPathSpec("arn:aws:lambda:us-east-1:490047557317:function:webapi-test-func");
}

View file

@ -3,7 +3,6 @@
package apigateway
import (
"crypto/sha1"
"fmt"
"strings"
@ -64,13 +63,6 @@ func (p *deploymentProvider) Check(ctx context.Context, obj *apigateway.Deployme
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
func (p *deploymentProvider) Create(ctx context.Context, obj *apigateway.Deployment) (resource.ID, error) {
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
var stageName string
if obj.StageName != nil {
stageName = *obj.StageName
} else {
stageName = resource.NewUniqueHex(*obj.Name+"_", maxDeploymentName, sha1.Size)
}
restAPIID, err := ParseRestAPIID(obj.RestAPI)
if err != nil {
return "", err
@ -79,7 +71,6 @@ func (p *deploymentProvider) Create(ctx context.Context, obj *apigateway.Deploym
create := &awsapigateway.CreateDeploymentInput{
RestApiId: aws.String(restAPIID),
Description: obj.Description,
StageName: aws.String(stageName),
}
deployment, err := p.ctx.APIGateway().CreateDeployment(create)
if err != nil {
@ -107,8 +98,8 @@ func (p *deploymentProvider) Get(ctx context.Context, id resource.ID) (*apigatew
}
return &apigateway.Deployment{
RestAPI: NewRestAPIID(p.ctx.Region(), restAPIID),
ID: aws.StringValue(resp.Id),
Description: resp.Description,
ID: aws.StringValue(resp.Id),
CreatedDate: resp.CreatedDate.String(),
}, nil
}
@ -123,7 +114,7 @@ func (p *deploymentProvider) InspectChange(ctx context.Context, id resource.ID,
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
func (p *deploymentProvider) Update(ctx context.Context, id resource.ID,
old *apigateway.Deployment, new *apigateway.Deployment, diff *resource.ObjectDiff) error {
ops, err := patchOperations(diff, apigateway.Deployment_StageName)
ops, err := patchOperations(diff)
if err != nil {
return err
}
@ -152,24 +143,6 @@ func (p *deploymentProvider) Delete(ctx context.Context, id resource.ID) error {
if err != nil {
return err
}
resp, err := p.ctx.APIGateway().GetStages(&awsapigateway.GetStagesInput{
RestApiId: aws.String(restAPIID),
DeploymentId: aws.String(deploymentID),
})
if err != nil || resp == nil {
return err
}
if len(resp.Item) == 1 {
// Assume that the single stage associated with this deployment
// is the stage that was automatically created along with the deployment.
_, err := p.ctx.APIGateway().DeleteStage(&awsapigateway.DeleteStageInput{
RestApiId: aws.String(restAPIID),
StageName: resp.Item[0].StageName,
})
if err != nil {
return err
}
}
_, err = p.ctx.APIGateway().DeleteDeployment(&awsapigateway.DeleteDeploymentInput{
RestApiId: aws.String(restAPIID),
DeploymentId: aws.String(deploymentID),

View file

@ -123,6 +123,9 @@ func (p *DeploymentProvider) InspectChange(
if diff.Changed("name") {
replaces = append(replaces, "name")
}
if diff.Changed("restAPI") {
replaces = append(replaces, "restAPI")
}
}
more, err := p.ops.InspectChange(ctx, id, old, new, diff)
if err != nil {
@ -177,8 +180,6 @@ type Deployment struct {
Name *string `json:"name,omitempty"`
RestAPI resource.ID `json:"restAPI"`
Description *string `json:"description,omitempty"`
StageDescription *StageDescription `json:"stageDescription,omitempty"`
StageName *string `json:"stageName,omitempty"`
ID string `json:"id,omitempty"`
CreatedDate string `json:"createdDate,omitempty"`
}
@ -188,50 +189,8 @@ const (
Deployment_Name = "name"
Deployment_RestAPI = "restAPI"
Deployment_Description = "description"
Deployment_StageDescription = "stageDescription"
Deployment_StageName = "stageName"
Deployment_ID = "id"
Deployment_CreatedDate = "createdDate"
)
/* Marshalable StageDescription structure(s) */
// StageDescription is a marshalable representation of its corresponding IDL type.
type StageDescription struct {
CacheClusterEnabled *bool `json:"cacheClusterEnabled,omitempty"`
CacheClusterSize *string `json:"cacheClusterSize,omitempty"`
CacheDataEncrypted *bool `json:"cacheDataEncrypted,omitempty"`
CacheTTLInSeconds *float64 `json:"cacheTTLInSeconds,omitempty"`
CachingEnabled *bool `json:"cachingEnabled,omitempty"`
ClientCertificate *resource.ID `json:"clientCertificate,omitempty"`
DataTraceEnabled *bool `json:"dataTraceEnabled,omitempty"`
Description *string `json:"description,omitempty"`
LoggingLevel *LoggingLevel `json:"loggingLevel,omitempty"`
MethodSettings *[]MethodSetting `json:"methodSettings,omitempty"`
MetricsEnabled *bool `json:"metricsEnabled,omitempty"`
StageName *string `json:"stageName,omitempty"`
ThrottlingBurstLimit *float64 `json:"throttlingBurstLimit,omitempty"`
ThrottlingRateLimit *float64 `json:"throttlingRateLimit,omitempty"`
Variables *map[string]string `json:"variables,omitempty"`
}
// StageDescription's properties have constants to make dealing with diffs and property bags easier.
const (
StageDescription_CacheClusterEnabled = "cacheClusterEnabled"
StageDescription_CacheClusterSize = "cacheClusterSize"
StageDescription_CacheDataEncrypted = "cacheDataEncrypted"
StageDescription_CacheTTLInSeconds = "cacheTTLInSeconds"
StageDescription_CachingEnabled = "cachingEnabled"
StageDescription_ClientCertificate = "clientCertificate"
StageDescription_DataTraceEnabled = "dataTraceEnabled"
StageDescription_Description = "description"
StageDescription_LoggingLevel = "loggingLevel"
StageDescription_MethodSettings = "methodSettings"
StageDescription_MetricsEnabled = "metricsEnabled"
StageDescription_StageName = "stageName"
StageDescription_ThrottlingBurstLimit = "throttlingBurstLimit"
StageDescription_ThrottlingRateLimit = "throttlingRateLimit"
StageDescription_Variables = "variables"
)