Skip to content

Commit

Permalink
Add pattern for ArgoCD workloads in AWS CodeCommit
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuriy Bezsonov committed Jan 5, 2024
1 parent 5443da3 commit c88e7ac
Show file tree
Hide file tree
Showing 8 changed files with 344 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ Patterns:
snyk
starter
gmaestro
workloads-codecommit
```
- Bootstrap your CDK environment.
Expand Down
6 changes: 6 additions & 0 deletions bin/workloads-codecommit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import WorkloadsCodeCommitConstruct from '../lib/workloads-codecommit-construct';
import { configureApp } from '../lib/common/construct-utils';

const app = configureApp();

new WorkloadsCodeCommitConstruct(app, 'workloads-codecommit');
Binary file added docs/patterns/images/argocd-cc-workloads.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/patterns/images/argocd-cc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
189 changes: 189 additions & 0 deletions docs/patterns/workloads-codecommit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# EKS Cluster with ArgoCD and Workloads in private AWS CodeCommit repository

## Objective

This example shows how to provision an EKS cluster with:

- ArgoCD
- Workloads deployed by ArgoCD
- Private AWS CodeCommit repository to store the configurations of workloads
- Setup to trigger ArgoCD projects sync on git push to AWS CodeCommit repository

Pattern source: /lib/workloads-codecommit-construct/index.ts

## Architecture

![Architectural diagram](./images/argocd-cc.png)

To better understand how ArgoCD works with EKS Blueprints, read the EKS Blueprints ArgoCD [Documentation](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/argo-cd/)

- After a push to AWS CodeCommit repository notification trigger calls AWS Lambda
- AWS Lambda calls ArgoCD webhook URL to trigger ArgoCD projects sync

## Prerequisites

Ensure that you have installed the following tools on your machine.

1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)
2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)
3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)
4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)
5. [jq](https://jqlang.github.io/jq/)
6. `make`

## Deploy EKS Cluster with Amazon EKS Blueprints for CDK

1. Clone the repository

```sh
git clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git
cd cdk-eks-blueprints-patterns
```

2. Update npm

```sh
npm install -g npm@latest
```

3. View patterns and deploy workloads-codecommit pattern

```sh
make list
npx cdk bootstrap
make pattern workloads-codecommit deploy
```

## Verify the resources

1. Run the update-kubeconfig command. You should be able to get the command from the CDK output message. More information can be found at https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access

```sh
aws eks update-kubeconfig --name workloads-codecommit-blueprint --region <your region> --role-arn arn:aws:iam::xxxxxxxxx:role/workloads-codecommit-blue-workloadscodecommitbluepr-VH6YOKWPAt5H
```

2. Verify the resources created from the steps above.

```bash
$ kubectl get po -n argocd
NAME READY STATUS RESTARTS AGE
blueprints-addon-argocd-application-controller-0 1/1 Running 0 1h
blueprints-addon-argocd-applicationset-controller-7b78c7fc5dmkx 1/1 Running 0 1h
blueprints-addon-argocd-dex-server-6cf94ddc54-p68pl 1/1 Running 0 1h
blueprints-addon-argocd-notifications-controller-6f6b7d95ckhf6p 1/1 Running 0 1h
blueprints-addon-argocd-redis-b8dbc7dc6-dvbkr 1/1 Running 0 1h
blueprints-addon-argocd-repo-server-66df7f448f-kvwmw 1/1 Running 0 1h
blueprints-addon-argocd-server-584db5f545-8xp48 1/1 Running 0 1h
```

## Give ArgoCD access to AWS CodeCommit

```bash
until kubectl get svc blueprints-addon-argocd-server -n argocd -o json | jq --raw-output '.status.loadBalancer.ingress[0].hostname' | grep -m 1 "elb.amazonaws.com"; do sleep 5 ; done;
export ARGOCD_SERVER=`kubectl get svc blueprints-addon-argocd-server -n argocd -o json | jq --raw-output '.status.loadBalancer.ingress[0].hostname'`

export ARGOCD_USER=argocd-cc
export CC_REPO_NAME=eks-blueprints-workloads-cc

