diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 27b7235..e21d4d9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,7 +35,7 @@ jobs: run: make lint - name: Stub GitHub App credentials (required for validation) - run: cd ./examples/with-backstage && STUB_FILE=1 node create-gh-app/index.js + run: cd ./examples/with-backstage && docker run --rm -e STUB_FILE=1 -v $(pwd):/pwd ghcr.io/humanitec-architecture/create-gh-app - name: Terraform Validate run: make validate diff --git a/examples/with-backstage/README.md b/examples/with-backstage/README.md index 86f6da7..21a0b70 100644 --- a/examples/with-backstage/README.md +++ b/examples/with-backstage/README.md @@ -5,24 +5,30 @@ Provisions the AWS reference architecture connected to Humanitec and installs Ba ## Prerequisites * The same prerequisites as the [base reference architecture](../../README.md#prerequisites), plus the following items. -* A GitHub organization and permission to create new repositories in it. Go to https://github.com/account/organizations/new to create a new org (the "Free" option is fine). Note: is has to be an organization, a free account is not sufficient. +* A GitHub organization and permission to create new repositories in it. Go to to create a new org (the "Free" option is fine). Note: is has to be an organization, a free account is not sufficient. * Create a classic github personal access token with `repo`, `workflow`, `delete_repo` and `admin:org` scope [here](https://github.com/settings/tokens). * Set the `GITHUB_TOKEN` environment variable to your token. - ``` + + ```bash export GITHUB_TOKEN="my-github-token" ``` + * Set the `GITHUB_ORG_ID` environment variable to your GitHub organization ID. - ``` + + ```bash export GITHUB_ORG_ID="my-github-org-id" ``` + * [Node.js](https://nodejs.org) installed locally. -* Install the GitHub App for Backstage into your GitHub organization using `node create-gh-app/index.js`. Follow the instructions. - * “All repositories” ~> Install - * “Okay, […] was installed on the […] account.” ~> You can close the window and server. +* Install the GitHub App for Backstage into your GitHub organization + * Run `docker run --rm -it -e GITHUB_ORG_ID -v $(pwd):/pwd -p 127.0.0.1:3000:3000 ghcr.io/humanitec-architecture/create-gh-app` ([image source](https://github.com/humanitec-architecture/create-gh-app/)) and follow the instructions: + * “All repositories” ~> Install + * “Okay, […] was installed on the […] account.” ~> You can close the window and server. ## Usage Follow the same steps as for the [base layer](../../README.md#usage), applying these modifications: + * Execute `cd ./examples/with-backstage` after cloning the repo. Execute all subsequent commands in this directory. * In particular, use the `./examples/with-backstage/terraform.tfvars.example` file as the basis for your `terraform.tfvars` file. It defines additional variables needed to setup and configure Backstage. @@ -32,31 +38,38 @@ Check for the existence of key elements of the backstage module. This is a subse 1. Perform the [verification steps of the base installation](../../README.md) if you have not already done so. 2. Verify the existence of the Backstage Application in your Humanitec Organization: + ``` curl -s https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/backstage \ --header "Authorization: Bearer ${HUMANITEC_TOKEN}" ``` + This should output a JSON formatted representation of the Application like so: + ``` {"id":"backstage","name":"backstage","created_at":"2023-10-02T13:44:27Z","created_by":"s-d3e94a0e-8b53-29f9-b666-27548b7e06e0","envs":[{"id":"development","name":"Development","type":"development"}]} ``` + You can also check for the Application in the [Humanitec Platform Orchestrator UI](https://app.humanitec.io). 3. Connect to your EKS cluster via `kubectl`. See the [AWS documentation](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html) or use this command: + ``` aws eks update-kubeconfig --region --name ref-arch ``` + 4. Get the elements in the newly created Kubernetes namespace: + ``` kubectl get all -n backstage-development ``` + You should see - - a `deployment`, `replicaset`, running `pod`, and `service` for Backstage - - a `statefulset`, running `pod`, and `service` for PostgreSQL database used by Backstage. + * a `deployment`, `replicaset`, running `pod`, and `service` for Backstage + * a `statefulset`, running `pod`, and `service` for PostgreSQL database used by Backstage. Note: it may take up to ten minutes after the `terraform apply` completed until you actually see those resources. The Backstage application needs to built and deployed via a GitHub action out of the newly created repository in your GitHub organization. - ## Cleaning up Once you are finished with the reference architecture, you can remove all provisioned infrastrcuture and the resource definitions created in Humanitec with the following: diff --git a/examples/with-backstage/create-gh-app/index.js b/examples/with-backstage/create-gh-app/index.js deleted file mode 100644 index 553c073..0000000 --- a/examples/with-backstage/create-gh-app/index.js +++ /dev/null @@ -1,153 +0,0 @@ -// Small CLI tool to create a GitHub App for Backstage -// -// Heavily inspired by https://github.com/backstage/backstage/blob/master/packages/cli/src/commands/create-github-app/ - -const http = require('http'); -const crypto = require('crypto'); -const fs = require('fs/promises') - -const hostname = '127.0.0.1'; -const port = 3000; - -const FORM_PAGE = ` - - -
- - -
- - - -`; - - -let baseUrl; - - -const webhookId = crypto -.randomBytes(15) -.toString('base64') -.replace(/[\+\/]/g, ''); - -const webhookUrl = `https://smee.io/${webhookId}`; - -const handleIndex = (req, res, GITHUB_ORG_ID) => { - const encodedOrg = encodeURIComponent(GITHUB_ORG_ID); - const actionUrl = `https://github.com/organizations/${encodedOrg}/settings/apps/new`; - - - res.statusCode = 200; - const manifest = { - default_events: ['create', 'delete', 'push', 'repository'], - default_permissions: { - members: 'read', - administration: 'write', - contents: 'write', - metadata: 'read', - pull_requests: 'write', - issues: 'write', - workflows: 'write', - checks: 'read', - actions_variables: 'write', - secrets: 'write', - environments: 'write', - }, - name: `backstage-${GITHUB_ORG_ID}`, - url: 'https://backstage.io', - description: 'GitHub App for Backstage', - public: false, - redirect_url: `${baseUrl}/callback`, - hook_attributes: { - url: webhookUrl, - active: false, - }, - }; - - const manifestJson = JSON.stringify(manifest).replace(/\"/g, '"'); - - let body = FORM_PAGE; - body = body.replace('MANIFEST_JSON', manifestJson); - body = body.replace('ACTION_URL', actionUrl); - - res.setHeader('content-type', 'text/html'); - res.end(body); -} - - -const writeConfigFile = async (data, webhookUrl) => { - const fileName = `github-app-credentials.json`; - const content = JSON.stringify({ - name: data.name, - slug: data.slug, - appId: data.id, - webhookUrl: webhookUrl, - clientId: data.client_id, - clientSecret: data.client_secret, - webhookSecret: data.webhook_secret, - privateKey: data.pem, - }, null, 2) - - await fs.writeFile(fileName, content); - - console.log(`Created ${fileName}, you can close the server now.`) -} - -const handleCallback = async (req, res) => { - const url = new URL(req.url, `http://${req.headers.host}`); - const conversionRes = await fetch(`https://api.github.com/app-manifests/${encodeURIComponent(url.searchParams.get('code'))}/conversions`, { - method: 'POST', - }); - - if (conversionRes.status !== 201) { - const body = await conversionRes.text(); - res.statusCode = conversionRes.status; - res.end(body); - } - - const data = await conversionRes.json(); - - await writeConfigFile(data, webhookUrl); - - res.writeHead(302, { Location: `${data.html_url}/installations/new` }); - res.end(); -} - -if (process.env.STUB_FILE === '1') { - writeConfigFile({ - name: 'stub', - slug: 'stub', - id: 'stub', - client_id: 'stub', - client_secret: 'stub', - webhook_secret: 'stub', - pem: 'stub', - }, 'https://smee.io/stub'); - - return; -} - -const GITHUB_ORG_ID = process.env.GITHUB_ORG_ID; -if (!GITHUB_ORG_ID) { - console.error('Please export GITHUB_ORG_ID'); - process.exit(1); -} - -const server = http.createServer((req, res) => { - if (req.url === '/') { - handleIndex(req, res, GITHUB_ORG_ID); - } else if (req.url.startsWith('/callback?')) { - handleCallback(req, res); - } else { - res.statusCode = 404; - res.end('Not found, url: ' + req.url); - } -}); - -server.listen(port, hostname, () => { - baseUrl = `http://${hostname}:${port}`; - - console.log(`Open ${baseUrl}`); -});