Search
⌃K

Custom Event Hooks

Velocity's Run Policy annotation allows users to hook a custom execution to environment creation, update, and destruction.
These custom events are defined as Kubernetes Jobs. By default, a K8s Job will run only once -- upon environment creation.
However, you can change this default behavior and trigger a custom event on environment creation and update with the velocity.tech.v1/runPolicy:always annotation.
You can also run any clean-up action either immediately before or after your environment's services are torn down with the velocity.tech.v1/runPolicy: destroy/beforeAll and velocity.tech.v1/runPolicy: destroy/afterAll annotations respectfully.
To demonstrate this functionality, we'll create an AWS Cloudfront distribution, which will be tied to a Velocity environment but will exist outside the scope of the environment. Then, we will ensure that this Cloudfront distribution is torn down when the associated environment is destroyed with a K8s Job that includes the velocity.tech.v1/runPolicy annotation.
NOTE: this functionality can extend to any resource you'd like to associate with your Velocity environment creation, update or destroy actions.

1. Create the example environment

Prerequisites:
Update your AWS IAM with the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl",
"cloudfront:GetDistribution",
"cloudfront:ListDistributions",
"cloudfront:UpdateDistribution",
"cloudfront:CreateDistribution",
"cloudfront:GetDistributionConfig",
"cloudfront:DeleteDistribution"
],
"Resource": "*"
}
]
}
Create the environment:
veloctl env create -f https://raw.githubusercontent.com/techvelocity/velocity-blueprints/main/examples/custom-event-hooks.yaml

Understanding the included elements

As shown above, we are creating three distinct K8s Jobs and one S3 Bucket. job-seed-s3 will upload our example HTML and CSS files into the bucket. job-init-cloudfront will create the Cloudfront distribution that will serve the uploaded files, and job-cleanup-cloudfront contains the custom cleanup hook that will be triggered at environment destruction.
The following YAML manifests will be applied to your Velocity environment.

Create an AWS S3 Bucket

First, there is an S3 bucket that will store the data that the Cloudfront distribution will serve.
apiVersion: s3.services.k8s.aws/v1alpha1
kind: Bucket
metadata:
annotations:
velocity.tech.v1/id: bucket # Velocity service identifier
velocity.tech.v1/exports-bucket-name: path="spec.name" # Velocity annotation to export the bucket name for other services to use
name: "{velocity.v1.generate:cloudResourcePrefix(seed=bucket)}-{velocity.v1.envName}-demo-bucket"
spec:
name: "{velocity.v1.generate:cloudResourcePrefix(seed=bucket)}-{velocity.v1.envName}-demo-bucket"

Seed the S3 Bucket with data

