-
Notifications
You must be signed in to change notification settings - Fork 4k
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
feat(ecs-service-extensions): Publish Extension #16326
feat(ecs-service-extensions): Publish Extension #16326
Conversation
25a6359
to
df0d027
Compare
Looks really good! Just had some docs nits that you can address or not as you want. Feel free to remove the label and get it merged. |
export interface IPublisher { | ||
publish(taskDefinition: ecs.TaskDefinition): void; | ||
|
||
envVarKey(): string; | ||
|
||
envVarValue(): string; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface doesn't look quite right to me...It's difficult to related envVarKey
and envVarValue
with IPublisher
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I agree, these aren't related to the actual 'publish' functionality that we want to provide the users. But if these aren't added to the interface, we will end up doing instanceof
type checks later in the code and we wanted to avoid such logic and find the most common interface. That's why envVarKey
and envVarValue
were added to the interface itself.
public publish(taskDefinition: ecs.TaskDefinition) { | ||
this.topic.grantPublish(taskDefinition.taskRole); | ||
} | ||
|
||
public envVarKey(): string { | ||
return this.topic.node.id; | ||
} | ||
|
||
public envVarValue(): string { | ||
return this.topic.topicArn; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm these function only contain one line logic. I wonder if we want this extension since it doesn't seem to bring a lot of convenience for users. Could you remind me how users are going to publish to a topic without this extension, so that we can better evaluate the value of bring this extra public extension 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This extension helps set up the publish permissions for SNS Topics and optionally restrict subscribe permissions to particular accounts. The simplest use case for setting up the publish permissions can be done by calling topic.grantPublish(taskDefinition.taskRole)
.
This extension might seem to be more useful after we add more IPublisher
resources perhaps? (like publishing to an EventBus)
/** | ||
* An interface that will be implemented by all the resources that can be published events to. | ||
*/ | ||
export interface IPublisher { | ||
publish(taskDefinition: ecs.TaskDefinition): void; | ||
|
||
environmentVar(): { [key: string]: string }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You know I think this is a neat interface, but it doesn't have to do much with being a publisher. For example, adding a DynamoDB table as an extension could be done by implementing this interface as well.
I see @iamhopaul123 's point (#16326 (comment)), what do you think of renaming this extension and interface like this?
export interface Injectable {
environmentVariables(): { [key: string]: string };
}
export interface GrantInjectable extends Injectable {
grant(taskDefinition: ecs.TaskDefinition): void;
}
SNS topics can implement this interface, S3 buckets, or DynamoDB tables for example.
In the future, to add RDS for example as an injected we can create another interface ConnectInjectable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name of of the extension will be: InjecterExtension
.
and it will take as input injectables: []Injectable
Then we can loop through each injectable and do this:
for (const injectable of injectables) {
if (injectable as GrantInjectable).grant !== undefined {
injectable.grant(taskDef)
}
merge(this.environment, injectable.environmentVariables())
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know what y'all think!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes a lot of sense to me! I think overall the concern here is: when we design a new interface, we need to think it as an interface instead of a concrete class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SNS topics can implement this interface, S3 buckets, or DynamoDB tables for example.
In the future, to add RDS for example as an injected we can create another interface ConnectInjectable.
This sounds super cool ❤️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Just some doc recommendations and a small request for the env variables 🚀
@@ -19,7 +19,8 @@ The `Service` construct provided by this module can be extended with optional `S | |||
- [AWS AppMesh](https://aws.amazon.com/app-mesh/) for adding your application to a service mesh | |||
- [Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html), for exposing your service to the public | |||
- [AWS FireLens](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_firelens.html), for filtering and routing application logs | |||
- Queue to allow your service to consume messages from an SQS Queue which is populated by one or more SNS Topics that it is subscribed to | |||
- [Injecter Extension](#injecter-extension), for allowing your service to publish events to resources like SNS Topics |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
- [Injecter Extension](#injecter-extension), for allowing your service to publish events to resources like SNS Topics | |
- [Injecter Extension](#injecter-extension), for allowing your service to connect to other AWS services by granting permission and injecting environment variables. |
@@ -322,9 +323,26 @@ const environment = Environment.fromEnvironmentAttributes(stack, 'Environment', | |||
|
|||
``` | |||
|
|||
## Injecter Extension | |||
|
|||
This service extension accepts a list of `Injectable` resources that the service can publish events or write data to and sets up the corresponding permissions for the service. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanna avoid saying publish events specifically in the first sentence. The Injectable
interface can be implemented by various resource. But we can give an example with InjectableTopic
.
This service extension accepts a list of `Injectable` resources that the service can publish events or write data to and sets up the corresponding permissions for the service. | |
This service extension accepts a list of `Injectable` resources that grants access to the resources and adds the necessary environment variables to the tasks part of the service. | |
For example, an `InjectableTopic` is an SNS topic that grants permission to the task role and adds the topic name as an environment variable to the task definition. |
feel free to change the wording!
|
||
The following example adds the `InjecterExtension` to a `Publisher` Service which can publish events to an SNS Topic and adds the `QueueExtension` to a `Worker` Service which can poll its `eventsQueue` to consume messages populated by the topic. | ||
|
||
```ts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome example! 🤩
|
||
for (const injectable of this.props.injectables) { | ||
for (const [key, val] of Object.entries(injectable.environmentVariables())) { | ||
this.environment[`${service.id.toUpperCase()}_${key}`] = val; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so will this create an environment variable like:
PUBLISHER_MYTOPIC_TOPIC_ARN
Was there a reason why you prefered to prefix with the service.id
? I think customers that implement the Injectable
interface might expect their environment variable to appear as is 💭
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it creates PUBLISHER_MYTOPIC_TOPIC_ARN
.
The service.id
was added as a prefix to distinguish between topics in cases of collision, when two topics with same name are created in different services.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotcha, I think since this works a bit different than copi where users create their own topics, I think we should pass the env var as is!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense!
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
6dac16d
to
8e92b07
Compare
Pull request has been modified.
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
This PR adds a new service extension,
PublisherExtension
. This extension can be added to a service to allow it to publish events to SNS Topics. (This PR when paired with #16049 can be used to set up the pub/ sub architecture pattern)It sets up publish permissions for the service to be able to publish events to the topics provided. The user can also provide a list of accounts that will be given permissions to subscribe to the given topics.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license