Skip to content

Commit

Permalink
Prow GitHub app migration part II (#290)
Browse files Browse the repository at this point in the history
Issue #, if available:  aws-controllers-k8s/community#1565

A 2nd attempt at deploying changes from #264, previously reverted in #289

Description of changes:

Update Prow deployments to use a GitHub app instead of a GitHub personal access token.

Also, includes addition of the ability to import an existing S3 bucket instead of trying to create one during the `cdk deploy`.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
  • Loading branch information
jljaco authored Jan 31, 2023
1 parent 589f834 commit 9b24d14
Show file tree
Hide file tree
Showing 36 changed files with 584 additions and 443 deletions.
55 changes: 47 additions & 8 deletions infra/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,57 @@

## Deploying the CDK
To deploy the CDK stacks, you must have the appropriate permissions to create
the CloudFormation stack and associated resources in a given AWS account. You
will also need:
- A personal access token for the [Prow bot account](https://github.com/kubernetes/test-infra/blob/master/prow/getting_started_deploy.md#github-bot-account)
- An HMAC token used to validate Github webhooks
- This can be generated using `openssl rand -hex 20`
the CloudFormation stack and associated resources in a given AWS account.

You will also need to **manually** create and configure a GitHub app for Prow as [documented here](https://github.com/kubernetes-sigs/prow/blob/main/site/content/en/docs/getting-started-deploy.md#github-app).

Once the GitHub app is configured, you will need three data elements from the app's settings page to pass to the CDK deployment:

- The app ID
- The app's private RSA key (in PEM format)
- The app's configured webhook secret
- **_NOTE_**: you can generate a valid value for this using `openssl rand -hex 20`

Use the following command to deploy the stack with the included requirements:
```bash
export BOT_PAT='<bot personal access token>'
export WEBHOOK_HMAC='<webhook HMAC>'
export LOGS_BUCKET='<S3 bucket name for logs>' # Optional
export GITHUB_PAT=<your personal access token>
export GITHUB_APP_ID=<App ID from GH app settings>
export GITHUB_APP_PRIVATE_KEY=<GitHub app private RSA key in PEM format>
export GITHUB_APP_CLIENT_ID=<Client ID from GH app settings>
export GITHUB_APP_WEBHOOK_SECRET=<Webhook secret from GH app settings>
export LOGS_BUCKET=<S3 bucket name for logs> # Optional
export LOGS_BUCKET_IMPORT=false # NOTE: optional, use this and set to true if you want to import an existing bucket

export AWS_DEFAULT_REGION=us-west-2

cd test-infra/infra
cdk bootstrap
cdk deploy
```

or, via command line arguments:
```bash
export AWS_DEFAULT_REGION=us-west-2

cd test-infra/infra
cdk bootstrap
cdk deploy \
-c pat="<personal access token>" \
-c app_id="<GitHub app ID>" \
-c client_id="<GitHub app client ID>" \
-c app_private_key="<GitHub app private RSA key in PEM format>" \
-c app_webhook_secret="<GitHub app webhook secret>" \
-c logs_bucket="<S3 bucket name for logs>" \
-c logs_bucket_import=<true|false>
```

An example:
```bash
export AWS_DEFAULT_REGION=us-west-2
cd $GOPATH/src/github.com/aws-controllers-k8s/test-infra/infra
cdk bootstrap
cdk deploy -c pat="12345" -c app_id="123456" -c client_id="12345" \
-c app_private_key="$(cat ./github_app_cert.pem)" \
-c app_webhook_secret="081d23f783c016e91950c92a4fe4f87bfe61ca8b" \
-c logs_bucket="ack-prow-logs-1234567890"
```
8 changes: 6 additions & 2 deletions infra/bin/test-ci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ new TestCIStack(app, 'TestCIStack', {

/* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
clusterConfig: {
botPersonalAccessToken: app.node.tryGetContext('bot_pat') || process.env.BOT_PAT,
webhookHMACToken: app.node.tryGetContext('webhook_hmac') || process.env.WEBHOOK_HMAC
personalAccessToken: app.node.tryGetContext('pat') || process.env.GITHUB_PAT,
appId: app.node.tryGetContext('app_id') || process.env.GITHUB_APP_ID,
appClientId: app.node.tryGetContext('client_id') || process.env.GITHUB_APP_CLIENT_ID,
appPrivateKey: app.node.tryGetContext('app_private_key') || process.env.GITHUB_APP_PRIVATE_KEY,
appWebhookSecret: app.node.tryGetContext('app_webhook_secret') || process.env.GITHUB_APP_WEBHOOK_SECRET
},
logsBucketName: app.node.tryGetContext('logs_bucket') || process.env.LOGS_BUCKET,
logsBucketImport: app.node.tryGetContext('logs_bucket_import') || process.env.LOGS_BUCKET_IMPORT || false,
pvreBucketName: app.node.tryGetContext('pvre_bucket') || process.env.PVRE_BUCKET,
});
25 changes: 0 additions & 25 deletions infra/lib/charts/app-of-apps.yaml

This file was deleted.

21 changes: 0 additions & 21 deletions infra/lib/charts/flux-configuration.ts

This file was deleted.

18 changes: 0 additions & 18 deletions infra/lib/charts/flux.ts

This file was deleted.

24 changes: 0 additions & 24 deletions infra/lib/charts/namespace.ts

This file was deleted.

76 changes: 59 additions & 17 deletions infra/lib/charts/prow-secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,89 @@ import * as constructs from 'constructs';
import * as kplus from 'cdk8s-plus-20';
import { PROW_NAMESPACE, PROW_JOB_NAMESPACE } from '../test-ci-stack';

export interface ProwSecretsChartProps {
readonly botPersonalAccessToken: string;
readonly webhookHMACToken: string;
export interface ProwGitHubSecretsChartProps {
readonly personalAccessToken: string;
readonly appId: string;
readonly appClientId: string;
readonly appPrivateKey: string;
readonly appWebhookSecret: string;
}

export class ProwSecretsChart extends cdk8s.Chart {
readonly botPATSecret: kplus.Secret;
// github token to be used by prowjobs in PROW_JOB_NAMESPACE
readonly prowjobBotPATSecret: kplus.Secret;
readonly webhookHMACSecret: kplus.Secret;
export class ProwGitHubSecretsChart extends cdk8s.Chart {
readonly pat: kplus.Secret;
readonly prowjobPAT: kplus.Secret;

constructor(scope: constructs.Construct, id: string, props: ProwSecretsChartProps) {
readonly token: kplus.Secret;
// github client secret to be used by prowjobs in PROW_JOB_NAMESPACE
readonly prowjobToken: kplus.Secret;
readonly hmacToken: kplus.Secret;

constructor(scope: constructs.Construct, id: string, props: ProwGitHubSecretsChartProps) {
super(scope, id);

if (props.botPersonalAccessToken === undefined || props.webhookHMACToken === undefined) {
throw new Error(`Expected bot personal access token and webhook HMAC token to be specified`);
if (
props.personalAccessToken === undefined ||
props.appPrivateKey === undefined ||
props.appClientId === undefined ||
props.appWebhookSecret === undefined ||
props.appId === undefined) {
throw new Error(`Expected: GitHub bot PAT, bot Webhook HMAC, app ID, client ID, app private key, & app webhook HMAC token`);
}
if (props.appPrivateKey.length < 1500) {
console.error("Found invalid app private key: ", props.appPrivateKey);
throw new Error(`Expected GitHub app private key to be in valid PEM format (and >= 1500 bytes)`);
}

this.botPATSecret = new kplus.Secret(this, 'github-token', {
// a GitHub PAT for use by various scripts for deploying code to repos
this.pat = new kplus.Secret(this, 'github-pat-token', {
stringData: {
'token': props.personalAccessToken
},
metadata: {
name: 'github-pat-token',
namespace: PROW_NAMESPACE
}
});

// a GitHub PAT for use by various Prow jobs
this.prowjobPAT = new kplus.Secret(this, 'prowjob-github-pat-token', {
stringData: {
'token': props.personalAccessToken
},
metadata: {
name: 'prowjob-github-pat-token',
namespace: PROW_JOB_NAMESPACE
}
});

// three pieces of important data from the GitHub app: the private key, the app ID, and the client ID
this.token = new kplus.Secret(this, 'github-token', {
stringData: {
'token': props.botPersonalAccessToken
'cert': props.appPrivateKey,
'appid': props.appId,
'clientid': props.appClientId
},
metadata: {
name: 'github-token',
namespace: PROW_NAMESPACE
}
});

this.prowjobBotPATSecret = new kplus.Secret(this, 'prowjob-github-token', {
this.prowjobToken = new kplus.Secret(this, 'prowjob-github-token', {
stringData: {
'token': props.botPersonalAccessToken
'cert': props.appPrivateKey,
'appid': props.appId,
'clientid': props.appClientId
},
metadata: {
name: 'prowjob-github-token',
namespace: PROW_JOB_NAMESPACE
}
});

this.webhookHMACSecret = new kplus.Secret(this, 'hmac-token', {
this.hmacToken = new kplus.Secret(this, 'hmac-token', {
stringData: {
'hmac': props.webhookHMACToken
'hmac': props.appWebhookSecret
},
metadata: {
name: 'hmac-token',
Expand Down
Loading

0 comments on commit 9b24d14

Please sign in to comment.