-
Notifications
You must be signed in to change notification settings - Fork 0
feat: formless #1
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
Closed
Closed
Changes from 9 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
82d21da
readme
thantos fa34511
deployments work, minus assets
thantos 0560258
pre-retry
thantos b60fb08
improving reliability
thantos 129cc61
things work kind of
thantos 13a4893
end to end lambda stack is working
thantos 466f4a8
fix bug
thantos ed11634
deployment metrics
thantos 103536d
making shit pretty
thantos 45b5182
module handler -> resource provider
thantos 975decf
topo sort BF
thantos cbc2869
use dfs to order deps and display
thantos 8ed3585
hand roll role and queue, fix eb rule
thantos 1ecd3c2
getting the cli started
thantos 0774aa2
event source mapping and secret
thantos File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import { Account, ClientOptions, IAws } from "cdk-assets"; | ||
| import s3_v2 from "aws-sdk/clients/s3"; | ||
| import secrets_manager_v2 from "aws-sdk/clients/secretsmanager"; | ||
| import ecr_v2 from "aws-sdk/clients/ecr"; | ||
| import sts from "@aws-sdk/client-sts"; | ||
| import * as os from "os"; | ||
| import { ChainableTemporaryCredentials, Credentials } from "aws-sdk"; | ||
|
|
||
| export default class AwsClient implements IAws { | ||
| constructor( | ||
| private account: string, | ||
| private region: string, | ||
| private sdkConfig?: any | ||
| ) {} | ||
|
|
||
| async discoverPartition(): Promise<string> { | ||
| return "aws"; | ||
| } | ||
| async discoverDefaultRegion(): Promise<string> { | ||
| return this.region; | ||
| } | ||
| async discoverCurrentAccount(): Promise<Account> { | ||
| return { | ||
| accountId: this.account, | ||
| partition: await this.discoverPartition(), | ||
| }; | ||
| } | ||
| async discoverTargetAccount(options: ClientOptions): Promise<Account> { | ||
| const stsClient = await this.stsClient(await this.awsOptions(options)); | ||
| const response = await stsClient.send(new sts.GetCallerIdentityCommand({})); | ||
| if (!response.Account || !response.Arn) { | ||
| throw new Error( | ||
| `Unrecognized response from STS: '${JSON.stringify(response)}'` | ||
| ); | ||
| } | ||
| return { | ||
| accountId: response.Account!, | ||
| partition: response.Arn!.split(":")[1], | ||
| }; | ||
| } | ||
| async s3Client(options: ClientOptions): Promise<s3_v2> { | ||
| return new s3_v2(options); | ||
| } | ||
| async stsClient(options: ClientOptions): Promise<sts.STSClient> { | ||
| return new sts.STSClient(options); | ||
| } | ||
| async ecrClient(options: ClientOptions): Promise<ecr_v2> { | ||
| return new ecr_v2(options); | ||
| } | ||
| async secretsManagerClient( | ||
| options: ClientOptions | ||
| ): Promise<secrets_manager_v2> { | ||
| return new secrets_manager_v2(await this.awsOptions(options)); | ||
| } | ||
| async awsOptions(options: ClientOptions) { | ||
| let credentials; | ||
| if (options.assumeRoleArn) { | ||
| credentials = await this.assumeRole( | ||
| options.region, | ||
| options.assumeRoleArn, | ||
| options.assumeRoleExternalId | ||
| ); | ||
| } | ||
| return { | ||
| ...this.sdkConfig, | ||
| region: options.region, | ||
| customUserAgent: "node-cfn", | ||
| credentials, | ||
| }; | ||
| } | ||
| /** | ||
| * Explicit manual AssumeRole call | ||
| * | ||
| * Necessary since I can't seem to get the built-in support for ChainableTemporaryCredentials to work. | ||
| * | ||
| * It needs an explicit configuration of `masterCredentials`, we need to put | ||
| * a `DefaultCredentialProverChain()` in there but that is not possible. | ||
| */ | ||
| async assumeRole( | ||
| region: string | undefined, | ||
| roleArn: string, | ||
| externalId?: string | ||
| ): Promise<Credentials> { | ||
| return new ChainableTemporaryCredentials({ | ||
| params: { | ||
| RoleArn: roleArn, | ||
| ExternalId: externalId, | ||
| RoleSessionName: `node-cfn-${safeUsername()}`, | ||
| }, | ||
| stsConfig: { | ||
| region, | ||
| customUserAgent: "node-cfn", | ||
| }, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Return the username with characters invalid for a RoleSessionName removed | ||
| * | ||
| * @see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html#API_AssumeRole_RequestParameters | ||
| */ | ||
| function safeUsername() { | ||
| try { | ||
| return os.userInfo().username.replace(/[^\w+=,.@-]/g, "@"); | ||
| } catch (e) { | ||
| return "noname"; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| // need to define how to create/update/delete a resource of type X when encountered. | ||
| /** | ||
| * registration and discovery - For now, manually register. | ||
| * Default deploy registers, collection of key to object with interfaces. | ||
| * Can use object with more definitions. | ||
| * Separate promise for | ||
| */ | ||
|
|
||
| import { CloudControlHandler } from "./module-handlers/cloud-control"; | ||
| import { EventBusModuleHandler } from "./module-handlers/event-bus"; | ||
| import { InlinePolicyHandler } from "./module-handlers/inline-policy"; | ||
| import { ManagedPolicyHandler } from "./module-handlers/managed-policy"; | ||
| import { QueuePolicyHandler } from "./module-handlers/queue-policy"; | ||
| import type { PhysicalResource, ResourceType } from "./resource"; | ||
|
|
||
| export interface CreateRequest<Properties> { | ||
| logicalId: string; | ||
| resourceType: ResourceType; | ||
| definition: Properties; | ||
| } | ||
|
|
||
| export interface UpdateRequest<Properties> extends CreateRequest<Properties> { | ||
| previous: PhysicalResource<Properties>; | ||
| } | ||
|
|
||
| export interface DeleteRequest<Properties> { | ||
| logicalId: string; | ||
| resourceType: ResourceType; | ||
| physicalId: string; | ||
| previous: PhysicalResource<Properties>; | ||
| /** | ||
| * True when the resource was already snapshot before the delete. | ||
| */ | ||
| snapshotDone: boolean; | ||
| } | ||
|
|
||
| export interface ModuleOperationResultMetadata { | ||
| /** | ||
| * Minimum milliseconds to wait after all deployment operations are complete. | ||
| * | ||
| * If it takes around 10s for a Policy update (paddingMillis: 10000) to be reflected (consistency), but | ||
| * the rest of the deployment only takes 5s, we will wait at least 5s before completing the deployment. | ||
| * | ||
| * TODO: replace with a consistent vs referable API. | ||
| */ | ||
| paddingMillis?: number; | ||
| } | ||
|
|
||
| export type ModuleOperationResult<Properties = any> = Promise< | ||
| | PhysicalResource<Properties> | ||
| | ({ resource: PhysicalResource<Properties> } & ModuleOperationResultMetadata) | ||
| >; | ||
|
|
||
| /** | ||
| * TODO: support optional snapshot. | ||
| */ | ||
| export interface ModuleHandler<Properties = any> { | ||
| create(request: CreateRequest<Properties>): ModuleOperationResult<Properties>; | ||
| update(request: UpdateRequest<Properties>): ModuleOperationResult<Properties>; | ||
| delete( | ||
| request: DeleteRequest<Properties> | ||
| ): Promise<void | ModuleOperationResultMetadata>; | ||
| } | ||
|
|
||
| export type ModuleHandlerInitializer<Properties = any> = ( | ||
| props: ModuleHandlerProps | ||
| ) => ModuleHandler<Properties>; | ||
|
|
||
| export const DEFAULT_MODULE_HANDLER_KEY = "FORMLESS_DEFAULT"; | ||
|
|
||
| export const DefaultModuleHandlers: Record< | ||
| string, | ||
| ModuleHandler | ModuleHandlerInitializer | ||
| > = { | ||
| "AWS::Events::EventBus": (props) => new EventBusModuleHandler(props), | ||
| "AWS::Events::Rule": (props) => new EventBusModuleHandler(props), | ||
| "AWS::IAM::Policy": (props) => new InlinePolicyHandler(props), | ||
| "AWS::IAM::ManagedPolicy": (props) => new ManagedPolicyHandler(props), | ||
| "AWS::SQS::QueuePolicy": (props) => new QueuePolicyHandler(props), | ||
| [DEFAULT_MODULE_HANDLER_KEY]: (props) => new CloudControlHandler(props), | ||
| }; | ||
|
|
||
| export interface ModuleHandlerProps { | ||
| sdkConfig: any; | ||
| account: string; | ||
| region: string; | ||
| } | ||
|
|
||
| export function initModuleHandlers( | ||
| props: ModuleHandlerProps, | ||
| moduleHandlers: Record<string, ModuleHandler | ModuleHandlerInitializer> | ||
| ): Record<string, ModuleHandler> { | ||
| return Object.fromEntries( | ||
| Object.entries(moduleHandlers).map(([key, handler]) => [ | ||
| key, | ||
| typeof handler === "function" ? handler(props) : handler, | ||
| ]) | ||
| ); | ||
| } | ||
|
|
||
| export class ModuleHandlerProvider { | ||
| private readonly __cache: Record<string, ModuleHandler>; | ||
| constructor( | ||
| private props: ModuleHandlerProps, | ||
| private moduleHandlers: Record< | ||
| string, | ||
| ModuleHandler | ModuleHandlerInitializer | ||
| > | ||
| ) { | ||
| this.__cache = {}; | ||
| } | ||
|
|
||
| getHandler(key: string): ModuleHandler<any> { | ||
| if (key in this.__cache) { | ||
| return this.__cache[key]; | ||
| } else if (key in this.moduleHandlers) { | ||
| const handler = this.moduleHandlers[key]; | ||
| return (this.__cache[key] = | ||
| typeof handler === "function" ? handler(this.props) : handler); | ||
| } else if (DEFAULT_MODULE_HANDLER_KEY in this.moduleHandlers) { | ||
| return this.getHandler(DEFAULT_MODULE_HANDLER_KEY); | ||
| } else { | ||
| throw new Error( | ||
| `No handler was found for ${key} and no default handler (${DEFAULT_MODULE_HANDLER_KEY}) was provided.` | ||
| ); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 don't understand the name
ModuleHandler. Is it aProvider?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.
Would be good to research existing solutions before making up your own terms.
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.
Updated