├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── lint-pr.yml │ ├── release.yml │ └── upgrade-backstage.yml ├── .gitignore ├── .mergify.yml ├── .prettierignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── app-config.yaml ├── backstage.json ├── docs ├── developing.md ├── images │ ├── proton-backstage-demo.gif │ ├── proton-entity-card.png │ ├── proton-logo.png │ ├── tutorial-scaffolder-1.png │ ├── tutorial-scaffolder-2.png │ ├── tutorial-scaffolder-3.png │ └── tutorial-service-entity-card.png ├── install.md ├── reference.md ├── tutorial-assets │ └── fargate-nginx-template │ │ ├── skeleton │ │ ├── repo-contents │ │ │ ├── Dockerfile │ │ │ ├── README.md │ │ │ ├── catalog-info.yaml │ │ │ ├── index.html │ │ │ └── run-tests.sh │ │ └── spec.yaml │ │ └── template.yaml └── tutorial.md ├── lerna.json ├── package.json ├── plugins ├── aws-proton-backend │ ├── .eslintrc.js │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── actions │ │ │ ├── create │ │ │ │ ├── create.test.ts │ │ │ │ ├── create.ts │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── api │ │ │ ├── AwsProtonApi.test.ts │ │ │ ├── AwsProtonApi.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── run.ts │ │ ├── service │ │ │ ├── router.test.ts │ │ │ ├── router.ts │ │ │ └── standaloneServer.ts │ │ └── setupTests.ts │ └── tsconfig.json └── aws-proton │ ├── .eslintrc.js │ ├── CHANGELOG.md │ ├── README.md │ ├── dev │ └── index.tsx │ ├── package.json │ ├── src │ ├── api │ │ ├── AWSProtonServiceApi.ts │ │ ├── AWSProtonServiceApiClient.ts │ │ └── index.ts │ ├── components │ │ └── AWSProtonServiceOverview │ │ │ ├── AWSProtonServiceOverview.test.tsx │ │ │ └── AWSProtonServiceOverview.tsx │ ├── constants.ts │ ├── hooks │ │ ├── useProtonService.ts │ │ └── useProtonServiceArnFromEntity.ts │ ├── index.ts │ ├── mocks │ │ ├── index.ts │ │ └── mocks.ts │ ├── plugin.test.ts │ ├── plugin.ts │ ├── routes.ts │ ├── setupTests.ts │ └── types.ts │ └── tsconfig.json ├── tsconfig.json └── yarn.lock /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: npm 5 | directory: "/" 6 | schedule: 7 | interval: monthly 8 | open-pull-requests-limit: 20 9 | ignore: 10 | # Let backstage-cli do these version bumps 11 | - dependency-name: '@backstage*' 12 | # Keep React in sync with the peer dependencies in the Backstage deps 13 | - dependency-name: 'react*' 14 | - dependency-name: '@types/react' 15 | # Version 5.0.0 has a breaking change 16 | - dependency-name: 'yn' 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | name: Run Unit Tests 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Install Node.js 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: 16 19 | - run: yarn install 20 | - run: yarn ci 21 | env: 22 | CI: true 23 | - run: yarn backstage-cli versions:check 24 | -------------------------------------------------------------------------------- /.github/workflows/lint-pr.yml: -------------------------------------------------------------------------------- 1 | name: "Lint PR" 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | semanticpr: 12 | name: Semantic Pull Request 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: amannn/action-semantic-pull-request@v5 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Plugins 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | deploymentApproval: 7 | description: "Are the team pipelines' time windows and alarm blockers open?" 8 | type: boolean 9 | required: true 10 | manualTestsCompleted: 11 | description: "Have you manually tested the latest plugin code in a local Backstage app by following the tutorial?" 12 | type: boolean 13 | required: true 14 | 15 | jobs: 16 | release: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Confirm deployment is approved 21 | if: ${{ !inputs.deploymentApproval }} 22 | run: | 23 | echo Please confirm that time windows and alarm blockers are open before releasing! 24 | exit 1 25 | - name: Confirm manual tests are completed 26 | if: ${{ !inputs.manualTestsCompleted }} 27 | run: | 28 | echo Please complete manual tests before releasing! 29 | exit 1 30 | - uses: actions/checkout@v3 31 | with: 32 | fetch-depth: 0 33 | - uses: actions/setup-node@v3 34 | with: 35 | node-version: 16 36 | 37 | - name: Configure git 38 | run: | 39 | git config user.name "github-actions[bot]" 40 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 41 | 42 | - name: Check for new commits to release 43 | run: | 44 | CURRENT_VERSION=$(cat lerna.json | jq -r '.version') 45 | COMMITS_TO_RELEASE=$(git log --pretty=oneline v$CURRENT_VERSION..HEAD | wc -l) 46 | 47 | echo Current version: v$CURRENT_VERSION 48 | echo Commits to release: $COMMITS_TO_RELEASE 49 | 50 | echo "CURRENT_VERSION=${CURRENT_VERSION}" >> $GITHUB_ENV 51 | echo "COMMITS_TO_RELEASE=${COMMITS_TO_RELEASE}" >> $GITHUB_ENV 52 | 53 | - name: Check if no release needed 54 | if: ${{ env.COMMITS_TO_RELEASE == 0 }} 55 | run: | 56 | echo No changes to release! 57 | echo Current release: $CURRENT_VERSION 58 | 59 | - name: Test latest changes 60 | if: ${{ env.COMMITS_TO_RELEASE != 0 }} 61 | run: | 62 | yarn install 63 | yarn ci 64 | env: 65 | CI: true 66 | 67 | - name: Publish new version to GitHub 68 | if: ${{ env.COMMITS_TO_RELEASE != 0 }} 69 | run: | 70 | yarn bump-version 71 | 72 | NEW_VERSION=$(cat lerna.json | jq -r '.version') 73 | RELEASE_COMMIT_ID=$(git rev-parse HEAD) 74 | 75 | echo "NEW_VERSION=${NEW_VERSION}" >> $GITHUB_ENV 76 | echo "RELEASE_COMMIT_ID=${RELEASE_COMMIT_ID}" >> $GITHUB_ENV 77 | env: 78 | GH_TOKEN: ${{ github.token }} 79 | 80 | - name: Check if version was bumped 81 | # This can happen when all the new commits were outside of the plugin directories, 82 | # so lerna does not bump the version number. 83 | if: ${{ env.COMMITS_TO_RELEASE != 0 && env.NEW_VERSION == env.CURRENT_VERSION }} 84 | run: | 85 | echo No changes to release! 86 | echo Current release: $CURRENT_VERSION 87 | 88 | - name: 'Show new version details' 89 | if: ${{ env.COMMITS_TO_RELEASE != 0 && env.NEW_VERSION != env.CURRENT_VERSION }} 90 | shell: bash 91 | run: | 92 | echo New version: v$NEW_VERSION 93 | echo Commit ID: $RELEASE_COMMIT_ID 94 | echo Previous version: v$CURRENT_VERSION 95 | echo Changes released: 96 | git log --pretty=oneline v$CURRENT_VERSION..v$NEW_VERSION 97 | 98 | - name: 'Update GitHub release notes' 99 | if: ${{ env.COMMITS_TO_RELEASE != 0 && env.NEW_VERSION != env.CURRENT_VERSION }} 100 | shell: bash 101 | run: | 102 | BACKSTAGE_VERSION=$(cat backstage.json | jq -r '.version') 103 | gh release view v$NEW_VERSION --json body -q ".body" > release-notes.txt 104 | echo -e "### Backstage Version\n\nThis release was built on Backstage version $BACKSTAGE_VERSION." >> release-notes.txt 105 | gh release edit v$NEW_VERSION --notes-file release-notes.txt 106 | rm release-notes.txt 107 | env: 108 | GH_TOKEN: ${{ github.token }} 109 | 110 | - name: 'Login to NPM registry' 111 | if: ${{ env.COMMITS_TO_RELEASE != 0 && env.NEW_VERSION != env.CURRENT_VERSION }} 112 | shell: bash 113 | run: | 114 | echo "//registry.npmjs.org/:_authToken=${{secrets.NPM_AUTH_TOKEN}}" >> .npmrc 115 | echo "always-auth = true" >> .npmrc 116 | 117 | - name: Publish new version to NPM registry 118 | if: ${{ env.COMMITS_TO_RELEASE != 0 && env.NEW_VERSION != env.CURRENT_VERSION }} 119 | run: | 120 | yarn publish-version 121 | -------------------------------------------------------------------------------- /.github/workflows/upgrade-backstage.yml: -------------------------------------------------------------------------------- 1 | name: Upgrade Backstage 2 | 3 | on: 4 | # schedule: 5 | # - cron: '0 1 1 * *' # 1 am UTC on 1st day of every month 6 | workflow_dispatch: {} 7 | 8 | jobs: 9 | upgrade: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 16 17 | 18 | - name: Upgrade plugins 19 | run: | 20 | yarn install 21 | yarn backstage-cli versions:bump 22 | 23 | - name: Commit changes 24 | run: | 25 | git config user.name "github-actions[bot]" 26 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 27 | git add -A 28 | git diff-index --quiet HEAD || git commit -m "chore(deps): Upgrade Backstage versions" 29 | 30 | - name: Test changes 31 | run: | 32 | yarn install 33 | yarn ci 34 | env: 35 | CI: true 36 | 37 | - name: Push changes 38 | run: | 39 | git push 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | 12 | # Coverage directory generated when running tests with coverage 13 | coverage 14 | 15 | # Dependencies 16 | node_modules/ 17 | 18 | # Node version directives 19 | .nvmrc 20 | 21 | # dotenv environment variables file 22 | .env 23 | .env.test 24 | 25 | # Build output 26 | dist 27 | dist-types 28 | 29 | # Temporary change files created by Vim 30 | *.swp 31 | 32 | # MkDocs build output 33 | site 34 | 35 | # Local configuration files 36 | *.local.yaml 37 | 38 | # Sensitive credentials 39 | *-credentials.yaml 40 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | queue_rules: 2 | - name: default 3 | conditions: 4 | # Conditions to get out of the queue (= merged) 5 | - status-success=Run Unit Tests 6 | - status-success=Semantic Pull Request 7 | 8 | pull_request_rules: 9 | - name: Automatically merge on CI success and review approval 10 | conditions: 11 | - base=main 12 | - '#approved-reviews-by>=1' 13 | - approved-reviews-by=@awslabs/aws-proton 14 | - -approved-reviews-by~=author 15 | - status-success=Run Unit Tests 16 | - status-success=Semantic Pull Request 17 | - label!=work-in-progress 18 | - -title~=(WIP|wip) 19 | - -merged 20 | - -closed 21 | - author!=dependabot[bot] 22 | actions: 23 | queue: 24 | method: squash 25 | name: default 26 | 27 | - name: Automatically approve and merge Dependabot PRs 28 | conditions: 29 | - base=main 30 | - author=dependabot[bot] 31 | - status-success=Run Unit Tests 32 | - status-success=Semantic Pull Request 33 | - -title~=(WIP|wip) 34 | - -label~=(blocked|do-not-merge) 35 | - -merged 36 | - -closed 37 | actions: 38 | review: 39 | type: APPROVE 40 | queue: 41 | method: squash 42 | name: default 43 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .yarn 2 | yarn.lock 3 | dist 4 | dist-types 5 | .vscode 6 | .github 7 | docs/tutorial-assets 8 | CHANGELOG.md 9 | CODE_OF_CONDUCT.md 10 | CONTRIBUTING.md 11 | app-config.yaml 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.2.2](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.2.1...v0.2.2) (2023-04-19) 7 | 8 | **Note:** Version bump only for package aws-proton-plugins-for-backstage 9 | 10 | 11 | 12 | 13 | 14 | ## [0.2.1](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.2.0...v0.2.1) (2023-01-19) 15 | 16 | **Note:** Version bump only for package aws-proton-plugins-for-backstage 17 | 18 | 19 | 20 | 21 | 22 | # [0.2.0](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.7...v0.2.0) (2022-12-20) 23 | 24 | ### Features 25 | 26 | - Support using Proton across multiple AWS accounts in one Backstage app ([8c8778a](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/8c8778aa8ab9cc7c9c996482e323b30d50326753)) 27 | 28 | ### BREAKING CHANGES 29 | 30 | You must make two changes to your Backstage app source code when upgrading your Backstage app to use this version of the Proton plugins: 31 | 32 | 1. Pass configuration to the Proton backend plugin router: 33 | ```diff 34 | diff --git a/packages/backend/src/plugins/awsProton.ts b/packages/backend/src/plugins/awsProton.ts 35 | index 68968b9..37de05a 100644 36 | --- a/packages/backend/src/plugins/awsProton.ts 37 | +++ b/packages/backend/src/plugins/awsProton.ts 38 | @@ -4,5 +4,6 @@ import { PluginEnvironment } from '../types'; 39 | export default async function createPlugin(env: PluginEnvironment) { 40 | return await createRouter({ 41 | logger: env.logger, 42 | + config: env.config, 43 | }); 44 | } 45 | ``` 46 | 47 | 2. Pass configuration to the Proton scaffolder action: 48 | ```diff 49 | diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts 50 | index d566691..fdb0f42 100644 51 | --- a/packages/backend/src/plugins/scaffolder.ts 52 | +++ b/packages/backend/src/plugins/scaffolder.ts 53 | @@ -22,7 +22,7 @@ export default async function createPlugin( 54 | config: env.config, 55 | }); 56 | 57 | - const actions = [...builtInActions, createAwsProtonServiceAction()]; 58 | + const actions = [...builtInActions, createAwsProtonServiceAction({ config: env.config })]; 59 | 60 | return await createRouter({ 61 | logger: env.logger, 62 | ``` 63 | 64 | ## [0.1.7](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.6...v0.1.7) (2022-12-05) 65 | 66 | **Note:** Version bump only for package aws-proton-plugins-for-backstage 67 | 68 | ## [0.1.6](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.5...v0.1.6) (2022-11-14) 69 | 70 | ### Bug Fixes 71 | 72 | - run prettier to get consistent code style ([b167e29](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/b167e29abd1b5fd51145bed8ae651a4c750f9654)) 73 | 74 | ## [0.1.5](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.4...v0.1.5) (2022-11-11) 75 | 76 | **Note:** Version bump only for package aws-proton-plugins-for-backstage 77 | 78 | ## [0.1.4](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.3...v0.1.4) (2022-11-09) 79 | 80 | ### Bug Fixes 81 | 82 | - Support scaffolding pipeline-less services ([9655f82](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/9655f822182a6184712ff76e7f2075a0b46bc559)) 83 | 84 | ## [0.1.3](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.2...v0.1.3) (2022-07-19) 85 | 86 | **Note:** Version bump only for package aws-proton-plugins-for-backstage 87 | 88 | ## [0.1.2](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.1...v0.1.2) (2022-06-21) 89 | 90 | **Note:** Version bump only for package aws-proton-plugins-for-backstage 91 | 92 | ## [0.1.1](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.0...v0.1.1) (2022-06-21) 93 | 94 | **Note:** Version bump only for package aws-proton-plugins-for-backstage 95 | 96 | # [0.1.0](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.0.0...v0.1.0) (2022-06-20) 97 | 98 | ### Bug Fixes 99 | 100 | - add user agent to proton calls ([b02903a](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/b02903a0be4ec90e083d98e7fb2805f88239b823)) 101 | - Fill in Proton console URL link ([edd5280](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/edd5280e4752fa7f4c9324eb214358dfa87980a0)) 102 | - Move around content on the entity card, add more test fixtures ([4806b41](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/4806b411104a8b0e3a382c8ea6a01bdfa630ab49)) 103 | - support EKS service account creds ([f9f2e22](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/f9f2e22646c9e0c00df4c9a3a6c99672a7f080c5)) 104 | 105 | ### Features 106 | 107 | - Change the annotation to aws.amazon.com ([8fad850](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/8fad85099d2b0f3d8f4baf74b764cb295e4c9b18)) 108 | - change the plugin names to the AWS namespace ([7ca8aeb](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/7ca8aeb9e6820cb2508b8cb067b177e8acfc1e5c)) 109 | 110 | ### Reverts 111 | 112 | - Revert "Bump yn from 4.0.0 to 5.0.0 in /plugins/aws-proton-backend" ([623c411](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/623c411911d6635a451b91eed1a9e8dbf6464ed8)) 113 | 114 | ## 0.0.0 (2022-04-04) 115 | 116 | Initial release 117 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Proton plugins for Backstage 2 | 3 | [![CI](https://github.com/awslabs/aws-proton-plugins-for-backstage/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/awslabs/aws-proton-plugins-for-backstage/actions/workflows/ci.yml) 4 | 5 | This repository contains a set of Backstage plugins for interacting with [AWS Proton](https://aws.amazon.com/proton/). 6 | 7 | The plugins provide: 8 | 9 | - An entity card to display the status of an AWS Proton service. 10 | - A scaffolder action to create an AWS Proton service. 11 | 12 | ![AWS Proton Service entity card](/docs/images/proton-entity-card.png 'AWS Proton Service entity card') 13 | 14 | ## Demo 15 | 16 | ![AWS Proton plugins for Backstage demo](/docs/images/proton-backstage-demo.gif 'AWS Proton plugins for Backstage demo') 17 | 18 | ## Installation 19 | 20 | See [AWS Proton plugins for Backstage installation guide](./docs/install.md). 21 | 22 | ## Usage 23 | 24 | For information about using the Proton plugins, see the following documents: 25 | 26 | - [AWS Proton plugins for Backstage reference](./docs/reference.md) 27 | - [Tutorial: using the AWS Proton plugins for Backstage](./docs/tutorial.md) 28 | 29 | ## Development 30 | 31 | For information about developing the Proton plugins locally, see [Developing the AWS Proton plugins for Backstage](./docs/developing.md). 32 | 33 | ## Security 34 | 35 | For information about contributing and reporting security issues, see [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications). 36 | 37 | ## License 38 | 39 | This project is licensed under the Apache-2.0 License. 40 | 41 | N.B.: Although this repository is released under the Apache-2.0 license, its test dependencies include the third party rollup-plugin-dts project. The rollup-plugin-dts project's licensing includes the LGPL-3.0 license. 42 | -------------------------------------------------------------------------------- /app-config.yaml: -------------------------------------------------------------------------------- 1 | # Stub app config needed to run with yarn start 2 | app: 3 | title: backstage example app 4 | baseUrl: http://localhost:3000 5 | 6 | backend: 7 | baseUrl: http://localhost:7007 -------------------------------------------------------------------------------- /backstage.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.15.0" 3 | } 4 | -------------------------------------------------------------------------------- /docs/developing.md: -------------------------------------------------------------------------------- 1 | # Developing the AWS Proton plugins for Backstage 2 | 3 | Learn how to develop the AWS Proton plugins for Backstage locally. 4 | 5 | To initialize the AWS Proton plugin packages: 6 | 7 | ```shell 8 | yarn install 9 | yarn ci 10 | ``` 11 | 12 | ## Test standalone 13 | 14 | The easiest way you can develop and test the AWS Proton plugins in your local environment is to run the plugins in standalone mode. 15 | 16 | You can run the backend plugin this way: 17 | 18 | ```shell 19 | yarn start:backend 20 | ``` 21 | 22 | And the frontend plugin this way: 23 | 24 | ```shell 25 | yarn start:frontend 26 | ``` 27 | 28 | ## Test in a local Backstage app 29 | 30 | To develop and test the AWS Proton plugins in a local Backstage app: 31 | 32 | 1. Initialize the packages like in the standalone case by running `yarn install` and `yarn ci`. Then, follow the main [Backstage instructions](https://backstage.io/docs/getting-started/create-an-app) to create and run a Backstage app locally. We recommend that you create a git repository for your personal Backstage app, so that you can use version control for your modifications. After you create the app, go into the app's root directory and run `git init`. 33 | 34 | 1. Follow the [AWS Proton plugins for Backstage installation guide](install.md) to install the AWS Proton plugins into your local Backstage app. You need to slightly modify the guide's instructions: don't run the `yarn workspace backend add...` or `yarn workspace app add...` steps of the guide. Instead, copy the plugin source code into your Backstage app: 35 | 36 | ```shell 37 | $ cp -rf ./aws-proton-plugins-for-backstage/plugins/* ./my-personal-backstage-app/plugins/ 38 | ``` 39 | 40 | 1. Manually add the AWS Proton plugin dependencies to the 'app' and 'backend' workspaces and configure them to point to the local plugin code: 41 | 42 | ```diff 43 | diff --git a/packages/app/package.json b/packages/app/package.json 44 | index 5509d17..5bf347a 100644 45 | --- a/packages/app/package.json 46 | +++ b/packages/app/package.json 47 | @@ -7,6 +7,7 @@ 48 | "role": "frontend" 49 | }, 50 | "dependencies": { 51 | + "@aws/aws-proton-plugin-for-backstage": "link:../../plugins/aws-proton", 52 | "@backstage/app-defaults": "^1.0.1", 53 | "@backstage/catalog-model": "^1.0.1", 54 | "@backstage/cli": "^0.17.0", 55 | diff --git a/packages/backend/package.json b/packages/backend/package.json 56 | index 8e7730c..de90e48 100644 57 | --- a/packages/backend/package.json 58 | +++ b/packages/backend/package.json 59 | @@ -16,6 +16,7 @@ 60 | "build-image": "docker build ../.. -f Dockerfile --tag backstage" 61 | }, 62 | "dependencies": { 63 | + "@aws/aws-proton-backend-plugin-for-backstage": "link:../../plugins/aws-proton-backend", 64 | "app": "link:../app", 65 | "@backstage/backend-common": "^0.13.2", 66 | "@backstage/backend-tasks": "^0.3.0", 67 | ``` 68 | 69 | 1. Run `yarn install` to set up the appropriate links to the local plugin code. Follow the rest of the instructions in the installation guide, like editing files to hook the plugins into your Backstage app frontend and backend. 70 | 71 | After everything is installed in your local Backstage app, verify the following: 72 | 73 | 1. You see `aws:proton:create-service` in the list of installed scaffolder actions on http://localhost:7007/create/actions. 74 | 2. The backend plugin is running: http://localhost:7007/api/aws-proton-backend/health. You should receive `{"status":"ok"}`. 75 | 76 | > **Note** 77 | > 78 | > Each time you make a code change to the plugins, you need to re-build them (by running `yarn ci`) and copy them again to your local Backstage app. 79 | 80 | To learn how to use the plugins in your Backstage app, see [Tutorial: using the AWS Proton plugins for Backstage](tutorial.md) . 81 | -------------------------------------------------------------------------------- /docs/images/proton-backstage-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-proton-plugins-for-backstage/f68cead42db58090aba0e972d21c69c355225af1/docs/images/proton-backstage-demo.gif -------------------------------------------------------------------------------- /docs/images/proton-entity-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-proton-plugins-for-backstage/f68cead42db58090aba0e972d21c69c355225af1/docs/images/proton-entity-card.png -------------------------------------------------------------------------------- /docs/images/proton-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-proton-plugins-for-backstage/f68cead42db58090aba0e972d21c69c355225af1/docs/images/proton-logo.png -------------------------------------------------------------------------------- /docs/images/tutorial-scaffolder-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-proton-plugins-for-backstage/f68cead42db58090aba0e972d21c69c355225af1/docs/images/tutorial-scaffolder-1.png -------------------------------------------------------------------------------- /docs/images/tutorial-scaffolder-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-proton-plugins-for-backstage/f68cead42db58090aba0e972d21c69c355225af1/docs/images/tutorial-scaffolder-2.png -------------------------------------------------------------------------------- /docs/images/tutorial-scaffolder-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-proton-plugins-for-backstage/f68cead42db58090aba0e972d21c69c355225af1/docs/images/tutorial-scaffolder-3.png -------------------------------------------------------------------------------- /docs/images/tutorial-service-entity-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-proton-plugins-for-backstage/f68cead42db58090aba0e972d21c69c355225af1/docs/images/tutorial-service-entity-card.png -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # AWS Proton plugins for Backstage installation guide 2 | 3 | This document covers the installation of the AWS Proton plugins for Backstage into your Backstage application. 4 | 5 | 6 | 7 | 1. [Prerequisites](#prerequisites) 8 | 1. [AWS credentials](#aws-credentials) 9 | 1. [IAM permissions](#iam-permissions) 10 | 1. [Install the backend plugin](#install-the-backend-plugin) 11 | 1. [Install the frontend UI plugin](#install-the-frontend-ui-plugin) 12 | 1. [Install the Software Templates scaffolder action](#install-the-software-templates-scaffolder-action) 13 | 14 | 15 | ## Prerequisites 16 | 17 | These instructions assume you already have a working Backstage application that you can install the plugins in. If this isn't the case, refer to the Backstage [Getting Started](https://backstage.io/docs/getting-started/) documentation. 18 | 19 | ## AWS credentials 20 | 21 | By default, the AWS Proton backend plugin relies on the [default behavior of the AWS SDK for Javascript](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_credential_provider_node.html) to determine the AWS credentials that it uses to authenticate an identity to use with AWS APIs. 22 | 23 | The AWS Proton backend plugin that runs in your Backstage app searches for credentials in the following order: 24 | 25 | 1. Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) 26 | 1. SSO credentials from the token cache 27 | 1. Web identity token credentials (including running in an Amazon EKS cluster using IAM roles for service accounts) 28 | 1. Shared credentials and config ini files (`~/.aws/credentials`, `~/.aws/config`) 29 | 1. Amazon Elastic Container Service (Amazon ECS) task metadata service 30 | 1. Amazon Elastic Compute Cloud (Amazon EC2) instance metadata service 31 | 32 | We recommend that you don't hard-code long lived AWS credentials in your production Backstage application configuration. Hard-coding credentials is risky and might expose your access key ID and secret access key. 33 | 34 | Instead, we recommend that you use short lived AWS credentials for your production Backstage application by deploying it to Amazon ECS, Amazon Elastic Kubernetes Service (Amazon EKS), or Amazon EC2. For more information about deploying Backstage to Amazon EKS using a Helm chart or to Amazon ECS on AWS Fargate using the AWS Cloud Development Kit (CDK), see [Deploying Backstage](https://backstage.io/docs/deployment/) in the Backstage documentation. 35 | 36 | To use multiple AWS accounts with your Backstage app or to explicitly configure credentials for an AWS account, you can configure AWS accounts in your Backstage app's configuration. 37 | For example, to configure an AWS account to use with the AWS Proton backend plugin which requires using an IAM role to retrieve credentials, add the following to your Backstage app-config.yaml file. 38 | 39 | ```yaml 40 | aws: 41 | accounts: 42 | - accountId: '111111111111' 43 | roleName: 'my-iam-role-name' 44 | ``` 45 | 46 | For more account configuration examples, see the [Backstage integration-aws-node package documentation](https://www.npmjs.com/package/@backstage/integration-aws-node). 47 | 48 | ## IAM permissions 49 | 50 | The AWS Proton backend plugin requires the AWS identity that it uses to have the following IAM permissions for populating the Proton entity card: 51 | 52 | ```json 53 | { 54 | "Version": "2012-10-17", 55 | "Statement": [ 56 | { 57 | "Effect": "Allow", 58 | "Action": ["proton:GetService", "proton:ListServiceInstances"], 59 | "Resource": "*" 60 | } 61 | ] 62 | } 63 | ``` 64 | 65 | The AWS Proton scaffolder action requires the AWS identity that it uses to have the following IAM permissions to create AWS Proton services: 66 | 67 | ```json 68 | { 69 | "Version": "2012-10-17", 70 | "Statement": [ 71 | { 72 | "Effect": "Allow", 73 | "Action": "proton:CreateService", 74 | "Resource": "*" 75 | }, 76 | { 77 | "Effect": "Allow", 78 | "Action": "codestar-connections:PassConnection", 79 | "Resource": "arn:aws:codestar-connections:*:*:connection/*", 80 | "Condition": { 81 | "StringEquals": { 82 | "codestar-connections:PassedToService": "proton.amazonaws.com" 83 | } 84 | } 85 | } 86 | ] 87 | } 88 | ``` 89 | 90 | Depending on how you configure the AWS Proton scaffolder action in your Backstage Software Templates, the AWS Proton scaffolder action permissions can also be further limited to specific AWS Proton service templates and CodeStar Connections connections: 91 | 92 | ```json 93 | { 94 | "Version": "2012-10-17", 95 | "Statement": [ 96 | { 97 | "Effect": "Allow", 98 | "Action": "proton:CreateService", 99 | "Resource": "*", 100 | "Condition": { 101 | "StringEquals": { 102 | "proton:ServiceTemplate": "arn:aws:proton:us-east-1:123456789012:service-template/my-service-template" 103 | } 104 | } 105 | }, 106 | { 107 | "Effect": "Allow", 108 | "Action": "codestar-connections:PassConnection", 109 | "Resource": "arn:aws:codestar-connections:us-east-1:123456789012:connection/c176b204-5bb1-48f1-b977-5aff4fa2df9d", 110 | "Condition": { 111 | "StringEquals": { 112 | "codestar-connections:PassedToService": "proton.amazonaws.com" 113 | } 114 | } 115 | } 116 | ] 117 | } 118 | ``` 119 | 120 | ## Install the backend plugin 121 | 122 | Install the AWS Proton backend plugin package in your Backstage app: 123 | 124 | ```shell 125 | yarn workspace backend add @aws/aws-proton-backend-plugin-for-backstage 126 | ``` 127 | 128 | Create a file `packages/backend/src/plugins/awsProton.ts` with the following content: 129 | 130 | ```typescript 131 | import { createRouter } from '@aws/aws-proton-backend-plugin-for-backstage'; 132 | import { PluginEnvironment } from '../types'; 133 | 134 | export default async function createPlugin(env: PluginEnvironment) { 135 | return await createRouter({ 136 | logger: env.logger, 137 | config: env.config, 138 | }); 139 | } 140 | ``` 141 | 142 | Edit `packages/backend/src/index.ts` to register the backend plugin: 143 | 144 | ```diff 145 | diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts 146 | index 70bc66b..1e624ae 100644 147 | --- a/packages/backend/src/index.ts 148 | +++ b/packages/backend/src/index.ts 149 | @@ -28,6 +28,7 @@ import scaffolder from './plugins/scaffolder'; 150 | import proxy from './plugins/proxy'; 151 | import techdocs from './plugins/techdocs'; 152 | import search from './plugins/search'; 153 | +import awsProton from './plugins/awsProton'; 154 | import { PluginEnvironment } from './types'; 155 | import { ServerPermissionClient } from '@backstage/plugin-permission-node'; 156 | 157 | @@ -79,6 +80,7 @@ async function main() { 158 | const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); 159 | const searchEnv = useHotMemoize(module, () => createEnv('search')); 160 | const appEnv = useHotMemoize(module, () => createEnv('app')); 161 | + const awsProtonEnv = useHotMemoize(module, () => createEnv('aws-proton-backend')); 162 | 163 | const apiRouter = Router(); 164 | apiRouter.use('/catalog', await catalog(catalogEnv)); 165 | @@ -87,6 +89,7 @@ async function main() { 166 | apiRouter.use('/techdocs', await techdocs(techdocsEnv)); 167 | apiRouter.use('/proxy', await proxy(proxyEnv)); 168 | apiRouter.use('/search', await search(searchEnv)); 169 | + apiRouter.use('/aws-proton-backend', await awsProton(awsProtonEnv)); 170 | 171 | // Add backends ABOVE this line; this 404 handler is the catch-all fallback 172 | apiRouter.use(notFoundHandler()); 173 | ``` 174 | 175 | Verify that the backend plugin is running in your Backstage app. You should receive `{"status":"ok"}` when accessing this URL: 176 | `https:///api/aws-proton-backend/health`. 177 | 178 | ## Install the frontend UI plugin 179 | 180 | Install the AWS Proton frontend UI plugin package in your Backstage app: 181 | 182 | ```shell 183 | yarn workspace app add @aws/aws-proton-plugin-for-backstage 184 | ``` 185 | 186 | Edit `packages/app/src/components/catalog/EntityPage.tsx` to add the AWS Proton service overview entity card in the entity page layout. 187 | For example, the following changes add the Proton entity card to the **Overview** tab for an entity: 188 | 189 | ```diff 190 | diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx 191 | index 84d0944..34f6f58 100644 192 | --- a/packages/app/src/components/catalog/EntityPage.tsx 193 | +++ b/packages/app/src/components/catalog/EntityPage.tsx 194 | @@ -43,10 +43,14 @@ import { 195 | } from '@backstage/plugin-catalog'; 196 | import { 197 | isGithubActionsAvailable, 198 | EntityGithubActionsContent, 199 | } from '@backstage/plugin-github-actions'; 200 | +import { 201 | + EntityAWSProtonServiceOverviewCard, 202 | + isAWSProtonServiceAvailable, 203 | +} from '@aws/aws-proton-plugin-for-backstage'; 204 | import { 205 | EntityUserProfileCard, 206 | EntityGroupProfileCard, 207 | EntityMembersListCard, 208 | EntityOwnershipCard, 209 | @@ -119,10 +123,18 @@ const overviewContent = ( 210 | 211 | {entityWarningContent} 212 | 213 | 214 | 215 | + 216 | + Boolean(isAWSProtonServiceAvailable(e))}> 217 | + 218 | + 219 | + 220 | + 221 | + 222 | 223 | 224 | 225 | 226 | 227 | ``` 228 | 229 | ## Install the Software Templates scaffolder action 230 | 231 | Edit `packages/backend/src/plugins/scaffolder.ts` to register the AWS Proton **Create Service** scaffolder action: 232 | 233 | ```diff 234 | diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts 235 | index 7ce5fcf..e2f1362 100644 236 | --- a/packages/backend/src/plugins/scaffolder.ts 237 | +++ b/packages/backend/src/plugins/scaffolder.ts 238 | @@ -2,6 +2,9 @@ import { CatalogClient } from '@backstage/catalog-client'; 239 | import { createRouter } from '@backstage/plugin-scaffolder-backend'; 240 | import { Router } from 'express'; 241 | import type { PluginEnvironment } from '../types'; 242 | +import { ScmIntegrations } from '@backstage/integration'; 243 | +import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; 244 | +import { createAwsProtonServiceAction } from '@aws/aws-proton-backend-plugin-for-backstage'; 245 | 246 | export default async function createPlugin( 247 | env: PluginEnvironment, 248 | @@ -10,11 +13,23 @@ export default async function createPlugin( 249 | discoveryApi: env.discovery, 250 | }); 251 | 252 | + const integrations = ScmIntegrations.fromConfig(env.config); 253 | + 254 | + const builtInActions = createBuiltinActions({ 255 | + integrations, 256 | + catalogClient, 257 | + reader: env.reader, 258 | + config: env.config, 259 | + }); 260 | + 261 | + const actions = [...builtInActions, createAwsProtonServiceAction({ config: env.config })]; 262 | + 263 | return await createRouter({ 264 | logger: env.logger, 265 | config: env.config, 266 | database: env.database, 267 | reader: env.reader, 268 | catalogClient, 269 | + actions, 270 | }); 271 | } 272 | ``` 273 | 274 | Verify that the scaffolder action is successfully registered in your Backstage app. 275 | You should see `aws:proton:create-service` in the list of installed scaffolder actions on the following page: 276 | `https:///create/actions`. 277 | -------------------------------------------------------------------------------- /docs/reference.md: -------------------------------------------------------------------------------- 1 | # AWS Proton plugins for Backstage reference 2 | 3 | This document covers the configuration options for the AWS Proton plugins for Backstage. To install the Proton plugins into your Backstage application, see the [AWS Proton plugins for Backstage installation guide](install.md). For a detailed walkthrough of using the Proton plugins in your Backstage app, see [Tutorial: using the AWS Proton plugins for Backstage](tutorial.md). 4 | 5 | ## Catalog entity annotation 6 | 7 | Annotate a component with the key `aws.amazon.com/aws-proton-service` and an AWS Proton service ARN as the value. Components with this annotation have an **AWS Proton service** card in their **Overview** tab. 8 | 9 | ```yaml 10 | apiVersion: backstage.io/v1alpha1 11 | kind: Component 12 | metadata: 13 | name: [...] 14 | annotations: 15 | [...] 16 | aws.amazon.com/aws-proton-service: arn:aws:proton:us-east-1:111111111111:service/my-proton-service 17 | spec: 18 | [...] 19 | ``` 20 | 21 | ## Software Templates scaffolder action: create an AWS Proton service 22 | 23 | The `aws:proton:create-service` scaffolder action allows Backstage Software Templates to create an AWS Proton service for new components. 24 | 25 | This action assumes that the following AWS resources are already created in your AWS account, prior to creating a Backstage Software Template: 26 | 27 | - [Proton service templates](https://docs.aws.amazon.com/proton/latest/adminguide/ag-templates.html) 28 | - [Proton environments](https://docs.aws.amazon.com/proton/latest/adminguide/ag-environments.html) 29 | - [CodeStar Connections connections](https://docs.aws.amazon.com/dtconsole/latest/userguide/welcome-connections.html) 30 | 31 | To use this action in a Software Template, add a step similar to the following: 32 | 33 | ```yaml 34 | spec: 35 | [...] 36 | steps: 37 | [...] 38 | - id: proton 39 | name: Create Proton Service 40 | action: aws:proton:create-service 41 | input: 42 | serviceName: ${{ parameters.component_id }} 43 | templateName: my-proton-template 44 | templateMajorVersion: '1' 45 | region: us-east-1 46 | repository: ${{ parameters.repoUrl | parseRepoUrl }} 47 | branchName: main 48 | repositoryConnectionArn: 'arn:aws:codestar-connections:us-east-1:111111111111:connection/4dde5c82-51d6-4ea9-918e-03aed6971ff3' 49 | serviceSpecPath: service_spec.yml 50 | ``` 51 | 52 | For full documentation of the `aws:proton:create-service` scaffolder action inputs and outputs, see the `https:///create/actions` page in your Backstage app. 53 | -------------------------------------------------------------------------------- /docs/tutorial-assets/fargate-nginx-template/skeleton/repo-contents/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM public.ecr.aws/nginx/nginx:1.21 2 | EXPOSE 80 3 | COPY index.html /usr/share/nginx/html 4 | -------------------------------------------------------------------------------- /docs/tutorial-assets/fargate-nginx-template/skeleton/repo-contents/README.md: -------------------------------------------------------------------------------- 1 | # ${{values.component_id}} 2 | 3 | ${{values.description}} 4 | -------------------------------------------------------------------------------- /docs/tutorial-assets/fargate-nginx-template/skeleton/repo-contents/catalog-info.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: backstage.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: ${{values.component_id | dump}} 5 | {%- if values.description %} 6 | description: ${{values.description | dump}} 7 | {%- endif %} 8 | annotations: 9 | github.com/project-slug: ${{values.destination.owner + "/" + values.destination.repo}} 10 | backstage.io/techdocs-ref: dir:. 11 | aws.amazon.com/aws-proton-service: arn:aws:proton:${{values.aws_region}}:${{values.aws_account_id}}:service/${{values.component_id}} 12 | spec: 13 | type: service 14 | lifecycle: experimental 15 | owner: ${{values.owner | dump}} 16 | -------------------------------------------------------------------------------- /docs/tutorial-assets/fargate-nginx-template/skeleton/repo-contents/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ${{values.initial_message}} 8 | 9 | 10 |
11 | ${{values.initial_message}} 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/tutorial-assets/fargate-nginx-template/skeleton/repo-contents/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Add tests to run-tests.sh in your repo!" 4 | -------------------------------------------------------------------------------- /docs/tutorial-assets/fargate-nginx-template/skeleton/spec.yaml: -------------------------------------------------------------------------------- 1 | proton: ServiceSpec 2 | 3 | pipeline: 4 | service_dir: "." 5 | unit_test_command: "./run-tests.sh" 6 | 7 | instances: 8 | - name: "dev" 9 | environment: "${{values.aws_proton_dev_environment_name}}" 10 | spec: 11 | desired_count: 1 12 | port: ${{values.http_port}} 13 | task_size: "x-small" 14 | - name: "prod" 15 | environment: "${{values.aws_proton_prod_environment_name}}" 16 | spec: 17 | desired_count: 2 18 | port: ${{values.http_port}} 19 | task_size: "medium" 20 | -------------------------------------------------------------------------------- /docs/tutorial-assets/fargate-nginx-template/template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scaffolder.backstage.io/v1beta3 2 | kind: Template 3 | metadata: 4 | name: fargate-nginx-template 5 | title: Nginx Fargate Web Service 6 | description: Create a simple website using Nginx and AWS Fargate 7 | tags: 8 | - recommended 9 | - nginx 10 | - fargate 11 | spec: 12 | owner: service@example.com 13 | type: service 14 | 15 | parameters: 16 | - title: Provide some simple information 17 | required: 18 | - component_id 19 | - owner 20 | - initial_message 21 | properties: 22 | component_id: 23 | title: Name 24 | type: string 25 | description: Unique name of the component 26 | ui:field: EntityNamePicker 27 | description: 28 | title: Description 29 | type: string 30 | description: Help others understand what this website is for. 31 | owner: 32 | title: Owner 33 | type: string 34 | description: Owner of the component 35 | ui:field: OwnerPicker 36 | ui:options: 37 | allowedKinds: 38 | - Group 39 | initial_message: 40 | title: Initial message 41 | type: string 42 | description: Initial message to display on the website ('Hello world!') 43 | - title: Choose a location 44 | required: 45 | - repoUrl 46 | properties: 47 | repoUrl: 48 | title: Repository Location 49 | type: string 50 | ui:field: RepoUrlPicker 51 | ui:options: 52 | allowedHosts: 53 | - github.com 54 | 55 | steps: 56 | - id: template 57 | name: Fetch Skeleton + Template 58 | action: fetch:template 59 | input: 60 | url: ./skeleton 61 | values: 62 | component_id: ${{ parameters.component_id }} 63 | description: ${{ parameters.description }} 64 | initial_message: ${{ parameters.initial_message }} 65 | owner: ${{ parameters.owner }} 66 | destination: ${{ parameters.repoUrl | parseRepoUrl }} 67 | http_port: 80 68 | # The AWS Proton environments to use for deploying the initial service instances 69 | aws_proton_dev_environment_name: "backstage-proton-plugins-tutorial-env" 70 | aws_proton_prod_environment_name: "backstage-proton-plugins-tutorial-env" 71 | # The AWS account where the Proton service will be created 72 | aws_account_id: "111111111111" 73 | # The AWS region where the Proton service will be created 74 | aws_region: us-east-1 75 | 76 | - id: publish 77 | name: Publish 78 | action: publish:github 79 | input: 80 | allowedHosts: ["github.com"] 81 | description: This is ${{ parameters.component_id }} 82 | repoUrl: ${{ parameters.repoUrl }} 83 | repoVisibility: private 84 | defaultBranch: main 85 | sourcePath: ./repo-contents 86 | 87 | - id: create-proton-service 88 | name: Create AWS Proton Service 89 | action: aws:proton:create-service 90 | input: 91 | serviceName: ${{ parameters.component_id }} 92 | repository: ${{ parameters.repoUrl | parseRepoUrl }} 93 | branchName: main 94 | serviceSpecPath: ./spec.yaml 95 | # Update the following fields to match the resources in your AWS account 96 | region: us-east-1 97 | templateName: load-balanced-fargate-svc 98 | templateMajorVersion: '1' 99 | repositoryConnectionArn: 'arn:aws:codestar-connections:us-east-1:111111111111:connection/c176b204-5bb1-48f1-b977-5aff4fa2df9d' 100 | 101 | - id: register 102 | name: Register 103 | action: catalog:register 104 | input: 105 | repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }} 106 | catalogInfoPath: "/catalog-info.yaml" 107 | 108 | output: 109 | links: 110 | - title: Repository 111 | url: ${{ steps.publish.output.remoteUrl }} 112 | - title: Open in catalog 113 | icon: catalog 114 | entityRef: ${{ steps.register.output.entityRef }} 115 | -------------------------------------------------------------------------------- /docs/tutorial.md: -------------------------------------------------------------------------------- 1 | # Tutorial: using the AWS Proton plugins for Backstage 2 | 3 | This tutorial walks you through an example of using the AWS Proton plugins for Backstage. 4 | 5 | Tutorial steps: 6 | 7 | 8 | 9 | 1. [Set up your Backstage app](#set-up-your-backstage-app) 10 | 1. [Fork this repository](#fork-this-repository) 11 | 1. [Create prerequisite AWS resources](#create-prerequisite-aws-resources) 12 | 1. [Customize the sample Software Template](#customize-the-sample-software-template) 13 | 1. [Register the Software Template in your Backstage app](#register-the-software-template-in-your-backstage-app) 14 | 1. [Create a Backstage component using the Software Template](#create-a-backstage-component-using-the-software-template) 15 | 1. [Tear down AWS resources](#tear-down-aws-resources) 16 | 17 | 18 | ## Set up your Backstage app 19 | 20 | This tutorial assumes that you have a working Backstage application. To use this tutorial with a local Backstage app, follow the main [Backstage instructions](https://backstage.io/docs/getting-started/create-an-app) to create and run a Backstage app locally. 21 | 22 | This tutorial also assumes that your Backstage app is connected to GitHub. Ensure that you have a GitHub [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) for Backstage, and set a `GITHUB_TOKEN` environment variable with that token in the environment where your Backstage app is running. The token needs the `repo` scope. For more information on connecting your Backstage app to GitHub, see [GitHub Locations](https://backstage.io/docs/integrations/github/locations) in the Backstage documentation. 23 | 24 | Follow the [AWS Proton plugins for Backstage installation guide](install.md) to install the Proton plugins into your Backstage application. 25 | 26 | ## Fork this repository 27 | 28 | This repository contains a sample Backstage Software Template. You need to customize the Software Template with your AWS account information, and then register it into your Backstage app. You can create a public fork of this repository. Alternatively, duplicate this repository into a private repository (preferred). For more information, see [Duplicating a repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/duplicating-a-repository) in the GitHub documentation. 29 | 30 | ## Create prerequisite AWS resources 31 | 32 | Create the following AWS resources in your AWS account for this tutorial: 33 | 34 | - An AWS CodeStar Connections repository connection 35 | - An AWS Proton registered repository 36 | - An AWS Proton environment template 37 | - An AWS Proton service template 38 | - An AWS Proton environment 39 | 40 | To create these resources, follow the **Getting Started** guide in the AWS Proton console: 41 | 42 | https://us-east-1.console.aws.amazon.com/proton/home?region=us-east-1#/getting-started 43 | 44 | While you're following the **Getting Started** guide: 45 | 46 | - Copy the [AWS Proton Sample CloudFormation Templates](https://github.com/aws-samples/aws-proton-cloudformation-sample-templates/) into a new repository in your GitHub account by clicking the "Use this template" button on GitHub. Then, link your new template repository with AWS Proton when asked for a template definition repository in the Getting Started guide. 47 | - For the environment template, choose **Sync a template bundle from Git**, link your new template repository, and name the template `fargate-env`. 48 | - For the service template, choose **Sync a template bundle from Git**, link your new template repository, and name the template `load-balanced-fargate-svc`. 49 | - Once the environment and service templates are created, click on the "Create environment" button. Choose the `fargate-env` template and name the environment `backstage-proton-plugins-tutorial-env`. 50 | - Proton creates each new template with a template version 1.0 that you need to publish. On both the [environment template page](https://us-east-1.console.aws.amazon.com/proton/home?region=us-east-1#/templates/environments/detail/fargate-env) and the [service template page](https://us-east-1.console.aws.amazon.com/proton/home?region=us-east-1#/templates/services/detail/load-balanced-fargate-svc), select the template version, choose **Publish**, and wait for the template version to go into the **Published** status. 51 | 52 | At the end of the **Getting started** guide, you should have at least one of each of the resources listed above. You can verify using the following commands: 53 | 54 | ``` 55 | $ aws codestar-connections list-connections --region us-east-1 56 | { 57 | "Connections": [ 58 | { 59 | "ConnectionName": "my-github-account", 60 | "ConnectionArn": "arn:aws:codestar-connections:us-east-1:111111111111:connection/c176b204-5bb1-48f1-b977-5aff4fa2df9d", 61 | "ProviderType": "GitHub", 62 | "OwnerAccountId": "111111111111", 63 | "ConnectionStatus": "AVAILABLE" 64 | } 65 | ] 66 | } 67 | 68 | $ aws proton list-repositories --region us-east-1 69 | { 70 | "repositories": [ 71 | { 72 | "arn": "arn:aws:proton:us-east-1:111111111111:repository/github:/aws-proton-cloudformation-sample-templates", 73 | "name": "/aws-proton-cloudformation-sample-templates", 74 | "provider": "GITHUB" 75 | } 76 | ] 77 | } 78 | 79 | $ aws proton list-environment-templates --region us-east-1 80 | { 81 | "templates": [ 82 | { 83 | "arn": "arn:aws:proton:us-east-1:111111111111:environment-template/fargate-env", 84 | "createdAt": "2022-04-21T11:29:01.800000-07:00", 85 | "lastModifiedAt": "2022-04-21T11:29:01.800000-07:00", 86 | "name": "fargate-env", 87 | "recommendedVersion": "1.0" 88 | } 89 | ] 90 | } 91 | 92 | $ aws proton list-service-templates --region us-east-1 93 | { 94 | "templates": [ 95 | { 96 | "arn": "arn:aws:proton:us-east-1:111111111111:service-template/load-balanced-fargate-svc", 97 | "createdAt": "2022-04-21T11:48:07.137000-07:00", 98 | "lastModifiedAt": "2022-04-21T11:48:07.137000-07:00", 99 | "name": "load-balanced-fargate-svc", 100 | "recommendedVersion": "1.0" 101 | } 102 | ] 103 | } 104 | 105 | $ aws proton list-environments --region us-east-1 106 | { 107 | "environments": [ 108 | { 109 | "arn": "arn:aws:proton:us-east-1:111111111111:environment/backstage-proton-plugins-tutorial-env", 110 | "createdAt": "2022-04-21T11:53:07.528000-07:00", 111 | "deploymentStatus": "SUCCEEDED", 112 | "lastDeploymentAttemptedAt": "2022-04-21T11:53:07.528000-07:00", 113 | "lastDeploymentSucceededAt": "2022-04-21T11:53:07.528000-07:00", 114 | "name": "backstage-proton-plugins-tutorial-env", 115 | "protonServiceRoleArn": "arn:aws:iam::111111111111:role/ProtonServiceRole", 116 | "templateMajorVersion": "1", 117 | "templateMinorVersion": "0", 118 | "templateName": "backstage-proton-plugins-tutorial-env-template" 119 | } 120 | ] 121 | } 122 | ``` 123 | 124 | ## Customize the sample Software Template 125 | 126 | You need to update the sample Software Template with the AWS resources you created in your account in the previous step. Clone your fork of this repository, and make the following changes to the file `docs/tutorial-assets/fargate-nginx-template/template.yaml`. 127 | 128 | 1. Find the `template` step. Update the `aws_account_id` field to match your AWS account. 129 | 130 | Note: The template is set up to create a service with instances in two environments, dev and prod. For the purpose of this tutorial, we use a single example environment, `backstage-proton-plugins-tutorial-env`, for both the dev and prod environments. 131 | 132 | 2. Find the `create-proton-service` step. Update the `repositoryConnectionArn` field to match the CodeStar connection resource in your AWS account. 133 | 134 | Commit and push these changes to your fork of this repository. 135 | 136 | ## Register the Software Template in your Backstage app 137 | 138 | Edit your Backstage app configuration file to register your customized Software Template in your Backstage app. For example, the file may be named `app-config.yaml`. 139 | 140 | In the `catalog` section of the config file, add the following location to the `locations` list. Replace `` with your GitHub account name. 141 | 142 | ```yaml 143 | # AWS Proton Plugins for Backstage tutorial template 144 | - type: url 145 | target: https://github.com//aws-proton-plugins-for-backstage/blob/main/docs/tutorial-assets/fargate-nginx-template/template.yaml 146 | rules: 147 | - allow: [Template] 148 | ``` 149 | 150 | Save your config file changes and restart your Backstage app. 151 | 152 | ## Create a Backstage component using the Software Template 153 | 154 | In your Backstage app, create a Backstage component that uses your customized Software Template. Go to `https:///create`, and choose the "Nginx Fargate Web Service" template. Fill in a component name like `my-tutorial-service`. 155 | 156 | > Note: 157 | > 158 | > _Backstage components_ are different than [Proton components](https://docs.aws.amazon.com/proton/latest/adminguide/ag-components.html), which are service extensions that allow developers to augment Proton services with additional provisioned resources. 159 | 160 | ![Tutorial scaffolder page 1](images/tutorial-scaffolder-1.png 'Tutorial scaffolder page 1') 161 | 162 | In the next step, fill in a name like `my-backstage-tutorial-website` for the private repository that Backstage will create for this new component. 163 | 164 | ![Tutorial scaffolder page 2](images/tutorial-scaffolder-2.png 'Tutorial scaffolder page 2') 165 | 166 | When the Software Template runs, you should see that Backstage successfully fetches the template, publishes initial code to a new GitHub repository, creates the AWS Proton service, and registers the component in the Backstage software catalog. 167 | 168 | ![Tutorial scaffolder page 3](images/tutorial-scaffolder-3.png 'Tutorial scaffolder page 3') 169 | 170 | Go to the new component's page: `http:///catalog/default/component/my-tutorial-service`. You should see an **AWS Proton Service** card in the component's **Overview** tab. The service has two service instances, **dev** and **prod**, deployed to the environment. 171 | 172 | ![Component entity card](images/tutorial-service-entity-card.png 'Component entity card') 173 | 174 | If you later need to re-register this component in your Backstage app, add the following location to the `locations` list in the `catalog` section of your Backstage app config file. Replace `` with your GitHub account name. 175 | 176 | ``` 177 | - type: url 178 | target: https://github.com//my-backstage-tutorial-website/blob/main/catalog-info.yaml 179 | rules: 180 | - allow: [Component] 181 | ``` 182 | 183 | ## Tear down AWS resources 184 | 185 | You don't get charged for using AWS Proton or for creating templates. However, when you provision other AWS resources, like Fargate tasks, by deploying environments and services, you are charged for these resources. When you are finished with this tutorial, you may want to delete your Proton environments and services by running the following commands. 186 | 187 | ``` 188 | $ aws proton delete-service --name my-tutorial-service 189 | 190 | $ aws proton wait service-deleted --name my-tutorial-service 191 | 192 | $ aws proton delete-environment --name backstage-proton-plugins-tutorial-env 193 | ``` 194 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.2", 3 | "packages": ["plugins/*"], 4 | "command": { 5 | "version": { 6 | "allowBranch": "main", 7 | "conventionalCommits": true, 8 | "createRelease": "github", 9 | "exact": true, 10 | "message": "chore(release): version bump %s", 11 | "yes": true 12 | } 13 | }, 14 | "npmClient": "yarn", 15 | "useWorkspaces": true 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-proton-plugins-for-backstage", 3 | "description": "AWS Proton Plugins for Backstage", 4 | "version": "0.0.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "github:awslabs/aws-proton-plugins-for-backstage" 8 | }, 9 | "license": "Apache-2.0", 10 | "author": { 11 | "name": "Amazon Web Services", 12 | "url": "http://aws.amazon.com" 13 | }, 14 | "private": true, 15 | "workspaces": { 16 | "packages": [ 17 | "plugins/*" 18 | ] 19 | }, 20 | "engines": { 21 | "node": ">=16 <=19" 22 | }, 23 | "scripts": { 24 | "bootstrap": "lerna bootstrap", 25 | "tsc": "tsc", 26 | "build": "backstage-cli repo build --all", 27 | "clean": "backstage-cli repo clean", 28 | "test": "backstage-cli test --watchAll=false", 29 | "lint": "backstage-cli repo lint", 30 | "prettier:check": "prettier --check .", 31 | "prettier:fix": "prettier --write .", 32 | "ci": "lerna bootstrap && tsc && backstage-cli repo build --all && backstage-cli test --watchAll=false && backstage-cli repo lint && prettier --check .", 33 | "start:frontend": "yarn workspace @aws/aws-proton-plugin-for-backstage start", 34 | "start:backend": "yarn workspace @aws/aws-proton-backend-plugin-for-backstage start", 35 | "bump-version": "lerna version", 36 | "publish-version": "lerna publish from-package --yes --no-verify-access", 37 | "yarn:audit:fix": "yarn-audit-fix" 38 | }, 39 | "devDependencies": { 40 | "@backstage/cli": "^0.22.8", 41 | "@spotify/prettier-config": "^14.0.0", 42 | "@types/react": "^17.0.0", 43 | "lerna": "^6.6.2", 44 | "prettier": "^3.0.3", 45 | "react": "^17.0.0", 46 | "react-dom": "^17.0.0", 47 | "react-router": "^6.3.0", 48 | "react-router-dom": "^6.3.0", 49 | "typescript": "~5.3.2", 50 | "yarn-audit-fix": "^10.0.1" 51 | }, 52 | "resolutions": { 53 | "**/yaml": "^2.2.2" 54 | }, 55 | "prettier": "@spotify/prettier-config" 56 | } 57 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); 2 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.2.2](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.2.1...v0.2.2) (2023-04-19) 7 | 8 | **Note:** Version bump only for package @aws/aws-proton-backend-plugin-for-backstage 9 | 10 | 11 | 12 | 13 | 14 | ## [0.2.1](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.2.0...v0.2.1) (2023-01-19) 15 | 16 | **Note:** Version bump only for package @aws/aws-proton-backend-plugin-for-backstage 17 | 18 | 19 | 20 | 21 | 22 | # [0.2.0](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.7...v0.2.0) (2022-12-20) 23 | 24 | ### Features 25 | 26 | - Support using Proton across multiple AWS accounts in one Backstage app ([8c8778a](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/8c8778aa8ab9cc7c9c996482e323b30d50326753)) 27 | 28 | ## [0.1.7](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.6...v0.1.7) (2022-12-05) 29 | 30 | **Note:** Version bump only for package @aws/aws-proton-backend-plugin-for-backstage 31 | 32 | ## [0.1.6](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.5...v0.1.6) (2022-11-14) 33 | 34 | ### Bug Fixes 35 | 36 | - run prettier to get consistent code style ([b167e29](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/b167e29abd1b5fd51145bed8ae651a4c750f9654)) 37 | 38 | ## [0.1.5](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.4...v0.1.5) (2022-11-11) 39 | 40 | **Note:** Version bump only for package @aws/aws-proton-backend-plugin-for-backstage 41 | 42 | ## [0.1.4](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.3...v0.1.4) (2022-11-09) 43 | 44 | **Note:** Version bump only for package @aws/aws-proton-backend-plugin-for-backstage 45 | 46 | ## [0.1.3](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.2...v0.1.3) (2022-07-19) 47 | 48 | **Note:** Version bump only for package @aws/aws-proton-backend-plugin-for-backstage 49 | 50 | ## [0.1.2](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.1...v0.1.2) (2022-06-21) 51 | 52 | **Note:** Version bump only for package @aws/aws-proton-backend-plugin-for-backstage 53 | 54 | ## [0.1.1](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.0...v0.1.1) (2022-06-21) 55 | 56 | **Note:** Version bump only for package @aws/aws-proton-backend-plugin-for-backstage 57 | 58 | # [0.1.0](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.0.0...v0.1.0) (2022-06-20) 59 | 60 | ### Bug Fixes 61 | 62 | - add user agent to proton calls ([b02903a](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/b02903a0be4ec90e083d98e7fb2805f88239b823)) 63 | - support EKS service account creds ([f9f2e22](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/f9f2e22646c9e0c00df4c9a3a6c99672a7f080c5)) 64 | 65 | ### Features 66 | 67 | - change the plugin names to the AWS namespace ([7ca8aeb](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/7ca8aeb9e6820cb2508b8cb067b177e8acfc1e5c)) 68 | 69 | ### Reverts 70 | 71 | - Revert "Bump yn from 4.0.0 to 5.0.0 in /plugins/aws-proton-backend" ([623c411](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/623c411911d6635a451b91eed1a9e8dbf6464ed8)) 72 | 73 | ## 0.0.0 (2022-04-04) 74 | 75 | Initial release 76 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/README.md: -------------------------------------------------------------------------------- 1 | # AWS Proton backend plugin for Backstage 2 | 3 | This backend plugin for Backstage adds functionality to interact with [AWS Proton](https://aws.amazon.com/proton/). The plugin provides: 4 | 5 | - Backend APIs to retrieve the status of an AWS Proton service. 6 | - A scaffolder action to create an AWS Proton service. 7 | 8 | ![AWS Proton Service entity card](../../docs/images/proton-entity-card.png 'AWS Proton Service entity card') 9 | 10 | ## Installation 11 | 12 | See the [AWS Proton plugins for Backstage installation guide](../../docs/install.md). 13 | 14 | ## Usage 15 | 16 | For information about using this plugin, see the following documents: 17 | 18 | - [AWS Proton plugins for Backstage reference](../../docs/reference.md) 19 | - [Tutorial: using the AWS Proton plugins for Backstage](../../docs/tutorial.md) 20 | 21 | ## Development 22 | 23 | For information about developing this plugin locally, see [Developing the AWS Proton plugins for Backstage](../../docs/developing.md). 24 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aws/aws-proton-backend-plugin-for-backstage", 3 | "description": "AWS Proton Backend Plugin for Backstage", 4 | "version": "0.2.2", 5 | "main": "src/index.ts", 6 | "types": "src/index.ts", 7 | "license": "Apache-2.0", 8 | "author": { 9 | "name": "Amazon Web Services", 10 | "url": "http://aws.amazon.com" 11 | }, 12 | "publishConfig": { 13 | "access": "public", 14 | "main": "dist/index.cjs.js", 15 | "types": "dist/index.d.ts" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "github:awslabs/aws-proton-plugins-for-backstage", 20 | "directory": "plugins/aws-proton-backend" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/awslabs/aws-proton-plugins-for-backstage/issues" 24 | }, 25 | "backstage": { 26 | "role": "backend-plugin" 27 | }, 28 | "scripts": { 29 | "start": "backstage-cli package start", 30 | "build": "backstage-cli package build", 31 | "lint": "backstage-cli package lint", 32 | "test": "backstage-cli package test --watch false", 33 | "prepack": "backstage-cli package prepack", 34 | "postpack": "backstage-cli package postpack", 35 | "clean": "backstage-cli package clean" 36 | }, 37 | "dependencies": { 38 | "@aws-sdk/client-proton": "^3.370.0", 39 | "@aws-sdk/util-arn-parser": "^3.310.0", 40 | "@backstage/backend-common": "^0.19.0", 41 | "@backstage/config": "^1.0.8", 42 | "@backstage/integration-aws-node": "^0.1.4", 43 | "@backstage/plugin-scaffolder-backend": "^1.15.0", 44 | "@types/express": "*", 45 | "express": "^4.17.1", 46 | "express-promise-router": "^4.1.0", 47 | "fs-extra": "^11.1.0", 48 | "winston": "^3.7.1", 49 | "yn": "^4.0.0" 50 | }, 51 | "devDependencies": { 52 | "@backstage/cli": "^0.22.8", 53 | "@backstage/dev-utils": "^1.0.16", 54 | "@types/fs-extra": "^11.0.1", 55 | "@types/glob": "^8.0.0", 56 | "@types/jest": "^29.0.0", 57 | "@types/mock-fs": "^4.13.1", 58 | "@types/supertest": "^6.0.2", 59 | "aws-sdk-client-mock": "^3.0.0", 60 | "mock-fs": "^5.1.2", 61 | "msw": "^1.0.0", 62 | "supertest": "^6.2.2" 63 | }, 64 | "files": [ 65 | "dist" 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/actions/create/create.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import mockFs from 'mock-fs'; 15 | 16 | import { PassThrough } from 'stream'; 17 | import os from 'os'; 18 | import { getVoidLogger } from '@backstage/backend-common'; 19 | import { ConfigReader } from '@backstage/config'; 20 | import { 21 | AwsCredentialProvider, 22 | AwsCredentialProviderOptions, 23 | DefaultAwsCredentialsManager, 24 | } from '@backstage/integration-aws-node'; 25 | import { createAwsProtonServiceAction } from '.'; 26 | import { CreateServiceCommand, ProtonClient } from '@aws-sdk/client-proton'; 27 | import { mockClient } from 'aws-sdk-client-mock'; 28 | import { resolve as resolvePath } from 'path'; 29 | 30 | function getMockCredentialProvider(): Promise { 31 | return Promise.resolve({ 32 | sdkCredentialProvider: async () => { 33 | return Promise.resolve({ 34 | accessKeyId: 'MY_ACCESS_KEY_ID', 35 | secretAccessKey: 'MY_SECRET_ACCESS_KEY', 36 | }); 37 | }, 38 | }); 39 | } 40 | const credsProviderMock = jest.spyOn( 41 | DefaultAwsCredentialsManager.prototype, 42 | 'getCredentialProvider', 43 | ); 44 | 45 | const protonMock = mockClient(ProtonClient); 46 | 47 | const root = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir'; 48 | const workspacePath = resolvePath(root, 'my-workspace'); 49 | 50 | describe('aws:proton:create-service', () => { 51 | const mockContext = { 52 | workspacePath, 53 | logger: getVoidLogger(), 54 | logStream: new PassThrough(), 55 | output: jest.fn(), 56 | createTemporaryDirectory: jest.fn(), 57 | }; 58 | 59 | beforeEach(() => { 60 | jest.resetAllMocks(); 61 | 62 | credsProviderMock.mockImplementation((_?: AwsCredentialProviderOptions) => 63 | getMockCredentialProvider(), 64 | ); 65 | 66 | protonMock.reset(); 67 | 68 | protonMock.on(CreateServiceCommand).resolves({ 69 | service: { 70 | arn: 'arn:aws:proton:us-west-2:1234567890:service/test', 71 | name: 'test', 72 | templateName: 'mock-template', 73 | createdAt: new Date(), 74 | lastModifiedAt: new Date(), 75 | status: 'ACTIVE', 76 | spec: 'asdasd', 77 | }, 78 | }); 79 | 80 | mockFs({ 81 | [workspacePath]: { 82 | 'spec.yaml': 'dummy', 83 | }, 84 | }); 85 | }); 86 | 87 | afterEach(() => { 88 | mockFs.restore(); 89 | }); 90 | 91 | it('should call AWS Proton API', async () => { 92 | const action = createAwsProtonServiceAction({ 93 | config: new ConfigReader({}), 94 | }); 95 | await action.handler({ 96 | ...mockContext, 97 | input: { 98 | serviceName: 'serviceName', 99 | branchName: 'main', 100 | region: 'us-west-2', 101 | repository: 'repository', 102 | repositoryConnectionArn: 'arn:mock', 103 | serviceSpecPath: './spec.yaml', 104 | templateMajorVersion: '1', 105 | templateName: 'template', 106 | }, 107 | }); 108 | 109 | expect(protonMock.send.getCall(0).args[0].input).toMatchObject({ 110 | name: 'serviceName', 111 | spec: 'dummy', 112 | }); 113 | 114 | expect(mockContext.output).toHaveBeenCalledWith( 115 | 'arn', 116 | 'arn:aws:proton:us-west-2:1234567890:service/test', 117 | ); 118 | }); 119 | 120 | it('should call AWS Proton API in a specific account', async () => { 121 | const action = createAwsProtonServiceAction({ 122 | config: new ConfigReader({}), 123 | }); 124 | 125 | await action.handler({ 126 | ...mockContext, 127 | input: { 128 | serviceName: 'serviceName', 129 | branchName: 'main', 130 | region: 'us-west-2', 131 | repository: 'repository', 132 | repositoryConnectionArn: 'arn:mock', 133 | serviceSpecPath: './spec.yaml', 134 | templateMajorVersion: '1', 135 | templateName: 'template', 136 | accountId: '1234567890', 137 | }, 138 | }); 139 | 140 | expect(credsProviderMock).toHaveBeenCalledWith({ accountId: '1234567890' }); 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/actions/create/create.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { Config } from '@backstage/config'; 15 | import { createTemplateAction } from '@backstage/plugin-scaffolder-backend'; 16 | import { CreateServiceCommand, ProtonClient } from '@aws-sdk/client-proton'; 17 | import { DefaultAwsCredentialsManager } from '@backstage/integration-aws-node'; 18 | import fs from 'fs-extra'; 19 | 20 | export function createAwsProtonServiceAction(options: { config: Config }) { 21 | const { config } = options; 22 | const awsCredentialsManager = DefaultAwsCredentialsManager.fromConfig(config); 23 | return createTemplateAction<{ 24 | serviceName: string; 25 | templateName: string; 26 | templateMajorVersion: string; 27 | repository?: any; 28 | repositoryConnectionArn?: string; 29 | branchName?: string; 30 | serviceSpecPath: string; 31 | region: string; 32 | accountId?: string; 33 | }>({ 34 | id: 'aws:proton:create-service', 35 | schema: { 36 | input: { 37 | required: [ 38 | 'serviceName', 39 | 'templateName', 40 | 'templateMajorVersion', 41 | 'serviceSpecPath', 42 | 'region', 43 | ], 44 | type: 'object', 45 | properties: { 46 | serviceName: { 47 | type: 'string', 48 | title: 'Service name', 49 | description: 'The name of the AWS Proton service', 50 | }, 51 | templateName: { 52 | type: 'string', 53 | title: 'Service template name', 54 | description: 'The name of the AWS Proton service template', 55 | }, 56 | templateMajorVersion: { 57 | type: 'string', 58 | title: 'Service template major version', 59 | description: 'The major version of the AWS Proton service template', 60 | }, 61 | repository: { 62 | type: 'object', 63 | title: 'Source code repository', 64 | description: 'The source code repository', 65 | }, 66 | repositoryConnectionArn: { 67 | type: 'string', 68 | title: 'Repository connection ARN', 69 | description: 'The AWS CodeStar Connections connection ARN', 70 | }, 71 | branchName: { 72 | type: 'string', 73 | title: 'Branch name', 74 | description: 'The source repository branch', 75 | }, 76 | serviceSpecPath: { 77 | type: 'string', 78 | title: 'Service specification', 79 | description: 80 | 'The filesystem path to the AWS Proton service specification', 81 | }, 82 | region: { 83 | type: 'string', 84 | title: 'AWS region', 85 | description: 'The AWS region', 86 | }, 87 | accountId: { 88 | type: 'string', 89 | title: 'AWS account ID', 90 | description: 'The AWS account ID', 91 | }, 92 | }, 93 | }, 94 | output: { 95 | type: 'object', 96 | properties: { 97 | arn: { 98 | title: 'ARN of the Proton service created', 99 | type: 'string', 100 | }, 101 | }, 102 | }, 103 | }, 104 | async handler(ctx) { 105 | const spec = await fs.readFile( 106 | `${ctx.workspacePath}/${ctx.input.serviceSpecPath}`, 107 | ); 108 | 109 | ctx.logger.info(`Creating AWS Proton service ${ctx.input.serviceName}`); 110 | 111 | const creds = await awsCredentialsManager.getCredentialProvider({ 112 | accountId: ctx.input.accountId, 113 | }); 114 | const client = new ProtonClient({ 115 | region: ctx.input.region, 116 | customUserAgent: 'aws-proton-plugin-for-backstage', 117 | credentialDefaultProvider: () => creds.sdkCredentialProvider, 118 | }); 119 | 120 | const resp = await client.send( 121 | new CreateServiceCommand({ 122 | name: ctx.input.serviceName, 123 | templateName: ctx.input.templateName, 124 | templateMajorVersion: ctx.input.templateMajorVersion, 125 | repositoryId: 126 | ctx.input.repository !== undefined 127 | ? `${ctx.input.repository.owner}/${ctx.input.repository.repo}` 128 | : undefined, 129 | repositoryConnectionArn: ctx.input.repositoryConnectionArn, 130 | branchName: ctx.input.branchName, 131 | spec: spec.toString(), 132 | }), 133 | ); 134 | 135 | if (resp.service !== undefined) { 136 | ctx.logger.info(`Successfully created service ${resp.service.arn}`); 137 | ctx.output('arn', `${resp.service.arn}`); 138 | } 139 | }, 140 | }); 141 | } 142 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/actions/create/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export { createAwsProtonServiceAction } from './create'; 15 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/actions/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export * from './create'; 15 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/api/AwsProtonApi.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { GetServiceCommand, ProtonClient } from '@aws-sdk/client-proton'; 15 | import { getVoidLogger } from '@backstage/backend-common'; 16 | import { 17 | AwsCredentialProvider, 18 | AwsCredentialProviderOptions, 19 | AwsCredentialsManager, 20 | } from '@backstage/integration-aws-node'; 21 | import { mockClient } from 'aws-sdk-client-mock'; 22 | import { AwsProtonApi } from './AwsProtonApi'; 23 | 24 | const protonMock = mockClient(ProtonClient); 25 | let awsCredentialsManager: AwsCredentialsManager; 26 | 27 | function getMockCredentialProvider(): Promise { 28 | return Promise.resolve({ 29 | sdkCredentialProvider: async () => { 30 | return Promise.resolve({ 31 | accessKeyId: 'MY_ACCESS_KEY_ID', 32 | secretAccessKey: 'MY_SECRET_ACCESS_KEY', 33 | }); 34 | }, 35 | }); 36 | } 37 | 38 | describe('AwsProtonApi', () => { 39 | beforeEach(() => { 40 | protonMock.reset(); 41 | 42 | awsCredentialsManager = { 43 | getCredentialProvider: jest.fn((_: AwsCredentialProviderOptions) => 44 | getMockCredentialProvider(), 45 | ), 46 | }; 47 | }); 48 | 49 | it('returns service', async () => { 50 | protonMock.on(GetServiceCommand).resolves({ 51 | service: { 52 | arn: 'arn:aws:proton:us-west-2:1234567890:service/test', 53 | name: 'test', 54 | templateName: 'mock-template', 55 | createdAt: new Date(), 56 | lastModifiedAt: new Date(), 57 | status: 'ACTIVE', 58 | spec: 'asdasd', 59 | }, 60 | }); 61 | 62 | const client = new AwsProtonApi(getVoidLogger(), awsCredentialsManager); 63 | 64 | const service = await client.getProtonService( 65 | 'arn:aws:proton:us-west-2:1234567890:service/test', 66 | ); 67 | 68 | expect(service.name).toEqual('test'); 69 | 70 | expect(awsCredentialsManager.getCredentialProvider).toHaveBeenCalledWith({ 71 | arn: 'arn:aws:proton:us-west-2:1234567890:service/test', 72 | }); 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/api/AwsProtonApi.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { Logger } from 'winston'; 15 | import { 16 | GetServiceCommand, 17 | ProtonClient, 18 | Service, 19 | ServiceInstanceSummary, 20 | paginateListServiceInstances, 21 | } from '@aws-sdk/client-proton'; 22 | import { AwsCredentialsManager } from '@backstage/integration-aws-node'; 23 | import { parse } from '@aws-sdk/util-arn-parser'; 24 | 25 | export class AwsProtonApi { 26 | public constructor( 27 | private readonly logger: Logger, 28 | private readonly awsCredentialsManager: AwsCredentialsManager, 29 | ) {} 30 | 31 | public async getProtonService(arn: string): Promise { 32 | this.logger?.debug(`Fetch Proton Service ${arn}`); 33 | 34 | const { region, resource } = parse(arn); 35 | const segments = resource.split('/'); 36 | if (segments.length < 2) throw new Error('Malformed Proton Service ARN'); 37 | 38 | const serviceName = segments[1]; 39 | 40 | const creds = await this.awsCredentialsManager.getCredentialProvider({ 41 | arn, 42 | }); 43 | const client = new ProtonClient({ 44 | region: region, 45 | customUserAgent: 'aws-proton-plugin-for-backstage', 46 | credentialDefaultProvider: () => creds.sdkCredentialProvider, 47 | }); 48 | const resp = await client.send( 49 | new GetServiceCommand({ 50 | name: serviceName, 51 | }), 52 | ); 53 | return resp.service!; 54 | } 55 | 56 | public async listProtonServiceInstances( 57 | arn: string, 58 | ): Promise { 59 | this.logger?.debug(`Fetch Proton Service ${arn}`); 60 | 61 | const { region, resource } = parse(arn); 62 | const segments = resource.split('/'); 63 | if (segments.length < 2) throw new Error('Malformed Proton Service ARN'); 64 | 65 | const serviceName = segments[1]; 66 | 67 | const creds = await this.awsCredentialsManager.getCredentialProvider({ 68 | arn, 69 | }); 70 | const client = new ProtonClient({ 71 | region: region, 72 | customUserAgent: 'aws-proton-plugin-for-backstage', 73 | credentialDefaultProvider: () => creds.sdkCredentialProvider, 74 | }); 75 | const serviceInstances: ServiceInstanceSummary[] = []; 76 | for await (const page of paginateListServiceInstances( 77 | { client }, 78 | { serviceName }, 79 | )) { 80 | if (page.serviceInstances) { 81 | serviceInstances.push(...page.serviceInstances); 82 | } 83 | } 84 | return serviceInstances; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/api/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export { AwsProtonApi } from './AwsProtonApi'; 15 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export * from './service/router'; 15 | export * from './actions'; 16 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/run.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { getRootLogger } from '@backstage/backend-common'; 15 | import yn from 'yn'; 16 | import { startStandaloneServer } from './service/standaloneServer'; 17 | 18 | const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7007; 19 | const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); 20 | const logger = getRootLogger(); 21 | 22 | startStandaloneServer({ port, enableCors, logger }).catch(err => { 23 | logger.error(err); 24 | process.exit(1); 25 | }); 26 | 27 | process.on('SIGINT', () => { 28 | logger.info('CTRL+C pressed; exiting.'); 29 | process.exit(0); 30 | }); 31 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/service/router.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { getVoidLogger } from '@backstage/backend-common'; 15 | import { ConfigReader } from '@backstage/config'; 16 | import express from 'express'; 17 | import request from 'supertest'; 18 | 19 | import { createRouter } from './router'; 20 | 21 | describe('createRouter', () => { 22 | let app: express.Express; 23 | 24 | beforeAll(async () => { 25 | const config = new ConfigReader({}); 26 | const router = await createRouter({ 27 | logger: getVoidLogger(), 28 | config, 29 | }); 30 | app = express().use(router); 31 | }); 32 | 33 | beforeEach(() => { 34 | jest.resetAllMocks(); 35 | }); 36 | 37 | describe('GET /health', () => { 38 | it('returns ok', async () => { 39 | const response = await request(app).get('/health'); 40 | 41 | expect(response.status).toEqual(200); 42 | expect(response.body).toEqual({ status: 'ok' }); 43 | }); 44 | }); 45 | 46 | describe('GET /service without arn', () => { 47 | it('returns error message', async () => { 48 | const response = await request(app).get('/service'); 49 | 50 | expect(response.status).toEqual(400); 51 | expect(response.body).toEqual({ error: 'No ARN provided' }); 52 | }); 53 | }); 54 | 55 | describe('GET /serviceInstances without arn', () => { 56 | it('returns error message', async () => { 57 | const response = await request(app).get('/serviceInstances'); 58 | 59 | expect(response.status).toEqual(400); 60 | expect(response.body).toEqual({ error: 'No ARN provided' }); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/service/router.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { errorHandler } from '@backstage/backend-common'; 15 | import { Config } from '@backstage/config'; 16 | import { DefaultAwsCredentialsManager } from '@backstage/integration-aws-node'; 17 | import express from 'express'; 18 | import Router from 'express-promise-router'; 19 | import { Logger } from 'winston'; 20 | import { AwsProtonApi } from '../api'; 21 | 22 | export interface RouterOptions { 23 | logger: Logger; 24 | config: Config; 25 | } 26 | 27 | export async function createRouter( 28 | options: RouterOptions, 29 | ): Promise { 30 | const { logger, config } = options; 31 | const awsCredentialsManager = DefaultAwsCredentialsManager.fromConfig(config); 32 | const awsProtonApi = new AwsProtonApi(logger, awsCredentialsManager); 33 | 34 | const router = Router(); 35 | router.use(express.json()); 36 | 37 | router.get('/health', (_, response) => { 38 | logger.info('PONG!'); 39 | response.send({ status: 'ok' }); 40 | }); 41 | 42 | router.get('/service', async (req, res) => { 43 | const arn = req.query.arn?.toString(); 44 | 45 | if (arn === undefined) { 46 | res.status(400).send({ error: 'No ARN provided' }); 47 | return; 48 | } 49 | 50 | const service = await awsProtonApi.getProtonService(arn); 51 | res.status(200).json(service); 52 | }); 53 | 54 | router.get('/serviceInstances', async (req, res) => { 55 | const arn = req.query.arn?.toString(); 56 | 57 | if (arn === undefined) { 58 | res.status(400).send({ error: 'No ARN provided' }); 59 | return; 60 | } 61 | 62 | const service = await awsProtonApi.listProtonServiceInstances(arn); 63 | res.status(200).json(service); 64 | }); 65 | 66 | router.use(errorHandler()); 67 | return router; 68 | } 69 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/service/standaloneServer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { 15 | createServiceBuilder, 16 | loadBackendConfig, 17 | } from '@backstage/backend-common'; 18 | import { Server } from 'http'; 19 | import { Logger } from 'winston'; 20 | import { createRouter } from './router'; 21 | 22 | export interface ServerOptions { 23 | port: number; 24 | enableCors: boolean; 25 | logger: Logger; 26 | } 27 | 28 | export async function startStandaloneServer( 29 | options: ServerOptions, 30 | ): Promise { 31 | const logger = options.logger.child({ service: 'aws-proton-backend' }); 32 | logger.debug('Starting application server...'); 33 | const config = await loadBackendConfig({ logger, argv: process.argv }); 34 | const router = await createRouter({ 35 | logger, 36 | config, 37 | }); 38 | 39 | let service = createServiceBuilder(module) 40 | .setPort(options.port) 41 | .addRouter('/aws-proton-backend', router); 42 | if (options.enableCors) { 43 | service = service.enableCors({ origin: 'http://localhost:3000' }); 44 | } 45 | 46 | return await service.start().catch(err => { 47 | logger.error(err); 48 | process.exit(1); 49 | }); 50 | } 51 | 52 | module.hot?.accept(); 53 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export {}; 15 | -------------------------------------------------------------------------------- /plugins/aws-proton-backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@backstage/cli/config/tsconfig.json", 3 | "include": ["src", "dev", "migrations"], 4 | "exclude": ["node_modules"], 5 | "compilerOptions": { 6 | "outDir": "dist-types", 7 | "rootDir": "." 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /plugins/aws-proton/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); 2 | -------------------------------------------------------------------------------- /plugins/aws-proton/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.2.2](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.2.1...v0.2.2) (2023-04-19) 7 | 8 | **Note:** Version bump only for package @aws/aws-proton-plugin-for-backstage 9 | 10 | 11 | 12 | 13 | 14 | ## [0.2.1](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.2.0...v0.2.1) (2023-01-19) 15 | 16 | **Note:** Version bump only for package @aws/aws-proton-plugin-for-backstage 17 | 18 | 19 | 20 | 21 | 22 | # [0.2.0](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.7...v0.2.0) (2022-12-20) 23 | 24 | **Note:** Version bump only for package @aws/aws-proton-plugin-for-backstage 25 | 26 | ## [0.1.7](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.6...v0.1.7) (2022-12-05) 27 | 28 | **Note:** Version bump only for package @aws/aws-proton-plugin-for-backstage 29 | 30 | ## [0.1.6](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.5...v0.1.6) (2022-11-14) 31 | 32 | ### Bug Fixes 33 | 34 | - run prettier to get consistent code style ([b167e29](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/b167e29abd1b5fd51145bed8ae651a4c750f9654)) 35 | 36 | ## [0.1.5](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.4...v0.1.5) (2022-11-11) 37 | 38 | **Note:** Version bump only for package @aws/aws-proton-plugin-for-backstage 39 | 40 | ## [0.1.4](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.3...v0.1.4) (2022-11-09) 41 | 42 | **Note:** Version bump only for package @aws/aws-proton-plugin-for-backstage 43 | 44 | ## [0.1.3](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.2...v0.1.3) (2022-07-19) 45 | 46 | **Note:** Version bump only for package @aws/aws-proton-plugin-for-backstage 47 | 48 | ## [0.1.2](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.1...v0.1.2) (2022-06-21) 49 | 50 | **Note:** Version bump only for package @aws/aws-proton-plugin-for-backstage 51 | 52 | ## [0.1.1](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.1.0...v0.1.1) (2022-06-21) 53 | 54 | **Note:** Version bump only for package @aws/aws-proton-plugin-for-backstage 55 | 56 | # [0.1.0](https://github.com/awslabs/aws-proton-plugins-for-backstage/compare/v0.0.0...v0.1.0) (2022-06-20) 57 | 58 | ### Bug Fixes 59 | 60 | - Fill in Proton console URL link ([edd5280](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/edd5280e4752fa7f4c9324eb214358dfa87980a0)) 61 | - Move around content on the entity card, add more test fixtures ([4806b41](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/4806b411104a8b0e3a382c8ea6a01bdfa630ab49)) 62 | 63 | ### Features 64 | 65 | - Change the annotation to aws.amazon.com ([8fad850](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/8fad85099d2b0f3d8f4baf74b764cb295e4c9b18)) 66 | - change the plugin names to the AWS namespace ([7ca8aeb](https://github.com/awslabs/aws-proton-plugins-for-backstage/commit/7ca8aeb9e6820cb2508b8cb067b177e8acfc1e5c)) 67 | 68 | ## 0.0.0 (2022-04-04) 69 | 70 | Initial release 71 | -------------------------------------------------------------------------------- /plugins/aws-proton/README.md: -------------------------------------------------------------------------------- 1 | # AWS Proton frontend UI plugin for Backstage 2 | 3 | This frontend UI plugin for Backstage provides an entity card to display the status of an [AWS Proton](https://aws.amazon.com/proton/) service. 4 | 5 | ![AWS Proton Service entity card](../../docs/images/proton-entity-card.png 'AWS Proton Service entity card') 6 | 7 | ## Installation 8 | 9 | See the [AWS Proton plugins for Backstage installation guide](../../docs/install.md). 10 | 11 | ## Usage 12 | 13 | For information about using this plugin, see the following documents: 14 | 15 | - [AWS Proton plugins for Backstage reference](../../docs/reference.md) 16 | - [Tutorial: using the AWS Proton plugins for Backstage](../../docs/tutorial.md) 17 | 18 | ## Development 19 | 20 | For information about developing this plugin locally, see [Developing the AWS Proton plugins for Backstage](../../docs/developing.md). 21 | -------------------------------------------------------------------------------- /plugins/aws-proton/dev/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { createDevApp } from '@backstage/dev-utils'; 15 | import { awsProtonApiRef } from '../src/api'; 16 | import { 17 | awsProtonPlugin, 18 | EntityAWSProtonServiceOverviewCard, 19 | } from '../src/plugin'; 20 | import { EntityProvider } from '@backstage/plugin-catalog-react'; 21 | import { TestApiProvider } from '@backstage/test-utils'; 22 | import React from 'react'; 23 | import { 24 | invalidEntity, 25 | mockEntity, 26 | MockGetServiceAPIError, 27 | MockListServiceInstancesAPIError, 28 | MockMegaService, 29 | MockProtonService, 30 | MockProtonServiceCreateInProgress, 31 | MockProtonServiceNoPipeline, 32 | MockSlowLoad, 33 | } from '../src/mocks'; 34 | 35 | createDevApp() 36 | .addPage({ 37 | path: '/fixture-proton-service', 38 | title: 'Service', 39 | element: ( 40 | 41 | 42 | 43 | 44 | 45 | ), 46 | }) 47 | .addPage({ 48 | path: '/fixture-mega-proton-service', 49 | title: 'Mega Service', 50 | element: ( 51 | 52 | 53 | 54 | 55 | 56 | ), 57 | }) 58 | .addPage({ 59 | path: '/fixture-proton-service-create-in-progress', 60 | title: 'Create In Progress', 61 | element: ( 62 | 65 | 66 | 67 | 68 | 69 | ), 70 | }) 71 | .addPage({ 72 | path: '/fixture-proton-service-no-pipeline', 73 | title: 'No Pipeline', 74 | element: ( 75 | 78 | 79 | 80 | 81 | 82 | ), 83 | }) 84 | .addPage({ 85 | path: '/fixture-invalid-annotation', 86 | title: 'Bad Annotation', 87 | element: ( 88 | 89 | 90 | 91 | 92 | 93 | ), 94 | }) 95 | .addPage({ 96 | path: '/fixture-proton-service-get-service-api-error', 97 | title: 'GetService Error', 98 | element: ( 99 | 100 | 101 | 102 | 103 | 104 | ), 105 | }) 106 | .addPage({ 107 | path: '/fixture-proton-service-list-service-instances-api-error', 108 | title: 'ListServiceInstances Error', 109 | element: ( 110 | 113 | 114 | 115 | 116 | 117 | ), 118 | }) 119 | .addPage({ 120 | path: '/fixture-slow-load', 121 | title: 'Slow Load', 122 | element: ( 123 | 124 | 125 | 126 | 127 | 128 | ), 129 | }) 130 | .registerPlugin(awsProtonPlugin) 131 | .render(); 132 | -------------------------------------------------------------------------------- /plugins/aws-proton/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aws/aws-proton-plugin-for-backstage", 3 | "description": "AWS Proton Plugin for Backstage", 4 | "version": "0.2.2", 5 | "main": "src/index.ts", 6 | "types": "src/index.ts", 7 | "license": "Apache-2.0", 8 | "author": { 9 | "name": "Amazon Web Services", 10 | "url": "http://aws.amazon.com" 11 | }, 12 | "publishConfig": { 13 | "access": "public", 14 | "main": "dist/index.esm.js", 15 | "types": "dist/index.d.ts" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "github:awslabs/aws-proton-plugins-for-backstage", 20 | "directory": "plugins/aws-proton" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/awslabs/aws-proton-plugins-for-backstage/issues" 24 | }, 25 | "backstage": { 26 | "role": "frontend-plugin" 27 | }, 28 | "scripts": { 29 | "build": "backstage-cli package build", 30 | "start": "backstage-cli package start", 31 | "lint": "backstage-cli package lint", 32 | "test": "backstage-cli package test --watch false", 33 | "prepack": "backstage-cli package prepack", 34 | "postpack": "backstage-cli package postpack", 35 | "clean": "backstage-cli package clean" 36 | }, 37 | "dependencies": { 38 | "@aws-sdk/client-proton": "^3.370.0", 39 | "@aws-sdk/util-arn-parser": "^3.310.0", 40 | "@backstage/catalog-model": "^1.4.0", 41 | "@backstage/core-components": "^0.13.2", 42 | "@backstage/core-plugin-api": "^1.5.2", 43 | "@backstage/errors": "^1.2.0", 44 | "@backstage/plugin-catalog-react": "^1.7.0", 45 | "@backstage/theme": "^0.4.0", 46 | "@material-ui/core": "^4.12.2", 47 | "@material-ui/icons": "^4.9.1", 48 | "@material-ui/lab": "4.0.0-alpha.61", 49 | "moment": "^2.29.2", 50 | "react-use": "^17.2.4" 51 | }, 52 | "peerDependencies": { 53 | "react": "^17.0.0", 54 | "react-dom": "^17.0.0" 55 | }, 56 | "devDependencies": { 57 | "@backstage/cli": "^0.22.8", 58 | "@backstage/core-app-api": "^1.8.1", 59 | "@backstage/dev-utils": "^1.0.16", 60 | "@backstage/test-utils": "^1.4.0", 61 | "@testing-library/jest-dom": "^6.1.2", 62 | "@testing-library/react": "^12.1.4", 63 | "@testing-library/user-event": "^14.0.4", 64 | "@types/jest": "^29.0.0", 65 | "@types/node": "^20.2.5", 66 | "cross-fetch": "^4.0.0", 67 | "msw": "^1.0.0" 68 | }, 69 | "files": [ 70 | "dist" 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/api/AWSProtonServiceApi.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { Service, ServiceInstanceSummary } from '@aws-sdk/client-proton'; 15 | import { createApiRef } from '@backstage/core-plugin-api'; 16 | 17 | export const awsProtonApiRef = createApiRef({ 18 | id: 'plugin.awsproton.service', 19 | }); 20 | 21 | export interface AwsProtonApi { 22 | getService({ arn }: { arn: string }): Promise; 23 | 24 | listServiceInstances({ 25 | arn, 26 | }: { 27 | arn: string; 28 | }): Promise; 29 | } 30 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/api/AWSProtonServiceApiClient.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { IdentityApi } from '@backstage/core-plugin-api'; 15 | 16 | import { ConfigApi } from '@backstage/core-plugin-api'; 17 | import { ResponseError } from '@backstage/errors'; 18 | import { Service, ServiceInstanceSummary } from '@aws-sdk/client-proton'; 19 | import { AwsProtonApi } from '.'; 20 | 21 | export class AwsProtonApiClient implements AwsProtonApi { 22 | private readonly configApi: ConfigApi; 23 | private readonly identityApi: IdentityApi; 24 | 25 | public constructor(options: { 26 | configApi: ConfigApi; 27 | identityApi: IdentityApi; 28 | }) { 29 | this.configApi = options.configApi; 30 | this.identityApi = options.identityApi; 31 | } 32 | 33 | async getService({ arn }: { arn: string }): Promise { 34 | const queryString = new URLSearchParams(); 35 | queryString.append('arn', arn); 36 | 37 | const urlSegment = `service?${queryString}`; 38 | 39 | const service = await this.get(urlSegment); 40 | 41 | return service; 42 | } 43 | 44 | async listServiceInstances({ 45 | arn, 46 | }: { 47 | arn: string; 48 | }): Promise { 49 | const queryString = new URLSearchParams(); 50 | queryString.append('arn', arn); 51 | 52 | const urlSegment = `serviceInstances?${queryString}`; 53 | 54 | const service = await this.get(urlSegment); 55 | 56 | return service; 57 | } 58 | 59 | private async get(path: string): Promise { 60 | const baseUrl = `${await this.configApi.getString( 61 | 'backend.baseUrl', 62 | )}/api/aws-proton-backend/}`; 63 | 64 | const url = new URL(path, baseUrl); 65 | 66 | const { token: idToken } = await this.identityApi.getCredentials(); 67 | 68 | const response = await fetch(url.toString(), { 69 | headers: idToken ? { Authorization: `Bearer ${idToken}` } : {}, 70 | }); 71 | 72 | if (!response.ok) { 73 | throw await ResponseError.fromResponse(response); 74 | } 75 | 76 | return response.json() as Promise; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/api/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export * from './AWSProtonServiceApi'; 15 | export * from './AWSProtonServiceApiClient'; 16 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/components/AWSProtonServiceOverview/AWSProtonServiceOverview.test.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import React from 'react'; 15 | import { render, waitFor } from '@testing-library/react'; 16 | import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; 17 | import { EntityProvider } from '@backstage/plugin-catalog-react'; 18 | import { awsProtonApiRef } from '../../api'; 19 | import { 20 | mockEntity, 21 | MockGetServiceAPIError, 22 | MockProtonService, 23 | MockProtonServiceNoPipeline, 24 | } from '../../mocks'; 25 | import { EntityAWSProtonServiceOverviewCard } from '../../plugin'; 26 | 27 | describe('AWSProtonServiceOverview', () => { 28 | it('should render service', async () => { 29 | const rendered = render( 30 | wrapInTestApp( 31 | 32 | 33 | 34 | 35 | , 36 | ), 37 | ); 38 | 39 | await waitFor(() => rendered.getByText('AWS Proton Service'), { 40 | timeout: 3000, 41 | }); 42 | 43 | expect(await rendered.findByText('mock-service')).toBeInTheDocument(); 44 | expect(await rendered.findByText('mock-instance1')).toBeInTheDocument(); 45 | }); 46 | 47 | it('should report error', async () => { 48 | const rendered = render( 49 | wrapInTestApp( 50 | 53 | 54 | 55 | 56 | , 57 | ), 58 | ); 59 | 60 | await waitFor(() => rendered.getByText('AWS Proton Service'), { 61 | timeout: 3000, 62 | }); 63 | 64 | expect( 65 | await rendered.findByText( 66 | 'Error: Could not find arn:aws:proton:us-west-2:1234567890:service/mock-service!', 67 | ), 68 | ).toBeInTheDocument(); 69 | }); 70 | 71 | it('should render service without pipeline', async () => { 72 | const rendered = render( 73 | wrapInTestApp( 74 | 77 | 78 | 79 | 80 | , 81 | ), 82 | ); 83 | 84 | await waitFor(() => rendered.getByText('AWS Proton Service'), { 85 | timeout: 3000, 86 | }); 87 | 88 | expect(await rendered.queryByText('Pipeline Template Version')).toBeNull(); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/components/AWSProtonServiceOverview/AWSProtonServiceOverview.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import React from 'react'; 15 | import { 16 | Typography, 17 | Grid, 18 | Card, 19 | CardHeader, 20 | Divider, 21 | CardContent, 22 | makeStyles, 23 | LinearProgress, 24 | } from '@material-ui/core'; 25 | import { Entity } from '@backstage/catalog-model'; 26 | import moment from 'moment'; 27 | import { 28 | MissingAnnotationEmptyState, 29 | IconLinkVerticalProps, 30 | HeaderIconLinkRow, 31 | TableColumn, 32 | Table, 33 | InfoCard, 34 | ResponseErrorPanel, 35 | StatusOK, 36 | StatusError, 37 | StatusWarning, 38 | StatusAborted, 39 | StatusRunning, 40 | StatusPending, 41 | } from '@backstage/core-components'; 42 | import { useEntity } from '@backstage/plugin-catalog-react'; 43 | import ViewServiceIcon from '@material-ui/icons/Visibility'; 44 | import { AWS_PROTON_SERVICE_ANNOTATION } from '../../constants'; 45 | import { useProtonServiceArnFromEntity } from '../../hooks/useProtonServiceArnFromEntity'; 46 | import { useProtonService } from '../../hooks/useProtonService'; 47 | import { isAWSProtonServiceAvailable } from '../../plugin'; 48 | import { ProtonServiceData } from '../../types'; 49 | import { 50 | DeploymentStatus, 51 | ServiceInstanceSummary, 52 | ServiceStatus, 53 | } from '@aws-sdk/client-proton'; 54 | import { parse } from '@aws-sdk/util-arn-parser'; 55 | 56 | const deploymentStatusComponent = (state: string | undefined) => { 57 | switch (state) { 58 | case DeploymentStatus.SUCCEEDED: 59 | return ( 60 | 61 | Succeeded 62 | 63 | ); 64 | case DeploymentStatus.IN_PROGRESS: 65 | return ( 66 | 67 | In progress 68 | 69 | ); 70 | case DeploymentStatus.FAILED: 71 | return ( 72 | 73 | Failed 74 | 75 | ); 76 | default: 77 | return ( 78 | 79 | Unknown 80 | 81 | ); 82 | } 83 | }; 84 | 85 | const templateVersionComponent = ( 86 | templateMajorVersion: string | undefined, 87 | templateMinorVersion: string | undefined, 88 | ) => { 89 | if (templateMajorVersion === undefined) { 90 | return '-'; 91 | } 92 | return `v${templateMajorVersion}.${templateMinorVersion}`; 93 | }; 94 | 95 | const deploymentTimeComponent = (deploymentTime: Date | undefined) => { 96 | if (deploymentTime === undefined) { 97 | return '-'; 98 | } 99 | return moment(deploymentTime).fromNow(); 100 | }; 101 | 102 | export const ServiceInstanceTable = ({ 103 | serviceData, 104 | }: { 105 | serviceData: ProtonServiceData; 106 | }) => { 107 | const columns: TableColumn[] = [ 108 | { 109 | title: 'Service Instance Name', 110 | field: 'name', 111 | }, 112 | { 113 | title: 'Environment', 114 | field: 'environmentName', 115 | }, 116 | { 117 | title: 'Service Template Version', 118 | field: 'templateName', 119 | render: (row: Partial) => 120 | templateVersionComponent( 121 | row.templateMajorVersion, 122 | row.templateMinorVersion, 123 | ), 124 | }, 125 | { 126 | title: 'Deployment Status', 127 | field: 'deploymentStatus', 128 | render: (row: Partial) => 129 | deploymentStatusComponent(row.deploymentStatus), 130 | }, 131 | { 132 | title: 'Last Successful Deployment', 133 | field: 'lastDeploymentSucceededAt', 134 | render: (row: Partial) => 135 | deploymentTimeComponent(row.lastDeploymentSucceededAt), 136 | }, 137 | ]; 138 | 139 | return ( 140 |
141 | 151 | 152 | ); 153 | }; 154 | 155 | const useStyles = makeStyles(theme => ({ 156 | links: { 157 | margin: theme.spacing(2, 0), 158 | display: 'grid', 159 | gridAutoFlow: 'column', 160 | gridAutoColumns: 'min-content', 161 | gridGap: theme.spacing(3), 162 | }, 163 | label: { 164 | color: theme.palette.text.secondary, 165 | textTransform: 'uppercase', 166 | fontSize: '10px', 167 | fontWeight: 'bold', 168 | letterSpacing: 0.5, 169 | overflow: 'hidden', 170 | whiteSpace: 'nowrap', 171 | }, 172 | value: { 173 | fontWeight: 'bold', 174 | overflow: 'hidden', 175 | lineHeight: '24px', 176 | wordBreak: 'break-word', 177 | }, 178 | description: { 179 | wordBreak: 'break-word', 180 | }, 181 | })); 182 | 183 | const AboutField = ({ 184 | label, 185 | value, 186 | gridSizes, 187 | children, 188 | }: { 189 | label: string; 190 | value?: string | JSX.Element; 191 | gridSizes?: Record; 192 | children?: React.ReactNode; 193 | }) => { 194 | const classes = useStyles(); 195 | 196 | // Content is either children or a string prop `value` 197 | const content = React.Children.count(children) ? ( 198 | children 199 | ) : ( 200 | 201 | {value || `unknown`} 202 | 203 | ); 204 | return ( 205 | 206 | 207 | {label} 208 | 209 | {content} 210 | 211 | ); 212 | }; 213 | 214 | const serviceStatusComponent = (state: string | undefined) => { 215 | switch (state) { 216 | case ServiceStatus.CREATE_IN_PROGRESS: 217 | return ( 218 | 219 | Create in progress 220 | 221 | ); 222 | case ServiceStatus.CREATE_FAILED_CLEANUP_IN_PROGRESS: 223 | return ( 224 | 225 | Create failed (Cleanup in progress) 226 | 227 | ); 228 | case ServiceStatus.CREATE_FAILED_CLEANUP_COMPLETE: 229 | case ServiceStatus.CREATE_FAILED_CLEANUP_FAILED: 230 | case ServiceStatus.CREATE_FAILED: 231 | return ( 232 | 233 | Create failed 234 | 235 | ); 236 | case ServiceStatus.DELETE_FAILED: 237 | return ( 238 | 239 | Delete failed 240 | 241 | ); 242 | case ServiceStatus.DELETE_IN_PROGRESS: 243 | return ( 244 | 245 | Delete in progress 246 | 247 | ); 248 | case ServiceStatus.UPDATE_IN_PROGRESS: 249 | return ( 250 | 251 | Update in progress 252 | 253 | ); 254 | case ServiceStatus.UPDATE_FAILED_CLEANUP_IN_PROGRESS: 255 | return ( 256 | 257 | Update failed (Cleanup in progress) 258 | 259 | ); 260 | case ServiceStatus.UPDATE_FAILED_CLEANUP_COMPLETE: 261 | case ServiceStatus.UPDATE_FAILED_CLEANUP_FAILED: 262 | case ServiceStatus.UPDATE_FAILED: 263 | return ( 264 | 265 | Update failed 266 | 267 | ); 268 | case ServiceStatus.ACTIVE: 269 | return ( 270 | 271 | Active 272 | 273 | ); 274 | default: 275 | return ( 276 | 277 | Unknown 278 | 279 | ); 280 | } 281 | }; 282 | 283 | const OverviewComponent = ({ 284 | serviceData, 285 | }: { 286 | serviceData: ProtonServiceData; 287 | }) => { 288 | const arnParts = parse(serviceData.service.arn!); 289 | const protonConsoleUrl = `https://${arnParts.region}.console.aws.amazon.com/proton/home?region=${arnParts.region}#/services/detail/${serviceData.service.name}`; 290 | 291 | const service = serviceData.service; 292 | 293 | const links: IconLinkVerticalProps[] = [ 294 | { 295 | label: 'View Service', 296 | disabled: false, 297 | icon: , 298 | href: protonConsoleUrl, 299 | }, 300 | ]; 301 | 302 | const classes = useStyles(); 303 | return ( 304 | 305 | AWS Proton Service} 307 | subheader={} 308 | /> 309 | 310 | 311 | 312 | 313 | 318 | {service.name} 319 | 320 | 321 | 325 | 330 | {service.templateName} 331 | 332 | 333 | 337 | {serviceStatusComponent(service.status)} 338 | 339 | {service.pipeline && ( 340 | 341 | 345 | 350 | {deploymentTimeComponent( 351 | service.pipeline?.lastDeploymentSucceededAt, 352 | )} 353 | 354 | 355 | 359 | 364 | {templateVersionComponent( 365 | service.pipeline?.templateMajorVersion, 366 | service.pipeline?.templateMinorVersion, 367 | )} 368 | 369 | 370 | 374 | 379 | {deploymentStatusComponent( 380 | service.pipeline?.deploymentStatus, 381 | )} 382 | 383 | 384 | 385 | )} 386 | 387 | 388 | 389 | 390 | ); 391 | }; 392 | 393 | const AWSProtonServiceOverview = ({ entity }: { entity: Entity }) => { 394 | const { arn } = useProtonServiceArnFromEntity(entity); 395 | 396 | const [serviceData] = useProtonService({ 397 | arn, 398 | }); 399 | if (serviceData.loading) { 400 | return ( 401 | 402 | 403 | 404 | ); 405 | } 406 | if (serviceData.error) { 407 | return ( 408 | 409 | 410 | 411 | ); 412 | } 413 | return ( 414 | <> 415 | {serviceData.service && ( 416 | 417 | )} 418 | 419 | ); 420 | }; 421 | 422 | type Props = { 423 | /** @deprecated The entity is now grabbed from context instead */ 424 | entity?: Entity; 425 | }; 426 | 427 | export const AWSProtonServiceOverviewWidget = (_props: Props) => { 428 | const { entity } = useEntity(); 429 | return !isAWSProtonServiceAvailable(entity) ? ( 430 | 431 | ) : ( 432 | 433 | ); 434 | }; 435 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export const AWS_PROTON_SERVICE_ANNOTATION = 15 | 'aws.amazon.com/aws-proton-service'; 16 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/hooks/useProtonService.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { useAsyncRetry } from 'react-use'; 15 | import { useApi } from '@backstage/core-plugin-api'; 16 | import { awsProtonApiRef } from '../api'; 17 | import { useCallback } from 'react'; 18 | import { ProtonServiceData } from '../types'; 19 | 20 | export function useProtonService({ arn }: { arn: string }) { 21 | const protonServiceApi = useApi(awsProtonApiRef); 22 | 23 | const getService = useCallback( 24 | async () => { 25 | const service = await protonServiceApi.getService({ 26 | arn: arn, 27 | }); 28 | 29 | const serviceInstances = await protonServiceApi.listServiceInstances({ 30 | arn: arn, 31 | }); 32 | 33 | return { 34 | service, 35 | serviceInstances, 36 | }; 37 | }, 38 | [arn], // eslint-disable-line react-hooks/exhaustive-deps 39 | ); 40 | const { 41 | loading, 42 | value: service, 43 | error, 44 | retry, 45 | } = useAsyncRetry(async () => { 46 | return await getService(); 47 | }, []); 48 | 49 | return [ 50 | { 51 | loading, 52 | service, 53 | error, 54 | retry, 55 | }, 56 | ] as const; 57 | } 58 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/hooks/useProtonServiceArnFromEntity.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { validate } from '@aws-sdk/util-arn-parser'; 15 | import { Entity } from '@backstage/catalog-model'; 16 | import { AWS_PROTON_SERVICE_ANNOTATION } from '../constants'; 17 | 18 | export function useProtonServiceArnFromEntity(entity: Entity): { 19 | arn: string; 20 | } { 21 | const arn = 22 | entity.metadata.annotations?.[AWS_PROTON_SERVICE_ANNOTATION] ?? ''; 23 | 24 | if (!validate(arn)) { 25 | throw new Error( 26 | `Value for annotation ${AWS_PROTON_SERVICE_ANNOTATION} was not a valid ARN`, 27 | ); 28 | } 29 | 30 | return { arn }; 31 | } 32 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export * from './plugin'; 15 | export * from './api'; 16 | export * from './components/AWSProtonServiceOverview/AWSProtonServiceOverview'; 17 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/mocks/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | export * from './mocks'; 15 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/mocks/mocks.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { 15 | DeploymentStatus, 16 | Service, 17 | ServiceInstanceSummary, 18 | ServiceStatus, 19 | } from '@aws-sdk/client-proton'; 20 | import { Entity } from '@backstage/catalog-model'; 21 | import { AwsProtonApi } from '../api'; 22 | 23 | export class MockProtonService implements AwsProtonApi { 24 | async getService({ arn }: { arn: string }): Promise { 25 | return { 26 | arn: arn, 27 | name: 'mock-service', 28 | templateName: 'mock-template', 29 | createdAt: new Date(), 30 | lastModifiedAt: new Date(), 31 | status: 'ACTIVE', 32 | spec: 'asdasd', 33 | pipeline: { 34 | arn: 'aasdasd', 35 | createdAt: new Date(), 36 | lastDeploymentAttemptedAt: new Date(), 37 | lastDeploymentSucceededAt: new Date(), 38 | deploymentStatus: 'SUCCEEDED', 39 | templateName: 'mock-template', 40 | templateMajorVersion: '1', 41 | templateMinorVersion: '0', 42 | }, 43 | }; 44 | } 45 | 46 | async listServiceInstances({ 47 | arn, 48 | }: { 49 | arn: string; 50 | }): Promise { 51 | return [ 52 | { 53 | arn: arn, 54 | name: 'mock-instance1', 55 | lastDeploymentAttemptedAt: new Date(), 56 | lastDeploymentSucceededAt: new Date(), 57 | templateName: 'mock-template', 58 | createdAt: new Date(), 59 | serviceName: 'mock-service', 60 | deploymentStatus: DeploymentStatus.SUCCEEDED, 61 | environmentName: 'dev', 62 | templateMajorVersion: '1', 63 | templateMinorVersion: '0', 64 | }, 65 | { 66 | arn: arn, 67 | name: 'mock-instance2', 68 | lastDeploymentAttemptedAt: new Date(), 69 | lastDeploymentSucceededAt: new Date(), 70 | templateName: 'mock-template', 71 | createdAt: new Date(), 72 | serviceName: 'mock-service', 73 | deploymentStatus: DeploymentStatus.IN_PROGRESS, 74 | environmentName: 'prod', 75 | templateMajorVersion: '1', 76 | templateMinorVersion: '0', 77 | }, 78 | ]; 79 | } 80 | } 81 | 82 | export class MockMegaService implements AwsProtonApi { 83 | async getService({ arn }: { arn: string }): Promise { 84 | return { 85 | arn: arn, 86 | name: 'mock-service', 87 | templateName: 'mock-template', 88 | createdAt: new Date(), 89 | lastModifiedAt: new Date(), 90 | status: 'ACTIVE', 91 | spec: 'asdasd', 92 | pipeline: { 93 | arn: 'aasdasd', 94 | createdAt: new Date(), 95 | lastDeploymentAttemptedAt: new Date(), 96 | lastDeploymentSucceededAt: new Date(), 97 | deploymentStatus: 'SUCCEEDED', 98 | templateName: 'mock-template', 99 | templateMajorVersion: '1', 100 | templateMinorVersion: '0', 101 | }, 102 | }; 103 | } 104 | 105 | async listServiceInstances({ 106 | arn, 107 | }: { 108 | arn: string; 109 | }): Promise { 110 | return Array.from({ length: 50 }, (_, i) => { 111 | return { 112 | arn: arn, 113 | name: `mock-instance-${i + 1}`, 114 | lastDeploymentAttemptedAt: new Date(), 115 | lastDeploymentSucceededAt: new Date(), 116 | templateName: 'mock-template', 117 | createdAt: new Date(), 118 | serviceName: 'mock-service', 119 | deploymentStatus: DeploymentStatus.SUCCEEDED, 120 | environmentName: 'dev', 121 | templateMajorVersion: '1', 122 | templateMinorVersion: '0', 123 | }; 124 | }); 125 | } 126 | } 127 | 128 | export class MockProtonServiceCreateInProgress implements AwsProtonApi { 129 | async getService({ arn }: { arn: string }): Promise { 130 | return { 131 | arn: arn, 132 | name: 'mock-service', 133 | templateName: 'mock-template', 134 | createdAt: new Date(), 135 | lastModifiedAt: new Date(), 136 | status: ServiceStatus.CREATE_IN_PROGRESS, 137 | spec: 'asdasd', 138 | pipeline: { 139 | arn: 'aasdasd', 140 | createdAt: new Date(), 141 | lastDeploymentAttemptedAt: new Date(), 142 | lastDeploymentSucceededAt: undefined, 143 | deploymentStatus: DeploymentStatus.IN_PROGRESS, 144 | templateName: 'mock-template', 145 | templateMajorVersion: undefined, 146 | templateMinorVersion: undefined, 147 | }, 148 | }; 149 | } 150 | 151 | async listServiceInstances({ 152 | arn, 153 | }: { 154 | arn: string; 155 | }): Promise { 156 | return [ 157 | { 158 | arn: arn, 159 | name: 'mock-instance1', 160 | lastDeploymentAttemptedAt: new Date(), 161 | lastDeploymentSucceededAt: undefined, 162 | templateName: 'mock-template', 163 | createdAt: new Date(), 164 | serviceName: 'mock-service', 165 | deploymentStatus: DeploymentStatus.IN_PROGRESS, 166 | environmentName: 'dev', 167 | templateMajorVersion: undefined, 168 | templateMinorVersion: undefined, 169 | }, 170 | { 171 | arn: arn, 172 | name: 'mock-instance2', 173 | lastDeploymentAttemptedAt: new Date(), 174 | lastDeploymentSucceededAt: undefined, 175 | templateName: 'mock-template', 176 | createdAt: new Date(), 177 | serviceName: 'mock-service', 178 | deploymentStatus: DeploymentStatus.IN_PROGRESS, 179 | environmentName: 'prod', 180 | templateMajorVersion: undefined, 181 | templateMinorVersion: undefined, 182 | }, 183 | ]; 184 | } 185 | } 186 | 187 | export class MockProtonServiceNoPipeline implements AwsProtonApi { 188 | async getService({ arn }: { arn: string }): Promise { 189 | return { 190 | arn: arn, 191 | name: 'mock-service', 192 | templateName: 'mock-template', 193 | createdAt: new Date(), 194 | lastModifiedAt: new Date(), 195 | status: 'ACTIVE', 196 | spec: 'asdasd', 197 | }; 198 | } 199 | 200 | async listServiceInstances({ 201 | arn, 202 | }: { 203 | arn: string; 204 | }): Promise { 205 | return [ 206 | { 207 | arn: arn, 208 | name: 'mock-instance1', 209 | lastDeploymentAttemptedAt: new Date(), 210 | lastDeploymentSucceededAt: new Date(), 211 | templateName: 'mock-template', 212 | createdAt: new Date(), 213 | serviceName: 'mock-service', 214 | deploymentStatus: DeploymentStatus.SUCCEEDED, 215 | environmentName: 'dev', 216 | templateMajorVersion: '1', 217 | templateMinorVersion: '0', 218 | }, 219 | { 220 | arn: arn, 221 | name: 'mock-instance2', 222 | lastDeploymentAttemptedAt: new Date(), 223 | lastDeploymentSucceededAt: new Date(), 224 | templateName: 'mock-template', 225 | createdAt: new Date(), 226 | serviceName: 'mock-service', 227 | deploymentStatus: DeploymentStatus.IN_PROGRESS, 228 | environmentName: 'prod', 229 | templateMajorVersion: '1', 230 | templateMinorVersion: '0', 231 | }, 232 | ]; 233 | } 234 | } 235 | 236 | export class MockGetServiceAPIError implements AwsProtonApi { 237 | async getService({ arn }: { arn: string }): Promise { 238 | throw new Error(`Could not find ${arn}!`); 239 | } 240 | 241 | async listServiceInstances({ 242 | arn, 243 | }: { 244 | arn: string; 245 | }): Promise { 246 | return [ 247 | { 248 | arn: arn, 249 | name: 'mock-instance1', 250 | lastDeploymentAttemptedAt: new Date(), 251 | lastDeploymentSucceededAt: new Date(), 252 | templateName: 'mock-template', 253 | createdAt: new Date(), 254 | serviceName: 'mock-service', 255 | deploymentStatus: DeploymentStatus.SUCCEEDED, 256 | environmentName: 'dev', 257 | templateMajorVersion: '1', 258 | templateMinorVersion: '0', 259 | }, 260 | ]; 261 | } 262 | } 263 | 264 | export class MockListServiceInstancesAPIError implements AwsProtonApi { 265 | async getService({ arn }: { arn: string }): Promise { 266 | return { 267 | arn: arn, 268 | name: 'mock-service', 269 | templateName: 'mock-template', 270 | createdAt: new Date(), 271 | lastModifiedAt: new Date(), 272 | status: 'ACTIVE', 273 | spec: 'asdasd', 274 | }; 275 | } 276 | 277 | async listServiceInstances({ 278 | arn, 279 | }: { 280 | arn: string; 281 | }): Promise { 282 | throw new Error(`Access denied to ${arn}!`); 283 | } 284 | } 285 | 286 | export class MockSlowLoad implements AwsProtonApi { 287 | async getService({ arn }: { arn: string }): Promise { 288 | return { 289 | arn: arn, 290 | name: 'mock-service', 291 | templateName: 'mock-template', 292 | createdAt: new Date(), 293 | lastModifiedAt: new Date(), 294 | status: 'ACTIVE', 295 | spec: 'asdasd', 296 | pipeline: { 297 | arn: 'aasdasd', 298 | createdAt: new Date(), 299 | lastDeploymentAttemptedAt: new Date(), 300 | lastDeploymentSucceededAt: new Date(), 301 | deploymentStatus: 'SUCCEEDED', 302 | templateName: 'mock-template', 303 | templateMajorVersion: '1', 304 | templateMinorVersion: '0', 305 | }, 306 | }; 307 | } 308 | 309 | async listServiceInstances({ 310 | arn, 311 | }: { 312 | arn: string; 313 | }): Promise { 314 | await new Promise(f => setTimeout(f, 5000)); 315 | return [ 316 | { 317 | arn: arn, 318 | name: 'mock-instance1', 319 | lastDeploymentAttemptedAt: new Date(), 320 | lastDeploymentSucceededAt: new Date(), 321 | templateName: 'mock-template', 322 | createdAt: new Date(), 323 | serviceName: 'mock-service', 324 | deploymentStatus: DeploymentStatus.SUCCEEDED, 325 | environmentName: 'dev', 326 | templateMajorVersion: '1', 327 | templateMinorVersion: '0', 328 | }, 329 | ]; 330 | } 331 | } 332 | 333 | export const mockEntity: Entity = { 334 | apiVersion: 'backstage.io/v1alpha1', 335 | kind: 'Component', 336 | metadata: { 337 | name: 'backstage', 338 | description: 'backstage.io', 339 | annotations: { 340 | 'aws.amazon.com/aws-proton-service': 341 | 'arn:aws:proton:us-west-2:1234567890:service/mock-service', 342 | }, 343 | }, 344 | spec: { 345 | lifecycle: 'production', 346 | type: 'service', 347 | owner: 'user:guest', 348 | }, 349 | }; 350 | 351 | export const invalidEntity: Entity = { 352 | apiVersion: 'backstage.io/v1alpha1', 353 | kind: 'Component', 354 | metadata: { 355 | name: 'backstage', 356 | description: 'backstage.io', 357 | annotations: { 358 | 'aws.amazon.com/aws-proton-service': 'mock-service', 359 | }, 360 | }, 361 | spec: { 362 | lifecycle: 'production', 363 | type: 'service', 364 | owner: 'user:guest', 365 | }, 366 | }; 367 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/plugin.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { awsProtonPlugin } from './plugin'; 15 | 16 | describe('aws-proton', () => { 17 | it('should export plugin', () => { 18 | expect(awsProtonPlugin).toBeDefined(); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/plugin.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { 15 | configApiRef, 16 | createApiFactory, 17 | createComponentExtension, 18 | createPlugin, 19 | identityApiRef, 20 | } from '@backstage/core-plugin-api'; 21 | import { AwsProtonApiClient, awsProtonApiRef } from './api'; 22 | import { AWS_PROTON_SERVICE_ANNOTATION } from './constants'; 23 | import { Entity } from '@backstage/catalog-model'; 24 | 25 | import { rootRouteRef } from './routes'; 26 | 27 | export const isAWSProtonServiceAvailable = (entity: Entity) => 28 | entity?.metadata.annotations?.[AWS_PROTON_SERVICE_ANNOTATION]; 29 | 30 | export const awsProtonPlugin = createPlugin({ 31 | id: 'aws-proton', 32 | 33 | apis: [ 34 | createApiFactory({ 35 | api: awsProtonApiRef, 36 | deps: { configApi: configApiRef, identityApi: identityApiRef }, 37 | factory: ({ configApi, identityApi }) => 38 | new AwsProtonApiClient({ configApi, identityApi }), 39 | }), 40 | ], 41 | routes: { 42 | root: rootRouteRef, 43 | }, 44 | }); 45 | 46 | export const EntityAWSProtonServiceOverviewCard = awsProtonPlugin.provide( 47 | createComponentExtension({ 48 | name: 'EntityAWSProtonOverviewCard', 49 | component: { 50 | lazy: () => 51 | import( 52 | './components/AWSProtonServiceOverview/AWSProtonServiceOverview' 53 | ).then(m => m.AWSProtonServiceOverviewWidget), 54 | }, 55 | }), 56 | ); 57 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/routes.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { createRouteRef } from '@backstage/core-plugin-api'; 15 | 16 | export const rootRouteRef = createRouteRef({ 17 | id: 'aws-proton', 18 | }); 19 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import '@testing-library/jest-dom'; 15 | import 'cross-fetch/polyfill'; 16 | -------------------------------------------------------------------------------- /plugins/aws-proton/src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | import { Service, ServiceInstanceSummary } from '@aws-sdk/client-proton'; 15 | 16 | export type ProtonServiceData = { 17 | service: Service; 18 | serviceInstances: ServiceInstanceSummary[]; 19 | }; 20 | -------------------------------------------------------------------------------- /plugins/aws-proton/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@backstage/cli/config/tsconfig.json", 3 | "include": ["src", "dev"], 4 | "exclude": ["node_modules"], 5 | "compilerOptions": { 6 | "outDir": "dist-types", 7 | "rootDir": "." 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@backstage/cli/config/tsconfig.json", 3 | "include": ["plugins/*/src", "plugins/*/dev"], 4 | "exclude": ["node_modules"], 5 | "compilerOptions": { 6 | "outDir": "dist-types", 7 | "rootDir": "." 8 | } 9 | } 10 | --------------------------------------------------------------------------------