Support for AWS SNS resources (#272)
Support for AWS SNS Topic and Subscription resources.
This commit is contained in:
parent
15a75c9ee4
commit
033c262918
14 changed files with 685 additions and 106 deletions
33
lib/aws/idl/sns/subscription.go
Normal file
33
lib/aws/idl/sns/subscription.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
package sns
|
||||
|
||||
import (
|
||||
"github.com/pulumi/lumi/pkg/resource/idl"
|
||||
)
|
||||
|
||||
// An Amazon Simple Notification Service (Amazon SNS) topic subscription. For more information, see
|
||||
// http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sns-subscription.html.
|
||||
type Subscription struct {
|
||||
idl.NamedResource
|
||||
// A name for the topic. If you don't specify a name, a unique physical ID will be generated.
|
||||
Topic *Topic `lumi:"topic,replaces"`
|
||||
// The subscription's protocol.
|
||||
Protocol Protocol `lumi:"protocol,replaces"`
|
||||
// The subscription's endpoint (format depends on the protocol).
|
||||
Endpoint string `lumi:"endpoint,replaces"`
|
||||
}
|
||||
|
||||
// The protocols supported by the Amazon Simple Notification Service (Amazon SNS).
|
||||
type Protocol string
|
||||
|
||||
const (
|
||||
HTTSubscription Protocol = "http" // delivery of JSON-encoded message via HTTP POST.
|
||||
HTTPSSubscription Protocol = "https" // delivery of JSON-encoded message via HTTPS POST.
|
||||
EmailSubscription Protocol = "email" // delivery of message via SMTP.
|
||||
EmailJSONSubscription Protocol = "email-json" // delivery of JSON-encoded message via SMTP.
|
||||
SMSSubscription Protocol = "sms" // delivery of message via SMS.
|
||||
SQSSubscription Protocol = "sqs" // delivery of JSON-encoded message to an Amazon SQS queue.
|
||||
ApplicationSubscription Protocol = "application" // delivery of JSON-encoded message to a mobile app or device.
|
||||
LambdaSubscription Protocol = "lambda" // delivery of JSON-encoded message to an AWS Lambda function.
|
||||
)
|
|
@ -14,27 +14,4 @@ type Topic struct {
|
|||
TopicName *string `lumi:"topicName,replaces,optional"`
|
||||
// A developer-defined string that can be used to identify this SNS topic.
|
||||
DisplayName *string `lumi:"displayName,optional"`
|
||||
// The SNS subscriptions (endpoints) for this topic.
|
||||
Subscription *[]TopicSubscription `lumi:"subscription,optional"`
|
||||
}
|
||||
|
||||
type TopicSubscription struct {
|
||||
// The subscription's protocol.
|
||||
Protocol TopicProtocol `lumi:"protocol"`
|
||||
// The subscription's endpoint (format depends on the protocol).
|
||||
Endpoint string `lumi:"endpoint"`
|
||||
}
|
||||
|
||||
// The protocols supported by the Amazon Simple Notification Service (Amazon SNS).
|
||||
type TopicProtocol string
|
||||
|
||||
const (
|
||||
HTTPTopic TopicProtocol = "http" // delivery of JSON-encoded message via HTTP POST.
|
||||
HTTPSTopic TopicProtocol = "https" // delivery of JSON-encoded message via HTTPS POST.
|
||||
EmailTopic TopicProtocol = "email" // delivery of message via SMTP.
|
||||
EmailJSONTopic TopicProtocol = "email-json" // delivery of JSON-encoded message via SMTP.
|
||||
SMSTopic TopicProtocol = "sms" // delivery of message via SMS.
|
||||
SQSTopic TopicProtocol = "sqs" // delivery of JSON-encoded message to an Amazon SQS queue.
|
||||
ApplicationTopic TopicProtocol = "application" // delivery of JSON-encoded message to a mobile app or device.
|
||||
LambdaTopic TopicProtocol = "lambda" // delivery of JSON-encoded message to an AWS Lambda function.
|
||||
)
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
/* tslint:disable:ordered-imports variable-name */
|
||||
import * as lumi from "@lumi/lumi";
|
||||
|
||||
import {TopicSubscription} from "../sns/topic";
|
||||
|
||||
export let AverageStatistic: AlarmStatistic = "Average";
|
||||
export let BitsMetric: AlarmMetric = "Bits";
|
||||
export let BitsPerSecondMetric: AlarmMetric = "Bits/Second";
|
||||
|
@ -46,7 +44,6 @@ export let ThresholdLessThanOrEqualTo: AlarmComparisonOperator = "LessThanOrEqua
|
|||
export class ActionTarget extends lumi.NamedResource implements ActionTargetArgs {
|
||||
public readonly topicName?: string;
|
||||
public displayName?: string;
|
||||
public subscription?: TopicSubscription[];
|
||||
|
||||
public static get(id: lumi.ID): ActionTarget {
|
||||
return <any>undefined; // functionality provided by the runtime
|
||||
|
@ -61,7 +58,6 @@ export class ActionTarget extends lumi.NamedResource implements ActionTargetArgs
|
|||
if (args !== undefined) {
|
||||
this.topicName = args.topicName;
|
||||
this.displayName = args.displayName;
|
||||
this.subscription = args.subscription;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +65,6 @@ export class ActionTarget extends lumi.NamedResource implements ActionTargetArgs
|
|||
export interface ActionTargetArgs {
|
||||
readonly topicName?: string;
|
||||
displayName?: string;
|
||||
subscription?: TopicSubscription[];
|
||||
}
|
||||
|
||||
export class Alarm extends lumi.NamedResource implements AlarmArgs {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
export * from "./subscription";
|
||||
export * from "./topic";
|
||||
|
||||
|
|
63
lib/aws/pack/sns/subscription.ts
Normal file
63
lib/aws/pack/sns/subscription.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
// *** WARNING: this file was generated by the Lumi IDL Compiler (LUMIDL). ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
/* tslint:disable:ordered-imports variable-name */
|
||||
import * as lumi from "@lumi/lumi";
|
||||
|
||||
import {Topic} from "./topic";
|
||||
|
||||
export let ApplicationSubscription: Protocol = "application";
|
||||
export let EmailJSONSubscription: Protocol = "email-json";
|
||||
export let EmailSubscription: Protocol = "email";
|
||||
export let HTTPSSubscription: Protocol = "https";
|
||||
export let HTTSubscription: Protocol = "http";
|
||||
export let LambdaSubscription: Protocol = "lambda";
|
||||
export let SMSSubscription: Protocol = "sms";
|
||||
export let SQSSubscription: Protocol = "sqs";
|
||||
|
||||
export type Protocol =
|
||||
"application" |
|
||||
"email-json" |
|
||||
"email" |
|
||||
"https" |
|
||||
"http" |
|
||||
"lambda" |
|
||||
"sms" |
|
||||
"sqs";
|
||||
|
||||
export class Subscription extends lumi.NamedResource implements SubscriptionArgs {
|
||||
public readonly topic: Topic;
|
||||
public readonly protocol: Protocol;
|
||||
public readonly endpoint: string;
|
||||
|
||||
public static get(id: lumi.ID): Subscription {
|
||||
return <any>undefined; // functionality provided by the runtime
|
||||
}
|
||||
|
||||
public static query(q: any): Subscription[] {
|
||||
return <any>undefined; // functionality provided by the runtime
|
||||
}
|
||||
|
||||
constructor(name: string, args: SubscriptionArgs) {
|
||||
super(name);
|
||||
if (args.topic === undefined) {
|
||||
throw new Error("Missing required argument 'topic'");
|
||||
}
|
||||
this.topic = args.topic;
|
||||
if (args.protocol === undefined) {
|
||||
throw new Error("Missing required argument 'protocol'");
|
||||
}
|
||||
this.protocol = args.protocol;
|
||||
if (args.endpoint === undefined) {
|
||||
throw new Error("Missing required argument 'endpoint'");
|
||||
}
|
||||
this.endpoint = args.endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
export interface SubscriptionArgs {
|
||||
readonly topic: Topic;
|
||||
readonly protocol: Protocol;
|
||||
readonly endpoint: string;
|
||||
}
|
||||
|
|
@ -4,19 +4,9 @@
|
|||
/* tslint:disable:ordered-imports variable-name */
|
||||
import * as lumi from "@lumi/lumi";
|
||||
|
||||
export let ApplicationTopic: TopicProtocol = "application";
|
||||
export let EmailJSONTopic: TopicProtocol = "email-json";
|
||||
export let EmailTopic: TopicProtocol = "email";
|
||||
export let HTTPSTopic: TopicProtocol = "https";
|
||||
export let HTTPTopic: TopicProtocol = "http";
|
||||
export let LambdaTopic: TopicProtocol = "lambda";
|
||||
export let SMSTopic: TopicProtocol = "sms";
|
||||
export let SQSTopic: TopicProtocol = "sqs";
|
||||
|
||||
export class Topic extends lumi.NamedResource implements TopicArgs {
|
||||
public readonly topicName?: string;
|
||||
public displayName?: string;
|
||||
public subscription?: TopicSubscription[];
|
||||
|
||||
public static get(id: lumi.ID): Topic {
|
||||
return <any>undefined; // functionality provided by the runtime
|
||||
|
@ -31,7 +21,6 @@ export class Topic extends lumi.NamedResource implements TopicArgs {
|
|||
if (args !== undefined) {
|
||||
this.topicName = args.topicName;
|
||||
this.displayName = args.displayName;
|
||||
this.subscription = args.subscription;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,21 +28,5 @@ export class Topic extends lumi.NamedResource implements TopicArgs {
|
|||
export interface TopicArgs {
|
||||
readonly topicName?: string;
|
||||
displayName?: string;
|
||||
subscription?: TopicSubscription[];
|
||||
}
|
||||
|
||||
export type TopicProtocol =
|
||||
"application" |
|
||||
"email-json" |
|
||||
"email" |
|
||||
"https" |
|
||||
"http" |
|
||||
"lambda" |
|
||||
"sms" |
|
||||
"sqs";
|
||||
|
||||
export interface TopicSubscription {
|
||||
protocol: TopicProtocol;
|
||||
endpoint: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/aws/aws-sdk-go/service/lambda"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/aws/aws-sdk-go/service/sns"
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/lumi/pkg/resource/provider"
|
||||
|
@ -38,6 +39,7 @@ type Context struct {
|
|||
iam *iam.IAM
|
||||
lambda *lambda.Lambda
|
||||
s3 *s3.S3
|
||||
sns *sns.SNS
|
||||
}
|
||||
|
||||
const regionConfig = "aws:config:region"
|
||||
|
@ -152,6 +154,14 @@ func (ctx *Context) S3() *s3.S3 {
|
|||
return ctx.s3
|
||||
}
|
||||
|
||||
func (ctx *Context) SNS() *sns.SNS {
|
||||
contract.Assert(ctx.sess != nil)
|
||||
if ctx.sns == nil {
|
||||
ctx.sns = sns.New(ctx.sess)
|
||||
}
|
||||
return ctx.sns
|
||||
}
|
||||
|
||||
// Request manufactures a standard Golang context object for a request within this overall AWS context.
|
||||
func (ctx *Context) Request() context.Context {
|
||||
// IDEA: unify this with the gRPC context; this will be easier once gRPC moves to the standard Golang context.
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/pulumi/lumi/lib/aws/provider/iam"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/lambda"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/s3"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/sns"
|
||||
)
|
||||
|
||||
// Provider implements the AWS resource provider's operations for all known AWS types.
|
||||
|
@ -49,6 +50,8 @@ func NewProvider(host *provider.HostClient) (*Provider, error) {
|
|||
iam.RoleToken: iam.NewRoleProvider(ctx),
|
||||
s3.BucketToken: s3.NewBucketProvider(ctx),
|
||||
s3.ObjectToken: s3.NewObjectProvider(ctx),
|
||||
sns.TopicToken: sns.NewTopicProvider(ctx),
|
||||
sns.SubscriptionToken: sns.NewSubscriptionProvider(ctx),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
70
lib/aws/provider/sns/sns_test.go
Normal file
70
lib/aws/provider/sns/sns_test.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
package sns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
awssns "github.com/aws/aws-sdk-go/service/sns"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/testutil"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/sns"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
prefix := resource.NewUniqueHex("lumitest", 20, 20)
|
||||
ctx := testutil.CreateContext(t)
|
||||
defer func() {
|
||||
err := cleanupTopics(prefix, ctx)
|
||||
assert.Nil(t, err)
|
||||
}()
|
||||
|
||||
resources := map[string]testutil.Resource{
|
||||
"topic": {Provider: NewTopicProvider(ctx), Token: TopicToken},
|
||||
}
|
||||
steps := []testutil.Step{
|
||||
{
|
||||
testutil.ResourceGenerator{
|
||||
Name: "topic",
|
||||
Creator: func(ctx testutil.Context) interface{} {
|
||||
return &sns.Topic{
|
||||
Name: aws.String(prefix),
|
||||
DisplayName: aws.String(prefix),
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
props := testutil.ProviderTest(t, resources, steps)
|
||||
assert.NotNil(t, props)
|
||||
}
|
||||
|
||||
func cleanupTopics(prefix string, ctx *awsctx.Context) error {
|
||||
fmt.Printf("Cleaning up topic with name:%v\n", prefix)
|
||||
list, err := ctx.SNS().ListTopics(&awssns.ListTopicsInput{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cleaned := 0
|
||||
for _, topic := range list.Topics {
|
||||
if strings.Contains(aws.StringValue(topic.TopicArn), prefix) {
|
||||
if _, delerr := ctx.SNS().DeleteTopic(&awssns.DeleteTopicInput{
|
||||
TopicArn: topic.TopicArn,
|
||||
}); delerr != nil {
|
||||
fmt.Printf("Unable to cleanup topic %v: %v\n", topic.TopicArn, delerr)
|
||||
return delerr
|
||||
}
|
||||
cleaned++
|
||||
}
|
||||
}
|
||||
fmt.Printf("Cleaned up %v topics\n", cleaned)
|
||||
return nil
|
||||
}
|
111
lib/aws/provider/sns/subscription.go
Normal file
111
lib/aws/provider/sns/subscription.go
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
package sns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
awssns "github.com/aws/aws-sdk-go/service/sns"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/sns"
|
||||
)
|
||||
|
||||
const SubscriptionToken = sns.SubscriptionToken
|
||||
|
||||
// NewSubscriptionProvider creates a provider that handles SNS subscription operations.
|
||||
func NewSubscriptionProvider(ctx *awsctx.Context) lumirpc.ResourceProviderServer {
|
||||
ops := &subscriptionProvider{ctx}
|
||||
return sns.NewSubscriptionProvider(ops)
|
||||
}
|
||||
|
||||
type subscriptionProvider struct {
|
||||
ctx *awsctx.Context
|
||||
}
|
||||
|
||||
// Check validates that the given property bag is valid for a resource of the given type.
|
||||
func (p *subscriptionProvider) Check(ctx context.Context, obj *sns.Subscription, property string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 *subscriptionProvider) Create(ctx context.Context, obj *sns.Subscription) (resource.ID, error) {
|
||||
topicName, err := arn.ParseResourceName(obj.Topic)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Printf("Creating SNS Subscription on topic '%v'\n", topicName)
|
||||
create := &awssns.SubscribeInput{
|
||||
TopicArn: aws.String(string(obj.Topic)),
|
||||
Endpoint: aws.String(obj.Endpoint),
|
||||
Protocol: aws.String(string(obj.Protocol)),
|
||||
}
|
||||
resp, err := p.ctx.SNS().Subscribe(create)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
contract.Assert(resp != nil)
|
||||
contract.Assert(resp.SubscriptionArn != nil)
|
||||
return resource.ID(*resp.SubscriptionArn), nil
|
||||
}
|
||||
|
||||
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *subscriptionProvider) Get(ctx context.Context, id resource.ID) (*sns.Subscription, error) {
|
||||
resp, err := p.ctx.SNS().GetSubscriptionAttributes(&awssns.GetSubscriptionAttributesInput{
|
||||
SubscriptionArn: aws.String(string(id)),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listResp, err := p.ctx.SNS().ListSubscriptionsByTopic(&awssns.ListSubscriptionsByTopicInput{
|
||||
TopicArn: resp.Attributes["TopicArn"],
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var subscription *awssns.Subscription
|
||||
for _, s := range listResp.Subscriptions {
|
||||
if *s.SubscriptionArn == string(id) {
|
||||
subscription = s
|
||||
}
|
||||
}
|
||||
return &sns.Subscription{
|
||||
Topic: resource.ID(aws.StringValue(resp.Attributes["TopicArn"])),
|
||||
Endpoint: aws.StringValue(subscription.Endpoint),
|
||||
Protocol: sns.Protocol(aws.StringValue(subscription.Protocol)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
func (p *subscriptionProvider) InspectChange(ctx context.Context, id resource.ID,
|
||||
old *sns.Subscription, new *sns.Subscription, diff *resource.ObjectDiff) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Update updates an existing resource with new values. Only those values in the provided property bag are updated
|
||||
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
|
||||
func (p *subscriptionProvider) Update(ctx context.Context, id resource.ID,
|
||||
old *sns.Subscription, new *sns.Subscription, diff *resource.ObjectDiff) error {
|
||||
contract.Failf("No updatable properties on SNS Subscription")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *subscriptionProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Deleting SNS Subscription '%v'\n", name)
|
||||
_, err = p.ctx.SNS().Unsubscribe(&awssns.UnsubscribeInput{
|
||||
SubscriptionArn: id.StringPtr(),
|
||||
})
|
||||
return err
|
||||
}
|
152
lib/aws/provider/sns/topic.go
Normal file
152
lib/aws/provider/sns/topic.go
Normal file
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
package sns
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
awssns "github.com/aws/aws-sdk-go/service/sns"
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/lib/aws/provider/arn"
|
||||
"github.com/pulumi/lumi/lib/aws/provider/awsctx"
|
||||
"github.com/pulumi/lumi/lib/aws/rpc/sns"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
)
|
||||
|
||||
const TopicToken = sns.TopicToken
|
||||
|
||||
// constants for the various topic limits.
|
||||
const (
|
||||
minTopicName = 1
|
||||
maxTopicName = 256
|
||||
displayNameAttributeName = "DisplayName"
|
||||
)
|
||||
|
||||
var (
|
||||
topicNameRegexp = regexp.MustCompile(`^[a-zA-Z0-9_\-]*$`)
|
||||
topicNameDisallowedRegexp = regexp.MustCompile(`[^a-zA-Z0-9_\-]`)
|
||||
)
|
||||
|
||||
// NewTopicProvider creates a provider that handles SNS topic operations.
|
||||
func NewTopicProvider(ctx *awsctx.Context) lumirpc.ResourceProviderServer {
|
||||
ops := &topicProvider{ctx}
|
||||
return sns.NewTopicProvider(ops)
|
||||
}
|
||||
|
||||
type topicProvider struct {
|
||||
ctx *awsctx.Context
|
||||
}
|
||||
|
||||
// Check validates that the given property bag is valid for a resource of the given type.
|
||||
func (p *topicProvider) Check(ctx context.Context, obj *sns.Topic, property string) error {
|
||||
switch property {
|
||||
case sns.Topic_TopicName:
|
||||
if name := obj.TopicName; name != nil {
|
||||
if matched := topicNameRegexp.MatchString(*name); !matched {
|
||||
fmt.Printf("Failed to match regexp\n")
|
||||
return fmt.Errorf("did not match regexp %v", topicNameRegexp)
|
||||
} else if len(*name) < minTopicName {
|
||||
return fmt.Errorf("less than minimum length of %v", minTopicName)
|
||||
} else if len(*name) > maxTopicName {
|
||||
return fmt.Errorf("exceeded maximum length of %v", maxTopicName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 *topicProvider) Create(ctx context.Context, obj *sns.Topic) (resource.ID, error) {
|
||||
// If an explicit name is given, use it. Otherwise, auto-generate a name in part based on the resource name.
|
||||
var name string
|
||||
if obj.TopicName != nil {
|
||||
name = *obj.TopicName
|
||||
} else {
|
||||
// SNS topic names have strict naming requirements. To use the Name property as a prefix, we
|
||||
// need to convert it to a safe form first.
|
||||
safeName := topicNameDisallowedRegexp.ReplaceAllString(*obj.Name, "-")
|
||||
name = resource.NewUniqueHex(safeName+"-", maxTopicName, sha1.Size)
|
||||
}
|
||||
fmt.Printf("Creating SNS Topic '%v' with name '%v'\n", *obj.Name, name)
|
||||
create := &awssns.CreateTopicInput{
|
||||
Name: aws.String(name),
|
||||
}
|
||||
resp, err := p.ctx.SNS().CreateTopic(create)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
contract.Assert(resp != nil)
|
||||
contract.Assert(resp.TopicArn != nil)
|
||||
if obj.DisplayName != nil {
|
||||
_, err := p.ctx.SNS().SetTopicAttributes(&awssns.SetTopicAttributesInput{
|
||||
TopicArn: resp.TopicArn,
|
||||
AttributeName: aws.String(displayNameAttributeName),
|
||||
AttributeValue: obj.DisplayName,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return resource.ID(*resp.TopicArn), nil
|
||||
}
|
||||
|
||||
// Get reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *topicProvider) Get(ctx context.Context, id resource.ID) (*sns.Topic, error) {
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := p.ctx.SNS().GetTopicAttributes(&awssns.GetTopicAttributesInput{
|
||||
TopicArn: aws.String(string(id)),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sns.Topic{
|
||||
TopicName: &name,
|
||||
DisplayName: resp.Attributes[displayNameAttributeName],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InspectChange checks what impacts a hypothetical update will have on the resource's properties.
|
||||
func (p *topicProvider) InspectChange(ctx context.Context, id resource.ID,
|
||||
old *sns.Topic, new *sns.Topic, diff *resource.ObjectDiff) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Update updates an existing resource with new values. Only those values in the provided property bag are updated
|
||||
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
|
||||
func (p *topicProvider) Update(ctx context.Context, id resource.ID,
|
||||
old *sns.Topic, new *sns.Topic, diff *resource.ObjectDiff) error {
|
||||
if diff.Changed(sns.Topic_DisplayName) {
|
||||
_, err := p.ctx.SNS().SetTopicAttributes(&awssns.SetTopicAttributesInput{
|
||||
TopicArn: aws.String(string(id)),
|
||||
AttributeName: aws.String(displayNameAttributeName),
|
||||
AttributeValue: new.DisplayName,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *topicProvider) Delete(ctx context.Context, id resource.ID) error {
|
||||
name, err := arn.ParseResourceName(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Deleting SNS Topic '%v'\n", name)
|
||||
_, err = p.ctx.SNS().DeleteTopic(&awssns.DeleteTopicInput{
|
||||
TopicArn: id.StringPtr(),
|
||||
})
|
||||
return err
|
||||
}
|
|
@ -16,8 +16,6 @@ import (
|
|||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
|
||||
__sns "github.com/pulumi/lumi/lib/aws/rpc/sns"
|
||||
)
|
||||
|
||||
/* RPC stubs for ActionTarget resource provider */
|
||||
|
@ -78,12 +76,6 @@ func (p *ActionTargetProvider) Check(
|
|||
resource.NewPropertyError("ActionTarget", "displayName", failure))
|
||||
}
|
||||
}
|
||||
if !unks["subscription"] {
|
||||
if failure := p.ops.Check(ctx, obj, "subscription"); failure != nil {
|
||||
failures = append(failures,
|
||||
resource.NewPropertyError("ActionTarget", "subscription", failure))
|
||||
}
|
||||
}
|
||||
if len(failures) > 0 {
|
||||
return plugin.NewCheckResponse(resource.NewErrors(failures)), nil
|
||||
}
|
||||
|
@ -208,7 +200,6 @@ type ActionTarget struct {
|
|||
Name *string `lumi:"name,optional"`
|
||||
TopicName *string `lumi:"topicName,optional"`
|
||||
DisplayName *string `lumi:"displayName,optional"`
|
||||
Subscription *[]__sns.TopicSubscription `lumi:"subscription,optional"`
|
||||
}
|
||||
|
||||
// ActionTarget's properties have constants to make dealing with diffs and property bags easier.
|
||||
|
@ -216,7 +207,6 @@ const (
|
|||
ActionTarget_Name = "name"
|
||||
ActionTarget_TopicName = "topicName"
|
||||
ActionTarget_DisplayName = "displayName"
|
||||
ActionTarget_Subscription = "subscription"
|
||||
)
|
||||
|
||||
/* RPC stubs for Alarm resource provider */
|
||||
|
|
242
lib/aws/rpc/sns/subscription.go
Normal file
242
lib/aws/rpc/sns/subscription.go
Normal file
|
@ -0,0 +1,242 @@
|
|||
// *** WARNING: this file was generated by the Lumi IDL Compiler (LUMIDL). ***
|
||||
// *** Do not edit by hand unless you're certain you know what you are doing! ***
|
||||
|
||||
package sns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
pbempty "github.com/golang/protobuf/ptypes/empty"
|
||||
pbstruct "github.com/golang/protobuf/ptypes/struct"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/pulumi/lumi/pkg/resource"
|
||||
"github.com/pulumi/lumi/pkg/resource/plugin"
|
||||
"github.com/pulumi/lumi/pkg/tokens"
|
||||
"github.com/pulumi/lumi/pkg/util/contract"
|
||||
"github.com/pulumi/lumi/pkg/util/mapper"
|
||||
"github.com/pulumi/lumi/sdk/go/pkg/lumirpc"
|
||||
)
|
||||
|
||||
/* RPC stubs for Subscription resource provider */
|
||||
|
||||
// SubscriptionToken is the type token corresponding to the Subscription package type.
|
||||
const SubscriptionToken = tokens.Type("aws:sns/subscription:Subscription")
|
||||
|
||||
// SubscriptionProviderOps is a pluggable interface for Subscription-related management functionality.
|
||||
type SubscriptionProviderOps interface {
|
||||
Check(ctx context.Context, obj *Subscription, property string) error
|
||||
Create(ctx context.Context, obj *Subscription) (resource.ID, error)
|
||||
Get(ctx context.Context, id resource.ID) (*Subscription, error)
|
||||
InspectChange(ctx context.Context,
|
||||
id resource.ID, old *Subscription, new *Subscription, diff *resource.ObjectDiff) ([]string, error)
|
||||
Update(ctx context.Context,
|
||||
id resource.ID, old *Subscription, new *Subscription, diff *resource.ObjectDiff) error
|
||||
Delete(ctx context.Context, id resource.ID) error
|
||||
}
|
||||
|
||||
// SubscriptionProvider is a dynamic gRPC-based plugin for managing Subscription resources.
|
||||
type SubscriptionProvider struct {
|
||||
ops SubscriptionProviderOps
|
||||
}
|
||||
|
||||
// NewSubscriptionProvider allocates a resource provider that delegates to a ops instance.
|
||||
func NewSubscriptionProvider(ops SubscriptionProviderOps) lumirpc.ResourceProviderServer {
|
||||
contract.Assert(ops != nil)
|
||||
return &SubscriptionProvider{ops: ops}
|
||||
}
|
||||
|
||||
func (p *SubscriptionProvider) Check(
|
||||
ctx context.Context, req *lumirpc.CheckRequest) (*lumirpc.CheckResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SubscriptionToken))
|
||||
obj, _, err := p.Unmarshal(req.GetProperties())
|
||||
if err != nil {
|
||||
return plugin.NewCheckResponse(err), nil
|
||||
}
|
||||
var failures []error
|
||||
unks := req.GetUnknowns()
|
||||
if !unks["name"] {
|
||||
if failure := p.ops.Check(ctx, obj, "name"); failure != nil {
|
||||
failures = append(failures,
|
||||
resource.NewPropertyError("Subscription", "name", failure))
|
||||
}
|
||||
}
|
||||
if !unks["topic"] {
|
||||
if failure := p.ops.Check(ctx, obj, "topic"); failure != nil {
|
||||
failures = append(failures,
|
||||
resource.NewPropertyError("Subscription", "topic", failure))
|
||||
}
|
||||
}
|
||||
if !unks["protocol"] {
|
||||
if failure := p.ops.Check(ctx, obj, "protocol"); failure != nil {
|
||||
failures = append(failures,
|
||||
resource.NewPropertyError("Subscription", "protocol", failure))
|
||||
}
|
||||
}
|
||||
if !unks["endpoint"] {
|
||||
if failure := p.ops.Check(ctx, obj, "endpoint"); failure != nil {
|
||||
failures = append(failures,
|
||||
resource.NewPropertyError("Subscription", "endpoint", failure))
|
||||
}
|
||||
}
|
||||
if len(failures) > 0 {
|
||||
return plugin.NewCheckResponse(resource.NewErrors(failures)), nil
|
||||
}
|
||||
return plugin.NewCheckResponse(nil), nil
|
||||
}
|
||||
|
||||
func (p *SubscriptionProvider) Name(
|
||||
ctx context.Context, req *lumirpc.NameRequest) (*lumirpc.NameResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SubscriptionToken))
|
||||
obj, _, err := p.Unmarshal(req.GetProperties())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if obj.Name == nil || *obj.Name == "" {
|
||||
if req.Unknowns[Subscription_Name] {
|
||||
return nil, errors.New("Name property cannot be computed from unknown outputs")
|
||||
}
|
||||
return nil, errors.New("Name property cannot be empty")
|
||||
}
|
||||
return &lumirpc.NameResponse{Name: *obj.Name}, nil
|
||||
}
|
||||
|
||||
func (p *SubscriptionProvider) Create(
|
||||
ctx context.Context, req *lumirpc.CreateRequest) (*lumirpc.CreateResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SubscriptionToken))
|
||||
obj, _, err := p.Unmarshal(req.GetProperties())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id, err := p.ops.Create(ctx, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.CreateResponse{Id: string(id)}, nil
|
||||
}
|
||||
|
||||
func (p *SubscriptionProvider) Get(
|
||||
ctx context.Context, req *lumirpc.GetRequest) (*lumirpc.GetResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SubscriptionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
obj, err := p.ops.Get(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.GetResponse{
|
||||
Properties: plugin.MarshalProperties(
|
||||
nil, resource.NewPropertyMap(obj), plugin.MarshalOptions{}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *SubscriptionProvider) InspectChange(
|
||||
ctx context.Context, req *lumirpc.InspectChangeRequest) (*lumirpc.InspectChangeResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SubscriptionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
new, newprops, err := p.Unmarshal(req.GetNews())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var replaces []string
|
||||
diff := oldprops.Diff(newprops)
|
||||
if diff != nil {
|
||||
if diff.Changed("name") {
|
||||
replaces = append(replaces, "name")
|
||||
}
|
||||
if diff.Changed("topic") {
|
||||
replaces = append(replaces, "topic")
|
||||
}
|
||||
if diff.Changed("protocol") {
|
||||
replaces = append(replaces, "protocol")
|
||||
}
|
||||
if diff.Changed("endpoint") {
|
||||
replaces = append(replaces, "endpoint")
|
||||
}
|
||||
}
|
||||
more, err := p.ops.InspectChange(ctx, id, old, new, diff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lumirpc.InspectChangeResponse{
|
||||
Replaces: append(replaces, more...),
|
||||
}, err
|
||||
}
|
||||
|
||||
func (p *SubscriptionProvider) Update(
|
||||
ctx context.Context, req *lumirpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(SubscriptionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
old, oldprops, err := p.Unmarshal(req.GetOlds())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
new, newprops, err := p.Unmarshal(req.GetNews())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diff := oldprops.Diff(newprops)
|
||||
if err := p.ops.Update(ctx, id, old, new, diff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbempty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (p *SubscriptionProvider) Delete(
|
||||
ctx context.Context, req *lumirpc.DeleteRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(SubscriptionToken))
|
||||
id := resource.ID(req.GetId())
|
||||
if err := p.ops.Delete(ctx, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbempty.Empty{}, nil
|
||||
}
|
||||
|
||||
func (p *SubscriptionProvider) Unmarshal(
|
||||
v *pbstruct.Struct) (*Subscription, resource.PropertyMap, error) {
|
||||
var obj Subscription
|
||||
props := plugin.UnmarshalProperties(nil, v, plugin.MarshalOptions{RawResources: true})
|
||||
return &obj, props, mapper.MapIU(props.Mappable(), &obj)
|
||||
}
|
||||
|
||||
/* Marshalable Subscription structure(s) */
|
||||
|
||||
// Subscription is a marshalable representation of its corresponding IDL type.
|
||||
type Subscription struct {
|
||||
Name *string `lumi:"name,optional"`
|
||||
Topic resource.ID `lumi:"topic"`
|
||||
Protocol Protocol `lumi:"protocol"`
|
||||
Endpoint string `lumi:"endpoint"`
|
||||
}
|
||||
|
||||
// Subscription's properties have constants to make dealing with diffs and property bags easier.
|
||||
const (
|
||||
Subscription_Name = "name"
|
||||
Subscription_Topic = "topic"
|
||||
Subscription_Protocol = "protocol"
|
||||
Subscription_Endpoint = "endpoint"
|
||||
)
|
||||
|
||||
/* Typedefs */
|
||||
|
||||
type (
|
||||
Protocol string
|
||||
)
|
||||
|
||||
/* Constants */
|
||||
|
||||
const (
|
||||
ApplicationSubscription Protocol = "application"
|
||||
EmailJSONSubscription Protocol = "email-json"
|
||||
EmailSubscription Protocol = "email"
|
||||
HTTPSSubscription Protocol = "https"
|
||||
HTTSubscription Protocol = "http"
|
||||
LambdaSubscription Protocol = "lambda"
|
||||
SMSSubscription Protocol = "sms"
|
||||
SQSSubscription Protocol = "sqs"
|
||||
)
|
||||
|
||||
|
|
@ -76,12 +76,6 @@ func (p *TopicProvider) Check(
|
|||
resource.NewPropertyError("Topic", "displayName", failure))
|
||||
}
|
||||
}
|
||||
if !unks["subscription"] {
|
||||
if failure := p.ops.Check(ctx, obj, "subscription"); failure != nil {
|
||||
failures = append(failures,
|
||||
resource.NewPropertyError("Topic", "subscription", failure))
|
||||
}
|
||||
}
|
||||
if len(failures) > 0 {
|
||||
return plugin.NewCheckResponse(resource.NewErrors(failures)), nil
|
||||
}
|
||||
|
@ -206,7 +200,6 @@ type Topic struct {
|
|||
Name *string `lumi:"name,optional"`
|
||||
TopicName *string `lumi:"topicName,optional"`
|
||||
DisplayName *string `lumi:"displayName,optional"`
|
||||
Subscription *[]TopicSubscription `lumi:"subscription,optional"`
|
||||
}
|
||||
|
||||
// Topic's properties have constants to make dealing with diffs and property bags easier.
|
||||
|
@ -214,40 +207,6 @@ const (
|
|||
Topic_Name = "name"
|
||||
Topic_TopicName = "topicName"
|
||||
Topic_DisplayName = "displayName"
|
||||
Topic_Subscription = "subscription"
|
||||
)
|
||||
|
||||
/* Marshalable TopicSubscription structure(s) */
|
||||
|
||||
// TopicSubscription is a marshalable representation of its corresponding IDL type.
|
||||
type TopicSubscription struct {
|
||||
Protocol TopicProtocol `lumi:"protocol"`
|
||||
Endpoint string `lumi:"endpoint"`
|
||||
}
|
||||
|
||||
// TopicSubscription's properties have constants to make dealing with diffs and property bags easier.
|
||||
const (
|
||||
TopicSubscription_Protocol = "protocol"
|
||||
TopicSubscription_Endpoint = "endpoint"
|
||||
)
|
||||
|
||||
/* Typedefs */
|
||||
|
||||
type (
|
||||
TopicProtocol string
|
||||
)
|
||||
|
||||
/* Constants */
|
||||
|
||||
const (
|
||||
ApplicationTopic TopicProtocol = "application"
|
||||
EmailJSONTopic TopicProtocol = "email-json"
|
||||
EmailTopic TopicProtocol = "email"
|
||||
HTTPSTopic TopicProtocol = "https"
|
||||
HTTPTopic TopicProtocol = "http"
|
||||
LambdaTopic TopicProtocol = "lambda"
|
||||
SMSTopic TopicProtocol = "sms"
|
||||
SQSTopic TopicProtocol = "sqs"
|
||||
)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue