Skip to content

Commit

Permalink
feat(aws): migrate pulumi provider to parameter store for resource re…
Browse files Browse the repository at this point in the history
…solution (#699)

Migrate from AWS Resource Groups Tagging API to AWS SSM Parameter Store for runtime resource ID resolution.

The AWS Tagging API is typically rate limited, which can impact resource resolution for larger projects. This migration instead stores resource information as a single parameter for improved performance and project scaling.

You can opt out of this change by setting the NITRIC_AWS_RESOURCE_RESOLVER env var to "tagging", which will continue to use the Resource Groups Tagging API for runtime resolution.

Tags are still applied to all resources to avoid breaking changes and maintain support for Resource Groups.

---------

Co-authored-by: Jye Cusch <[email protected]>
Co-authored-by: Ryan Cartwright <[email protected]>
  • Loading branch information
3 people authored Nov 28, 2024
1 parent 3855e86 commit 355828d
Show file tree
Hide file tree
Showing 15 changed files with 587 additions and 65 deletions.
11 changes: 10 additions & 1 deletion cloud/aws/cmd/runtime/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,22 @@ package main

import (
"github.com/nitrictech/nitric/cloud/aws/runtime"
"github.com/nitrictech/nitric/cloud/aws/runtime/env"
"github.com/nitrictech/nitric/cloud/aws/runtime/resource"
"github.com/nitrictech/nitric/core/pkg/logger"
"github.com/nitrictech/nitric/core/pkg/server"
)

func main() {
resolver, err := resource.New()
var resolver resource.AwsResourceResolver
var err error

resolver, err = resource.NewSSMResourceResolver()

if env.NITRIC_AWS_RESOURCE_RESOLVER.String() == "tagging" {
resolver, err = resource.NewTaggedResourceResolver()
}

if err != nil {
logger.Fatalf("could not create aws resource resolver: %v", err)
return
Expand Down
58 changes: 58 additions & 0 deletions cloud/aws/common/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2021 Nitric Technologies Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package common

// AwsResourceName - Provides a type hint for the mapping of Nitric resource names to AWS resource names
type AwsResourceName = string

// AwsResourceArn - Provides a type hint for the mapping of Nitric resource names to AWS resource ARNs
type AwsResourceArn = string

type ApiGateway struct {
Arn string `json:"arn"`
Endpoint string `json:"endpoint"`
}

type Topic struct {
Arn string `json:"arn"`
StateMachineArn string `json:"stateMachineArn"`
}

// ResourceIndex - The resource index for a nitric stack
type ResourceIndex struct {
Buckets map[string]AwsResourceArn `json:"buckets"`
Topics map[string]Topic `json:"topics"`
KvStores map[string]AwsResourceArn `json:"kvStores"`
Queues map[string]AwsResourceArn `json:"queues"`
Secrets map[string]AwsResourceArn `json:"secrets"`
Apis map[string]ApiGateway `json:"apis"`
HttpProxies map[string]ApiGateway `json:"httpProxies"`
Websockets map[string]ApiGateway `json:"websockets"`
Schedules map[string]AwsResourceArn `json:"schedules"`
}

func NewResourceIndex() *ResourceIndex {
return &ResourceIndex{
Buckets: make(map[string]AwsResourceName),
Topics: make(map[string]Topic),
KvStores: make(map[string]AwsResourceArn),
Queues: make(map[string]AwsResourceArn),
Secrets: make(map[string]AwsResourceArn),
Apis: make(map[string]ApiGateway),
HttpProxies: make(map[string]ApiGateway),
Websockets: make(map[string]ApiGateway),
Schedules: make(map[string]AwsResourceArn),
}
}
9 changes: 8 additions & 1 deletion cloud/aws/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/rds"
"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/resourcegroups"
"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/s3"
"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/scheduler"
"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/secretsmanager"
"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/sqs"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/codebuild"
Expand Down Expand Up @@ -96,6 +97,7 @@ type NitricAwsPulumiProvider struct {
Websockets map[string]*apigatewayv2.Api
KeyValueStores map[string]*dynamodb.Table
JobDefinitions map[string]*batch.JobDefinition
Schedules map[string]*scheduler.Schedule

provider.NitricDefaultOrder

Expand Down Expand Up @@ -230,7 +232,12 @@ func (a *NitricAwsPulumiProvider) Pre(ctx *pulumi.Context, resources []*pulumix.
}

func (a *NitricAwsPulumiProvider) Post(ctx *pulumi.Context) error {
return a.applyVpcRules(ctx)
err := a.applyVpcRules(ctx)
if err != nil {
return err
}

return a.resourcesStore(ctx)
}

func (a *NitricAwsPulumiProvider) Result(ctx *pulumi.Context) (pulumi.StringOutput, error) {
Expand Down
216 changes: 216 additions & 0 deletions cloud/aws/deploy/resources.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// Copyright 2021 Nitric Technologies Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package deploy

import (
"encoding/json"

"github.com/nitrictech/nitric/cloud/aws/common"
"github.com/pulumi/pulumi-aws/sdk/v5/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v6/go/aws/ssm"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func (a *NitricAwsPulumiProvider) resourcesStore(ctx *pulumi.Context) error {
// Build the AWS resource index from the provider information
// This will be used to store the ARNs/Identifiers of all resources created by the stack
bucketNameMap := pulumi.StringMap{}
for name, bucket := range a.Buckets {
bucketNameMap[name] = bucket.Arn
}

apiArnMap := pulumi.StringMap{}
for name, api := range a.Apis {
apiArnMap[name] = api.Arn
}

apiEndpointMap := pulumi.StringMap{}
for name, api := range a.Apis {
apiEndpointMap[name] = api.ApiEndpoint
}

websocketArnMap := pulumi.StringMap{}
for name, api := range a.Websockets {
apiArnMap[name] = api.Arn
}

websocketEndpointMap := pulumi.StringMap{}
for name, api := range a.Websockets {
apiEndpointMap[name] = api.ApiEndpoint
}

topicArnMap := pulumi.StringMap{}
stateMachArnMap := pulumi.StringMap{}
for name, topic := range a.Topics {
topicArnMap[name] = topic.sns.Arn
stateMachArnMap[name] = topic.sfn.Arn
}

kvStoreArnMap := pulumi.StringMap{}
for name, kvStore := range a.KeyValueStores {
kvStoreArnMap[name] = kvStore.Arn
}

queueArnMap := pulumi.StringMap{}
for name, queue := range a.Queues {
queueArnMap[name] = queue.Arn
}

secretsArnMap := pulumi.StringMap{}
for name, secret := range a.Secrets {
secretsArnMap[name] = secret.Arn
}

httpProxyArnMap := pulumi.StringMap{}
for name, proxy := range a.HttpProxies {
httpProxyArnMap[name] = proxy.Arn
}

httpProxyEndpointMap := pulumi.StringMap{}
for name, proxy := range a.HttpProxies {
httpProxyEndpointMap[name] = proxy.ApiEndpoint
}

// Build the index from the provider information
resourceIndexJson := pulumi.All(
bucketNameMap,
apiArnMap,
apiEndpointMap,
websocketArnMap,
websocketEndpointMap,
topicArnMap,
kvStoreArnMap,
queueArnMap,
secretsArnMap,
stateMachArnMap,
httpProxyArnMap,
httpProxyEndpointMap,
).ApplyT(func(args []interface{}) (string, error) {
bucketNameMap := args[0].(map[string]string)
apiArnMap := args[1].(map[string]string)
apiEndpointMap := args[2].(map[string]string)
websocketArnMap := args[3].(map[string]string)
websocketEndpointMap := args[4].(map[string]string)
topicArnMap := args[5].(map[string]string)
kvStoreArnMap := args[6].(map[string]string)
queueArnMap := args[7].(map[string]string)
secretsArnMap := args[8].(map[string]string)
stateMachArnMap := args[9].(map[string]string)
httpProxyArnMap := args[10].(map[string]string)
httpProxyEndpointMap := args[11].(map[string]string)

index := common.NewResourceIndex()
for name, bucket := range bucketNameMap {
index.Buckets[name] = bucket
}

for name, arn := range apiArnMap {
index.Apis[name] = common.ApiGateway{
Arn: arn,
Endpoint: apiEndpointMap[name],
}
}

for name, arn := range websocketArnMap {
index.Websockets[name] = common.ApiGateway{
Arn: arn,
Endpoint: websocketEndpointMap[name],
}
}

for name, arn := range httpProxyArnMap {
index.HttpProxies[name] = common.ApiGateway{
Arn: arn,
Endpoint: httpProxyEndpointMap[name],
}
}

for name, arn := range topicArnMap {
index.Topics[name] = common.Topic{
Arn: arn,
StateMachineArn: stateMachArnMap[name],
}
}

for name, arn := range kvStoreArnMap {
index.KvStores[name] = arn
}

for name, arn := range queueArnMap {
index.Queues[name] = arn
}

for name, arn := range secretsArnMap {
index.Secrets[name] = arn
}

indexJson, err := json.Marshal(index)
if err != nil {
return "", err
}

return string(indexJson), nil
}).(pulumi.StringOutput)

_, err := ssm.NewParameter(ctx, "nitric-resource-index", &ssm.ParameterArgs{
// Create a deterministic name for the resource index
Name: pulumi.Sprintf("/nitric/%s/resource-index", a.StackId),
DataType: pulumi.String("text"),
Type: pulumi.String("String"),
// Store the nitric resource index serialized as a JSON string
Value: resourceIndexJson,
})
if err != nil {
return err
}

// Create a role that gives read access to the above parameter
policy, err := iam.NewPolicy(ctx, "nitric-index-policy", &iam.PolicyArgs{Policy: pulumi.Sprintf(`{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ssm:GetParameter",
"Resource": "arn:aws:ssm:*:*:parameter/nitric/%s/resource-index"
}
]
}`, a.StackId)},
)
if err != nil {
return err
}

for name, role := range a.LambdaRoles {
_, err = iam.NewRolePolicyAttachment(ctx, name+"NitricIndexAccess", &iam.RolePolicyAttachmentArgs{
PolicyArn: policy.Arn,
Role: role.ID(),
})
if err != nil {
return err
}
}

for name, role := range a.BatchRoles {
_, err = iam.NewRolePolicyAttachment(ctx, name+"NitricIndexAccess", &iam.RolePolicyAttachmentArgs{
PolicyArn: policy.Arn,
Role: role.ID(),
})
if err != nil {
return err
}
}

return nil
}
2 changes: 1 addition & 1 deletion cloud/aws/deploy/schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (a *NitricAwsPulumiProvider) Schedule(ctx *pulumi.Context, parent pulumi.Re
}

// Create a new eventbridge schedule
_, err = scheduler.NewSchedule(ctx, name, &scheduler.ScheduleArgs{
a.Schedules[name], err = scheduler.NewSchedule(ctx, name, &scheduler.ScheduleArgs{
ScheduleExpression: pulumi.String(awsScheduleExpression),
ScheduleExpressionTimezone: pulumi.String(a.AwsConfig.ScheduleTimezone),
FlexibleTimeWindow: &scheduler.ScheduleFlexibleTimeWindowArgs{
Expand Down
9 changes: 5 additions & 4 deletions cloud/aws/deploytf/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ func (a *NitricAwsTerraformProvider) Service(stack cdktf.TerraformStack, name st
}

jsiiEnv := map[string]*string{
"NITRIC_STACK_ID": a.Stack.StackIdOutput(),
"NITRIC_ENVIRONMENT": jsii.String("cloud"),
"MIN_WORKERS": jsii.String(fmt.Sprint(config.Workers)),
"NITRIC_HTTP_PROXY_PORT": jsii.String(fmt.Sprint(3000)),
"NITRIC_STACK_ID": a.Stack.StackIdOutput(),
"NITRIC_ENVIRONMENT": jsii.String("cloud"),
"MIN_WORKERS": jsii.String(fmt.Sprint(config.Workers)),
"NITRIC_HTTP_PROXY_PORT": jsii.String(fmt.Sprint(3000)),
"NITRIC_AWS_RESOURCE_PLUGIN": jsii.String("tagging"),
}

// TODO: Only apply to requesting services
Expand Down
9 changes: 5 additions & 4 deletions cloud/aws/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/avast/retry-go v3.0.0+incompatible
github.com/aws/aws-lambda-go v1.34.1
github.com/aws/aws-sdk-go v1.44.298
github.com/aws/aws-sdk-go-v2 v1.30.4
github.com/aws/aws-sdk-go-v2 v1.32.5
github.com/aws/aws-sdk-go-v2/config v1.27.4
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.13.6
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.7.6
Expand All @@ -24,7 +24,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sqs v1.31.1
github.com/aws/constructs-go/constructs/v10 v10.3.0
github.com/aws/jsii-runtime-go v1.99.0
github.com/aws/smithy-go v1.20.4
github.com/aws/smithy-go v1.22.1
github.com/cdktf/cdktf-provider-aws-go/aws/v19 v19.20.1
github.com/cdktf/cdktf-provider-docker-go/docker/v11 v11.0.0
github.com/getkin/kin-openapi v0.113.0
Expand Down Expand Up @@ -90,8 +90,8 @@ require (
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.4 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.2 // indirect
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.20.1 // indirect
Expand All @@ -100,6 +100,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ssm v1.55.6 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.1 // indirect
Expand Down
Loading

0 comments on commit 355828d

Please sign in to comment.