From 04b791c8ccffeca0e0b61602b3672ab477bcfcaa Mon Sep 17 00:00:00 2001 From: Haim Bell Date: Mon, 9 Sep 2024 11:24:17 +0300 Subject: [PATCH] feat(provisioning-terraform-aws-repository-ecr): add plugin --- package-lock.json | 44 ++++++++ .../.amplicationrc.json | 12 +++ .../.eslintrc.json | 4 + .../.npmignore | 2 + .../.prettierignore | 1 + .../README.md | 23 ++++ .../package.json | 35 ++++++ .../project.json | 6 ++ .../src/constants.ts | 6 ++ .../src/index.ts | 102 ++++++++++++++++++ .../src/static/ecr-template.tf | 28 +++++ .../src/tests/.keep | 0 .../src/types.ts | 12 +++ .../src/utils.ts | 45 ++++++++ .../tsconfig.json | 5 + .../webpack.config.js | 43 ++++++++ 16 files changed, 368 insertions(+) create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/.amplicationrc.json create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/.eslintrc.json create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/.npmignore create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/.prettierignore create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/README.md create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/package.json create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/project.json create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/constants.ts create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/index.ts create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/static/ecr-template.tf create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/tests/.keep create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/types.ts create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/utils.ts create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/tsconfig.json create mode 100644 plugins/dotnet-provisioning-terraform-aws-repository-ecr/webpack.config.js diff --git a/package-lock.json b/package-lock.json index 4459b663a..beb155f91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -231,6 +231,10 @@ "resolved": "plugins/dotnet-provisioning-terraform-aws-database-rds", "link": true }, + "node_modules/@amplication/plugin-dotnet-provisioning-terraform-aws-repository-ecr": { + "resolved": "plugins/dotnet-provisioning-terraform-aws-repository-ecr", + "link": true + }, "node_modules/@amplication/plugin-formatter-prettier": { "resolved": "plugins/formatter-prettier", "link": true @@ -17199,6 +17203,46 @@ "@amplication/csharp-ast": "*" } }, + "plugins/dotnet-provisioning-terraform-aws-repository-ecr": { + "version": "1.0.5", + "license": "Apache-2.0", + "devDependencies": { + "@amplication/code-gen-types": "^2.0.34", + "@amplication/code-gen-utils": "^0.0.9", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "@types/lodash": "^4.14.200", + "@typescript-eslint/eslint-plugin": "^6.9.1", + "@typescript-eslint/parser": "^6.9.1", + "copy-webpack-plugin": "^12.0.2", + "eslint": "^8.52.0", + "jest-mock-extended": "^3.0.5", + "lodash": "^4.17.21", + "prettier": "^2.6.2", + "rimraf": "^5.0.5", + "ts-loader": "^9.5.0", + "typescript": "^5.2.2", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "webpack-node-externals": "^3.0.0" + } + }, + "plugins/dotnet-provisioning-terraform-aws-repository-ecr/node_modules/@amplication/code-gen-types": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/@amplication/code-gen-types/-/code-gen-types-2.0.37.tgz", + "integrity": "sha512-wCQZtj+q/bByN+2D+A96aI3qXaIVcaA5JeYgNZdkzPQqmDAZWSQwp0DlY8i6356E1Y/RpG55q/oziWmQdaqCJw==", + "dev": true, + "dependencies": { + "ast-types": "^0.14.2", + "json-schema": "^0.4.0", + "prisma-schema-dsl-types": "^1.1.2", + "tslib": "^2.6.2", + "type-fest": "^3.11.0" + }, + "peerDependencies": { + "@amplication/csharp-ast": "*" + } + }, "plugins/formatter-prettier": { "name": "@amplication/plugin-formatter-prettier", "version": "1.0.7", diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.amplicationrc.json b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.amplicationrc.json new file mode 100644 index 000000000..d71d4d66c --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.amplicationrc.json @@ -0,0 +1,12 @@ +{ + "settings": { + "repository_name": "", + "repository_type": "private", + "configuration": { + "force_delete": false + } + }, + "systemSettings": { + "requireAuthenticationEntity": "false" + } +} diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.eslintrc.json b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.eslintrc.json new file mode 100644 index 000000000..052a5874e --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"] +} diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.npmignore b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.npmignore new file mode 100644 index 000000000..99ba36b26 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.npmignore @@ -0,0 +1,2 @@ +.prettierignore +.gitignore \ No newline at end of file diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.prettierignore b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.prettierignore new file mode 100644 index 000000000..53c37a166 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/.prettierignore @@ -0,0 +1 @@ +dist \ No newline at end of file diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/README.md b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/README.md new file mode 100644 index 000000000..7a74647a1 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/README.md @@ -0,0 +1,23 @@ +# @amplication/plugin-dotnet-provisioning-terraform-aws-repository-ecr + +[![NPM Downloads](https://img.shields.io/npm/dt/@amplication/plugin-dotnet-provisioning-terraform-aws-repository-ecrc)](https://www.npmjs.com/package/@amplication/plugin-dotnet-provisioning-terraform-aws-repository-ecr) + +Adds terraform code for provisioning Amazon Web Services Elastic Container Registry (ECR) as an addition to the 'core' terraform code base. + +## Purpose + +The purpose of this plugin is to be able to add a terraform code that can be used to provision a container registry within Amazon Elastic Container Registry. + +## Configuration + +`repository_name` determines what the name of the repository for the generated service will be - in the following format: aws_account_id.dkr.ecr.region.amazonaws.com/` + +`repository_type` determines what type the repository will be, this can eiter be `public` or `private`. + +`configuration` adds additional configuration to the container repository. + +`configuration.force_delete` determines if the repository will be deleted on a terraform destroy - even if it contains container images. + +## Usage + +As this is an addition to the code base, where non of the other code is touched, using the plugin won't impact the final build. diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/package.json b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/package.json new file mode 100644 index 000000000..c17966932 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/package.json @@ -0,0 +1,35 @@ +{ + "name": "@amplication/plugin-dotnet-provisioning-terraform-aws-repository-ecr", + "version": "1.0.0", + "description": "Adds a container registry setup based on terraform for Amazon Web Services to the generated service.", + "main": "dist/index.js", + "nx": {}, + "scripts": { + "prepublishOnly": "npm run build", + "dev": "webpack --watch", + "build": "webpack", + "prebuild": "rimraf dist" + }, + "author": "Haim Bell", + "license": "Apache-2.0", + "devDependencies": { + "@amplication/code-gen-types": "^2.0.34", + "@amplication/code-gen-utils": "^0.0.9", + "lodash": "^4.17.21", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "@types/lodash": "^4.14.200", + "@typescript-eslint/eslint-plugin": "^6.9.1", + "@typescript-eslint/parser": "^6.9.1", + "copy-webpack-plugin": "^12.0.2", + "eslint": "^8.52.0", + "jest-mock-extended": "^3.0.5", + "prettier": "^2.6.2", + "rimraf": "^5.0.5", + "ts-loader": "^9.5.0", + "typescript": "^5.2.2", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "webpack-node-externals": "^3.0.0" + } +} diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/project.json b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/project.json new file mode 100644 index 000000000..1407e4eb5 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/project.json @@ -0,0 +1,6 @@ +{ + "targets": { + "lint": {}, + "npm:publish": {} + } +} diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/constants.ts b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/constants.ts new file mode 100644 index 000000000..3f60fd4c6 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/constants.ts @@ -0,0 +1,6 @@ +export const moduleNameKey = "${{ MODULE_NAME }}"; + +// settings based +export const nameKey = "${{ NAME }}"; +export const typeKey = "${{ TYPE }}"; +export const configurationForceDeleteKey = "${{ CONFIGURATION_FORCE_DELETE }}"; diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/index.ts b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/index.ts new file mode 100644 index 000000000..a9557b44d --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/index.ts @@ -0,0 +1,102 @@ +import { + dotnetPluginEventsTypes, + dotnetPluginEventsParams as dotnet, + dotnetTypes, + FileMap, + IFile, +} from "@amplication/code-gen-types"; +import { CodeBlock } from "@amplication/csharp-ast"; +import { + nameKey, + typeKey, + moduleNameKey, + configurationForceDeleteKey, +} from "./constants"; +import { resolve } from "path"; +import { getPluginSettings, getTerraformDirectory } from "./utils"; +import { kebabCase, snakeCase } from "lodash"; +import { RepositoryType } from "./types"; +import { AmplicationPlugin } from "@amplication/code-gen-types/src/dotnet-plugins.types"; + +class TerraformAwsRepositoryEcrPlugin implements AmplicationPlugin { + register(): dotnetPluginEventsTypes.DotnetEvents { + return { + LoadStaticFiles: { + after: this.afterLoadStaticFiles, + }, + }; + } + + async afterLoadStaticFiles( + context: dotnetTypes.DsgContext, + eventParams: dotnet.LoadStaticFilesParams, + files: FileMap + ): Promise> { + context.logger.info(`Generating Terraform AWS Repository ECR...`); + + // get the name for the service, to be used as a fallback for the + // repository name + const serviceName = kebabCase(context.resourceInfo?.name); + if (!serviceName) { + throw new Error("Service name is undefined"); + } + + // instantiate a variable consisting of the path on the + // 'provisioning-terraform-aws-core' made up of the settings + // 'root_directory' & 'directory_name', this function will throw + // an error if the aforementioned plugin wasnt installed. + const terraformDirectory = getTerraformDirectory( + context.pluginInstallations, + context.serverDirectories.baseDirectory + ); + + // fetch the plugin specific settings and merge them with the defaults + const settings = getPluginSettings(context.pluginInstallations); + + if ( + settings.repository_type != RepositoryType.Private && + settings.repository_type != RepositoryType.Public + ) { + throw new Error( + "TerraformAwsRepositoryEcrPlugin: The setting repository_type should either be 'private' or 'public'" + ); + } + + const templateFileName = "ecr-template.tf"; + const fileNamePrefix = "ecr-"; + const fileNameSuffix = ".tf"; + const name: string = settings.repository_name + ? settings.repository_name + : serviceName; + + const staticPath = resolve(__dirname, "./static/"); + const chartTemplateFiles = await context.utils.importStaticFiles( + staticPath, + terraformDirectory + ); + const fileMap = new FileMap(context.logger); + for (const item of chartTemplateFiles.getAll()) { + const newCode = item.code + .replaceAll(moduleNameKey, "ecr_" + snakeCase(name)) + .replaceAll(nameKey, kebabCase(name)) + .replaceAll(typeKey, settings.repository_type) + .replaceAll( + configurationForceDeleteKey, + String(settings.configuration.force_delete) + ); + const file: IFile = { + path: item.path.replace(templateFileName, fileNamePrefix + name + fileNameSuffix), + code: new CodeBlock({ + code: newCode, + }), + }; + fileMap.set(file); + } + + context.logger.info(`Generated Terraform AWS Repository ECR...`); + await files.merge(fileMap); + return files; + } +} + +export default TerraformAwsRepositoryEcrPlugin; diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/static/ecr-template.tf b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/static/ecr-template.tf new file mode 100644 index 000000000..ae17685a4 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/static/ecr-template.tf @@ -0,0 +1,28 @@ +module "${{ MODULE_NAME }}" { + source = "terraform-aws-modules/ecr/aws" + version = "1.6.0" + + repository_name = "${{ NAME }}" + repository_type = "${{ TYPE }}" + + create_lifecycle_policy = false + + repository_force_delete = ${{ CONFIGURATION_FORCE_DELETE }} + repository_read_access_arns = [] + repository_read_write_access_arns = [] +} + +output "repository_arn" { + description = "Full ARN of the repository" + value = module.${{ MODULE_NAME }}.repository_arn +} + +output "repository_registry_id" { + description = "The registry ID where the repository was created" + value = module.${{ MODULE_NAME }}.repository_registry_id +} + +output "repository_url" { + description = "The URL of the repository (in the form `aws_account_id.dkr.ecr.region.amazonaws.com/repositoryName`)" + value = module.${{ MODULE_NAME }}.repository_url +} diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/tests/.keep b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/tests/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/types.ts b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/types.ts new file mode 100644 index 000000000..c102c8773 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/types.ts @@ -0,0 +1,12 @@ +export enum RepositoryType { + Public = "public", + Private = "private", +} + +export interface Settings { + repository_type: RepositoryType; + repository_name: string; + configuration: { + force_delete: boolean; + }; +} diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/utils.ts b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/utils.ts new file mode 100644 index 000000000..5c74ea083 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/src/utils.ts @@ -0,0 +1,45 @@ +import { PluginInstallation } from "@amplication/code-gen-types"; +import { name as PackageName } from "../package.json"; +import { Settings } from "./types"; +import defaultSettings from "../.amplicationrc.json"; + +export const getPluginSettings = ( + pluginInstallations: PluginInstallation[] +): Settings => { + const plugin = pluginInstallations.find( + (plugin) => plugin.npm === PackageName + ); + + const userSettings = plugin?.settings ?? {}; + + const settings: Settings = { + ...defaultSettings.settings, + ...userSettings, + }; + + return settings; +}; + +export const getTerraformDirectory = ( + pluginInstallations: PluginInstallation[], + serverBaseDirectory: string +): string => { + const plugin = pluginInstallations.find( + (plugin) => + plugin.npm === "@amplication/plugin-dotnet-provisioning-terraform-aws-core" + ); + + if (!plugin) { + throw new Error( + "TerraformAwsRepositoryEcrPlugin: is dependent on 'Terraform - AWS Core' plugin" + ); + } + + const { root_level, directory_name } = plugin.settings; + + if (root_level) { + return `./${directory_name}`; + } else { + return `./${serverBaseDirectory}/${directory_name}`; + } +}; diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/tsconfig.json b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/tsconfig.json new file mode 100644 index 000000000..6646f44f5 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/index.ts"], + "exclude": ["node_modules"] +} diff --git a/plugins/dotnet-provisioning-terraform-aws-repository-ecr/webpack.config.js b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/webpack.config.js new file mode 100644 index 000000000..07eadf115 --- /dev/null +++ b/plugins/dotnet-provisioning-terraform-aws-repository-ecr/webpack.config.js @@ -0,0 +1,43 @@ +const path = require("path"); +const webpack = require("webpack"); +const CopyWebpackPlugin = require("copy-webpack-plugin"); + +/** @type {import("webpack").Configuration} */ +module.exports = { + mode: "production", + target: "node", + entry: "./src/index.ts", + externals: ["@amplication/code-gen-utils", "@amplication/code-gen-types"], + plugins: [ + new webpack.SourceMapDevToolPlugin({ + filename: "[name].js.map", + }), + new CopyWebpackPlugin({ + patterns: [ + { from: "src/static", to: "static", noErrorOnMissing: true }, + { from: "src/templates", to: "templates", noErrorOnMissing: true }, + ], + }), + ], + module: { + rules: [ + { + test: /\.tsx?$/, + use: "ts-loader", + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: [".ts", ".js", ".json"], + }, + optimization: { + minimize: false, + }, + output: { + filename: "index.js", + path: path.resolve(__dirname, "dist"), + libraryTarget: "commonjs2", + clean: true, + }, +};