aws iam create-service-specific-credential --user-name $ARGOCD_USER --service-name codecommit.amazonaws.com --no-cli-pager
export CC_REPO_URL=$(aws codecommit get-repository --repository-name $CC_REPO_NAME --query 'repositoryMetadata.cloneUrlHttp' --output text)
export SSC_ID=$(aws iam list-service-specific-credentials --user-name $ARGOCD_USER --query 'ServiceSpecificCredentials[0].ServiceSpecificCredentialId' --output text)
export SSC_USER=$(aws iam list-service-specific-credentials --user-name $ARGOCD_USER --query 'ServiceSpecificCredentials[0].ServiceUserName' --output text)
export SSC_PWD=$(aws iam reset-service-specific-credential --user-name $ARGOCD_USER --service-specific-credential-id $SSC_ID --query 'ServiceSpecificCredential.ServicePassword' --output text)

cat > argocd-workloads-repos-creds.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: repo-creds-platform-https
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repo-creds
stringData:
url: ${CC_REPO_URL}
password: ${SSC_PWD}
username: ${SSC_USER}
EOF

kubectl apply -f argocd-workloads-repos-creds.yaml
rm argocd-workloads-repos-creds.yaml

echo Deployment finished.
echo "AWS CodeCommit Blueprint workloads repository URL: $CC_REPO_URL"
echo "ArgoCD URL: https://$ARGOCD_SERVER"
echo "ArgoCD server user: admin"
echo "ArgoCD admin password: $(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)"
```

## Create notification trigger

```bash
export LAMBDA_ARN=$(aws lambda get-function --function-name eks-blueprints-workloads-cc-webhook | jq -r .Configuration.FunctionArn)

cat > trigger.json <<EOF
[
{
"destinationArn": "${LAMBDA_ARN}",
"branches": [],
"name": "${CC_REPO_NAME}-trigger",
"customData": "${ARGOCD_SERVER}",
"events": [
"all"
]
}
]
EOF

aws codecommit put-repository-triggers --repository-name $CC_REPO_NAME --triggers file://trigger.json --no-cli-pager
rm trigger.json
```

## Populate AWS CodeCommit with Blueprint workloads Sample repository

```bash
pushd ..
git clone https://github.com/aws-samples/eks-blueprints-workloads.git
git clone codecommit::$AWS_REGION://$CC_REPO_NAME
cd $CC_REPO_NAME
git checkout -b main
cd ..
rsync -av eks-blueprints-workloads/ $CC_REPO_NAME --exclude .git
cd $CC_REPO_NAME
git add . && git commit -m "initial commit" && git push --set-upstream origin main
popd
```

ArgoCD will receive notification and will start sync.

![ArgoCD sync](./images/argocd-cc-workloads.png)

## Destroy

To teardown and remove the resources created in this example:

1. Delete "bootstrap-apps" project in ArgoCD UI and wait until ArgoCD delete workloads

2. Delete AWS CodeCommit credentials

```sh
export SSC_ID=$(aws iam list-service-specific-credentials --user-name $ARGOCD_USER --query 'ServiceSpecificCredentials[1].ServiceSpecificCredentialId' --output text)
aws iam delete-service-specific-credential --user-name $ARGOCD_USER --service-specific-credential-id $SSC_ID
export SSC_ID=$(aws iam list-service-specific-credentials --user-name $ARGOCD_USER --query 'ServiceSpecificCredentials[0].ServiceSpecificCredentialId' --output text)
aws iam delete-service-specific-credential --user-name $ARGOCD_USER --service-specific-credential-id $SSC_ID
```

3. Delete deployed resources

```sh
cd cdk-eks-blueprints-patterns
make pattern workloads-codecommit destroy
```

4. Delete cloned repositories (`if necessary`)

```sh
pushd ..
rm -rf eks-blueprints-workloads-cc
rm -rf eks-blueprints-workloads
popd
```
40 changes: 40 additions & 0 deletions lib/workloads-codecommit-construct/WorkloadsCodeCommitRepo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Construct } from 'constructs';
import { NestedStack, NestedStackProps } from 'aws-cdk-lib';
import * as blueprints from '@aws-quickstart/eks-blueprints';
import * as codecommit from 'aws-cdk-lib/aws-codecommit';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';

export default class WorkloadsCodeCommitRepoStack extends NestedStack {
public static builder(userName: string, repoName: string): blueprints.NestedStackBuilder {
return {
build(scope: Construct, id: string, props: NestedStackProps) {
return new WorkloadsCodeCommitRepoStack(scope, id, props, userName, repoName);
}
};
}

constructor(scope: Construct, id: string, props: NestedStackProps, userName: string, repoName: string) {
super(scope, id);

const repo = new codecommit.Repository(this, repoName + '-codecommit-repo', {
repositoryName: repoName,
});

const user = new iam.User(this, userName + '-user-name', {
userName: userName,
});
repo.grantPull(user);

const fn = new lambda.Function(this, repoName + '-webhook', {
runtime: lambda.Runtime.NODEJS_20_X,
functionName: repoName + '-webhook',
description: 'Webhook for ArgoCD on commit to AWS CodeCommit',
handler: 'index.handler',
code: lambda.Code.fromAsset("lib/workloads-codecommit-construct/lambda"),
});

const principal = new iam.ServicePrincipal('codecommit.amazonaws.com');
fn.grantInvoke(principal);
}
}
56 changes: 56 additions & 0 deletions lib/workloads-codecommit-construct/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as blueprints from '@aws-quickstart/eks-blueprints';
import { Construct } from 'constructs';
import WorkloadsCodeCommitRepoStack from './WorkloadsCodeCommitRepo';

/**
* Demonstrates how to use AWS CodeCommmit as a repository for ArgoCD workloads.
*/

export default class WorkloadsCodeCommitConstruct extends Construct {
constructor(scope: Construct, id: string) {
super(scope, id);

const region = process.env.CDK_DEFAULT_REGION!;

const userName = 'argocd-cc';
const repoName = 'eks-blueprints-workloads-cc';

const repoUrl = 'https://git-codecommit.' + region + '.amazonaws.com/v1/repos/' + repoName;

const stackId = `${id}-blueprint`;

const bootstrapRepo : blueprints.ApplicationRepository = {
repoUrl,
targetRevision: 'main',
};

const addOns: Array<blueprints.ClusterAddOn> = [
new blueprints.NestedStackAddOn({
builder: WorkloadsCodeCommitRepoStack.builder(userName, repoName),
id: repoName + "-codecommit-repo-nested-stack"
}),
new blueprints.SecretsStoreAddOn,
new blueprints.ArgoCDAddOn({
bootstrapRepo: {
...bootstrapRepo,
path: 'envs/dev',
},
values: {
server: {
service: {
type: "LoadBalancer"
}
}
}
})
];

blueprints.EksBlueprint.builder()
.account(process.env.CDK_DEFAULT_ACCOUNT!)
.region(process.env.CDK_DEFAULT_REGION)
.addOns(...addOns)

.version('auto')
.build(scope, stackId);
}
}
52 changes: 52 additions & 0 deletions lib/workloads-codecommit-construct/lambda/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* eslint-env node */
const https = require('https'); // eslint-disable-line

/* webhook payload
{
"ref": "refs/heads/main",
"repository": {
"html_url": "https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-blueprints-workloads-cc",
"default_branch": "main"
}
}
*/

exports.handler = async function(event) {
const eventSourceARNarray = event.Records[0].eventSourceARN.split(':');
const repoName = eventSourceARNarray[eventSourceARNarray.length - 1];
const ref = event.Records[0].codecommit.references[0].ref;
const refArray = ref.split('/');
const branch = refArray[refArray.length - 1];
const data = JSON.stringify({
"ref": ref,
"repository": {
"html_url": "https://git-codecommit." + event.Records[0].awsRegion + ".amazonaws.com/v1/repos/" + repoName,
"default_branch": branch
}
});
console.log(data);

const options = {
hostname: event.Records[0].customData,
path: '/api/webhook',
method: 'POST',
port: 443,
headers: {
'Content-Type': 'application/json',
'X-GitHub-Event': 'push',
'Content-Length': data.length,
},
};

const promise = new Promise(function(resolve, reject) {
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;
const req = https.request(options, (res) => {
resolve(res.statusCode);
}).on('error', (e) => {
reject(Error(e));
});
req.write(data);
req.end();
});
return promise;
};

0 comments on commit c88e7ac

Please sign in to comment.