Next, we have a Job that will seed the S3 bucket with data that we'll pass as a K8s configMap.
kind: ConfigMap
apiVersion: v1
metadata:
name: configmap-seed-s3
data:
index.html: <!DOCTYPE html>
<html lang="en">
<head>
<title>Hello world!</title>
<meta charset="utf-8">
<link rel="stylesheet" href="./style.css"/>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
style.css: |
body {
background: #236192;
font-family: Helvetica, Arial, sans-serif;
color: #ffffff;
max-width: 75em;
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
---
apiVersion: batch/v1
kind: Job
metadata:
name: job-seed-s3
annotations:
velocity.tech.v1/id: job-seed-s3 # Velocity service identifier
velocity.tech.v1/dependsOn: bucket # Velocity dependencies declaration
spec:
template:
metadata:
name: job-seed-s3
spec:
restartPolicy: Never
containers:
- name: "seed-s3"
image: amazon/aws-cli
command: [/bin/bash, -c]
args:
- |
aws s3 cp /scripts/index.html s3://$(echo $BUCKET_NAME)/index.html \
--acl public-read && \
aws s3 cp /scripts/style.css s3://$(echo $BUCKET_NAME)/style.css --acl public-read
envFrom:
- secretRef:
name: aws-cred-developer
env:
- name: BUCKET_NAME
value: "{velocity.v1:bucket.exports.bucket-name}"
- name: AWS_REGION
value: "{velocity.v1.aws.region}"
volumeMounts:
- mountPath: /scripts
name: seed-s3-vol
volumes:
- name: seed-s3-vol
configMap:
name: configmap-seed-s3 name: configmap-seed-s3
The envFrom.secretRef value holds a set of keys that denote en vars for the AWS cli to pickup -- specifically, your AWS secret key, access key and session token.

Create an AWS Cloudfront distribution

Next, there is a K8s Job that will create the Cloudfront distribution.
apiVersion: batch/v1
kind: Job
metadata:
name: job-init-cloudfront
annotations:
velocity.tech.v1/id: job-init-cloudfront # Velocity service identifier
velocity.tech.v1/dependsOn: bucket # Velocity dependencies declaration
spec:
template:
metadata:
name: job-init-cloudfront
spec:
restartPolicy: Never
containers:
- name: "init-cloudfront"
image: amazon/aws-cli
command: ["/bin/bash", "-c"]
args:
- |-
set -e
yum install jq -y
echo "Creating cloudfront distribution with an S3 bucket origin..."
ORIGIN_DOMAIN_NAME=$BUCKET_NAME.s3.eu-central-1.amazonaws.com
aws cloudfront create-distribution \
--origin-domain-name $ORIGIN_DOMAIN_NAME \
--default-root-object index.html > distribution.json
echo "Deploying Cloudfront Distribution..."
DIST_ID=$(cat distribution.json | jq -r '.Distribution.Id')
aws cloudfront wait distribution-deployed --id $DIST_ID
echo "Cloudfront Distribution deployed successfully"
envFrom:
- secretRef:
name: aws-cred-developer
env:
- name: BUCKET_NAME
value: "{velocity.v1:bucket.exports.bucket-name}"
- name: AWS_REGION
value: "{velocity.v1.aws.region}"

Create the custom event hook

Finally, there is a K8s Job that will tear down the Cloudfront distribution on environment destruction.
apiVersion: batch/v1
kind: Job
metadata:
name: job-cleanup-cloudfront
annotations:
velocity.tech.v1/id: job-cleanup-cloudfront # Velocity service identifier
velocity.tech.v1/dependsOn: bucket # Velocity dependencies declaration
velocity.tech.v1/runPolicy: destroy/afterAll
spec:
template:
metadata:
name: job-cleanup-cloudfront
spec:
restartPolicy: Never
containers:
- name: "cleanup-cloudfront"
image: amazon/aws-cli
command: ["/bin/bash", "-c"]
args:
- |-
set -e
yum install jq -y
echo "Disabling Cloudfront Distribution..."
QUERY="(DistributionList.Items[].{ID: Id, OriginDomainName: Origins.Items[0].DomainName}[?contains(OriginDomainName, '$BUCKET_NAME')] | [0]).ID"
CF_ID=$(aws cloudfront list-distributions --query "$QUERY" --output text)
aws cloudfront get-distribution-config --id $CF_ID > config.json
cat config.json | jq '.DistributionConfig | .Enabled = false' > disabled-config.json
CF_ETAG=$(cat config.json | jq -r '.ETag')
aws cloudfront update-distribution --id $CF_ID --if-match $CF_ETAG \
--distribution-config "file://$(pwd)/disabled-config.json" > disabled-config-res.json
echo "Waiting for Cloudfront Distribution to be ready for deletion..."
aws cloudfront wait distribution-deployed --id $CF_ID
echo "Destroying Cloudfront Distribution..."
CF_ETAG_DISABLED=$(cat disabled-config-res.json | jq -r '.ETag')
aws cloudfront delete-distribution --id $CF_ID --if-match $CF_ETAG_DISABLED
echo "Finished destroying Cloudfront Distribution"
envFrom:
- secretRef:
name: aws-cred-developer
env:
- name: BUCKET_NAME
value: "{velocity.v1:bucket.exports.bucket-name}"
- name: AWS_REGION
value: "{velocity.v1.aws.region}"

2. Trigger the custom event hook

When we run the following command, the above custom event hook, job-cleanup-cloudfront, will be kicked off after all services in the environment have been torn down, because it includes the velocity.tech.v1/runPolicy: destroy/afterAll annotation.
veloctl env destroy

Summary

Custom events in Velocity are defined as Kubernetes Jobs. By default, Jobs will run once upon environment creation. However, this default behavior can be modified with the velocity.tech.v1/runPolicy: annotation, such that custom events can be triggered at event creation, update, and destruction as needed.

Next Steps: