Skip to content

Commit 67fa0f7

Browse files
authored
Merge pull request #52 from dev-dull/ci
Build monthly to uptake patches to base image
2 parents 2ef8088 + eec2e97 commit 67fa0f7

File tree

5 files changed

+262
-7
lines changed

5 files changed

+262
-7
lines changed

.github/build_tests/backend.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
terraform {
2+
backend "http" {
3+
address = "http://localhost:2442/?env=InT"
4+
lock_address = "http://localhost:2442/lock?env=InT"
5+
unlock_address = "http://localhost:2442/unlock?env=InT"
6+
skip_cert_verification = "true"
7+
}
8+
}

.github/build_tests/main.tf

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
terraform {
2+
required_version = ">= 1.2.0"
3+
}
4+
5+
resource "local_file" "test_file" {
6+
content = "This is a local file."
7+
filename = "${path.module}/test.txt"
8+
}
9+
10+
output "file_path" {
11+
value = local_file.test_file.filename
12+
}
13+
14+
terraform {
15+
required_providers {
16+
null = {
17+
source = "hashicorp/null"
18+
version = "~> 3.0"
19+
}
20+
}
21+
}
22+
23+
resource "null_resource" "null_file" {
24+
provisioner "local-exec" {
25+
command = "echo Null resource created at $(date) > null_file.txt"
26+
}
27+
28+
triggers = {
29+
always_run = "${timestamp()}"
30+
}
31+
}
32+
33+
output "null_file_output" {
34+
value = null_resource.null_file.id
35+
}
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
name: Build and Run Docker Image
2+
3+
on:
4+
schedule:
5+
# Refresh monthly to pick up base image updates
6+
- cron: '0 0 1 * *'
7+
push:
8+
branches:
9+
- main
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
outputs:
15+
DOCKER_IMAGE_NAME: ${{ steps.build-image-name.outputs.DOCKER_IMAGE_NAME }}
16+
VERSION: ${{ steps.build-image-name.outputs.VERSION }}
17+
CODELINE: ${{ steps.build-image-name.outputs.CODELINE }}
18+
INSTANCE_NAME: ${{ steps.build-image-name.outputs.INSTANCE_NAME }}
19+
steps:
20+
- name: Checkout Code
21+
uses: actions/checkout@v4
22+
23+
- name: Build Image Name
24+
id: build-image-name
25+
# TODO: going to both ENV and OUTPUTS is probably redundant
26+
run: |
27+
version=$(cat pyterrabacktyl.py | grep __version__ | tr -d "[a-zA-Z ='_]")
28+
29+
# Seems like GITHUB_ENV value can be changed, but value in GITHUB_OUTPUT can NOT
30+
if [ github.ref_name != 'main' ]; then
31+
echo "CODELINE=${{ github.ref_name }}_${version}" | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT
32+
else
33+
echo CODELINE=$(echo ${{ github.ref_name }} | tr '[A-Z]' '[a-z]') | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT
34+
fi
35+
36+
echo DOCKER_IMAGE_NAME=devdull/$(echo ${{ github.event.repository.name }} | tr '[A-Z]' '[a-z]') | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT
37+
echo INSTANCE_NAME=${{ github.ref_name }}_InT | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT
38+
echo "VERSION=${version}" | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT
39+
40+
- name: Build Docker Image
41+
run: |
42+
docker build . --file Dockerfile --tag ${{ env.DOCKER_IMAGE_NAME }}:${{ env.CODELINE }}
43+
echo '## Image Details' >> $GITHUB_STEP_SUMMARY
44+
header=$(docker images | sed -r 's/\s{2,}/|/g' | grep -E '^R' | sed -r 's/^|$/\|/g')
45+
echo "$header" >> $GITHUB_STEP_SUMMARY
46+
echo "$header" | sed -r 's/[^|]/-/g' >> $GITHUB_STEP_SUMMARY
47+
docker images | sed -r 's/\s{2,}/|/g' | grep -E 'pyterrabacktyl' | sed -r 's/^|$/\|/g' >> $GITHUB_STEP_SUMMARY
48+
docker save ${{ env.DOCKER_IMAGE_NAME }}:${{ env.CODELINE }} -o /tmp/${{ github.event.repository.name }}.tar
49+
50+
- name: Upload Docker Image
51+
uses: actions/upload-artifact@v4
52+
with:
53+
name: ${{ github.event.repository.name }}.tar
54+
path: /tmp/${{ github.event.repository.name }}.tar
55+
56+
- name: Create summary header
57+
run: |
58+
echo '## Test Results' | tee -a $GITHUB_STEP_SUMMARY
59+
60+
run_and_test:
61+
runs-on: ubuntu-latest
62+
needs: build
63+
strategy:
64+
matrix:
65+
# TODO: Test OpenTofu
66+
terraform_version:
67+
- '1.10.4' # Latest
68+
- '1.5.6' # Popular
69+
- '1.3.9' # Popular
70+
71+
steps:
72+
- name: Checkout Code
73+
uses: actions/checkout@v4
74+
75+
- name: Download Docker Image
76+
uses: actions/download-artifact@v4
77+
with:
78+
name: ${{ github.event.repository.name }}.tar
79+
path: /tmp
80+
81+
- name: Load Docker Image
82+
run: |
83+
docker load -i /tmp/${{ github.event.repository.name }}.tar
84+
85+
- name: Run Docker Container
86+
run: |
87+
docker run -d --name ${{ needs.build.outputs.INSTANCE_NAME }} -p 2442:2442 ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }}
88+
89+
- name: Wait for PyTerraBackTYL to start
90+
run: |
91+
for ct in {0..9}
92+
do
93+
# '000' gets set if curl fails
94+
STATUS=$(curl -Ss -o /dev/null -w "%{http_code}" http://localhost:2442 || true)
95+
if [ ${STATUS} -ne 200 ]; then
96+
echo "waiting..."
97+
sleep 1
98+
else
99+
exit 0
100+
fi
101+
done
102+
exit 1
103+
104+
- name: Set up Terraform
105+
uses: hashicorp/setup-terraform@v3
106+
with:
107+
terraform_version: ${{ matrix.terraform_version }}
108+
109+
# TODO: nicely formatted summary output doesn't show the failures
110+
# Maybe just write a bunch of shell scripts and exec them here
111+
# Loop over them and report the name ($0), results (✅, ❌)
112+
- name: Set headers for test output
113+
run: |
114+
echo '## Terraform ${{ matrix.terraform_version }}' >> $GITHUB_STEP_SUMMARY
115+
COLS="|Test Name|Status|"
116+
echo "${COLS}" >> /tmp/summary
117+
echo "${COLS}" | sed -r 's/[^|]/-/g' >> /tmp/summary
118+
119+
- name: Run 'happy-path' Test
120+
run: |
121+
cd .github/build_tests
122+
terraform init
123+
terraform plan
124+
terraform apply --auto-approve
125+
docker logs ${{ needs.build.outputs.INSTANCE_NAME }}
126+
echo TFSTATE=$(curl -sS http://localhost:2442/?env=InT) | tee -a $GITHUB_ENV
127+
echo "|Happy Path|✅ Success|" >> /tmp/summary
128+
129+
- name: Validate Terraform State
130+
# Validate that Terraform state saved in PyTerraBackTYL matches the content of the file generated by Terraform
131+
run: |
132+
cd .github/build_tests
133+
TF_CONTENT=$(echo '${{ env.TFSTATE }}' | jq -r '.resources[].instances[].attributes.content | select(. != null)')
134+
FILE_CONTENT=$(cat test.txt)
135+
if [[ "${TF_CONTENT}" != "${FILE_CONTENT}" ]]; then
136+
echo ${TF_CONTENT} != ${FILE_CONTENT} >&2
137+
exit 1
138+
fi
139+
echo -e "|State Saved|✅ Success|" >> /tmp/summary
140+
141+
- name: Validate Terraform State Changed
142+
# The ID of the null resource should change with every apply. Verify PyTerraBackTYL is saving the updated state
143+
run: |
144+
cd .github/build_tests
145+
CURRENT_NULL_RESOURCE_ID=$(echo '${{env.TFSTATE}}' | jq -r '.resources[] | select(.type == "null_resource") | .instances[].attributes.id')
146+
terraform apply --auto-approve
147+
NEW_NULL_RESOURCE_ID=$(curl -sS http://localhost:2442/?env=InT | jq -r '.resources[] | select(.type == "null_resource") | .instances[].attributes.id')
148+
[ -n "${NEW_NULL_RESOURCE_ID}" ]
149+
[ -n "${CURRENT_NULL_RESOURCE_ID}" ]
150+
[ "${CURRENT_NULL_RESOURCE_ID}" -ne "${NEW_NULL_RESOURCE_ID}" ]
151+
echo -e "|State Changed|✅ Success|" >> /tmp/summary
152+
153+
- name: Run Terraform Test, Locked ENV
154+
# Manually lock the environment and capture how terraform exited
155+
id: prelock_InT
156+
continue-on-error: true
157+
run: |
158+
curl -X LOCK -sS http://localhost:2442/lock?env=InT
159+
cd .github/build_tests
160+
terraform apply --auto-approve
161+
162+
- name: Verify Command Failed Successfully
163+
# Verify that terraform failed with a lock error when trying to run against a locked environment
164+
run: |
165+
if [ "${{ steps.prelock_InT.outcome }}" != "success" ]; then
166+
curl -X UNLOCK -sS http://localhost:2442/unlock?env=InT
167+
echo "|Locked Environment is Blocking|✅ Success|" >> /tmp/summary
168+
else
169+
# Picked confusing wording just to be silly
170+
echo "Step unexpectedly failed to fail as expected."
171+
exit 1
172+
fi
173+
174+
- name: Show results, Clean Up
175+
run: |
176+
cat /tmp/summary | tee -a $GITHUB_STEP_SUMMARY
177+
# Hopefully this is being nice to GHA infra and not just wasting CPU cycles
178+
docker rm -f ${{ needs.build.outputs.INSTANCE_NAME }} || true
179+
docker rmi ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} || true
180+
181+
tag_and_push:
182+
runs-on: ubuntu-latest
183+
needs:
184+
- build
185+
- run_and_test
186+
steps:
187+
- name: Download Docker Image
188+
uses: actions/download-artifact@v4
189+
with:
190+
name: ${{ github.event.repository.name }}.tar
191+
path: /tmp
192+
193+
- name: Load Docker Image
194+
run: |
195+
docker load -i /tmp/${{ github.event.repository.name }}.tar
196+
197+
- name: Docker Login
198+
run: |
199+
echo '${{ secrets.DOCKERHUB_PASSWORD }}' | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
200+
echo '## Push image to DockerHub (v${{ needs.build.outputs.VERSION }})' >> $GITHUB_STEP_SUMMARY
201+
202+
- name: Tag Docker Image
203+
if: github.ref_name != 'main'
204+
run: |
205+
docker push ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }}
206+
echo "${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }}" >> $GITHUB_STEP_SUMMARY
207+
208+
- name: Tag and Push Latest
209+
if: github.ref_name == 'main'
210+
run: |
211+
docker tag ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:latest
212+
docker tag ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }} ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.VERSION }}
213+
docker push ${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:latest
214+
echo "${{ needs.build.outputs.DOCKER_IMAGE_NAME }}:${{ needs.build.outputs.CODELINE }}" >> $GITHUB_STEP_SUMMARY

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ creds.txt
66
backends/zenoss_post_processor.py
77
ssl/private.key
88
ssl/public.key
9+
.github/build_tests/.terraform
10+
.github/build_tests/*.txt
11+
.github/build_tests/*.lock.hcl

Dockerfile

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
1-
FROM ubuntu
1+
FROM python:3-slim
22
ENV PTBT_WORKDIR="/opt/pyterrabacktyl"
33
ENV PTBT_DATADIR="${PTBT_WORKDIR}/data"
44
ENV PTBT_USER="tfbackendsvc"
55
COPY . "${PTBT_WORKDIR}"
66
RUN mkdir "${PTBT_DATADIR}"
77

8-
RUN apt-get update
9-
RUN apt-get dist-upgrade -y
10-
RUN apt-get install python3 python3-pip python3-setuptools net-tools openssh-client sudo git -y
11-
RUN apt-get autoremove --purge -y
12-
RUN adduser "${PTBT_USER}" --system
8+
RUN adduser "${PTBT_USER}" --system --home /home/${PTBT_USER}
139
RUN chown -Rh $PTBT_USER: "${PTBT_WORKDIR}"
1410
RUN chown -Rh $PTBT_USER: "${PTBT_DATADIR}"
15-
RUN echo "${PTBT_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
1611
USER "${PTBT_USER}"
1712
RUN pip3 install pyyaml jsonpath flask GitPython --user
1813
EXPOSE 2442

0 commit comments

Comments
 (0)