Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added github workflow, made the staging environment deployable #137

Merged
merged 26 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f266978
copied github actions and bake from hatm
nickkats Feb 22, 2024
427f252
changed build -> frontend
nickkats Feb 22, 2024
fcea865
added NODE_VERSION variable to bake
nickkats Feb 22, 2024
1d1922f
added default to node_version within docker-bake
nickkats Feb 22, 2024
9d790e3
added args to targets
nickkats Feb 22, 2024
996bfb6
removed default for NODE_VERSION within bake file
nickkats Feb 22, 2024
587c4e8
updated "target" for frontend target
nickkats Feb 22, 2024
084f664
updated the client dockerfile
nickkats Feb 22, 2024
6ee40f9
added push to deploy workflow
nickkats Feb 23, 2024
3bab6f9
Added a "build" stage to the deploy workflow
nickkats Feb 23, 2024
754d083
added account id to deploy build
nickkats Feb 23, 2024
c1e9fcd
added a noop nginx frontend
nickkats Feb 23, 2024
45662dd
trying a build stage for frontend
nickkats Feb 23, 2024
7f1fb3d
fixed the name of the nginx stage
nickkats Feb 23, 2024
0ede507
made 'deploy' job dependent on 'build' job
nickkats Feb 23, 2024
f6ff8e0
Added a configurable base url to the backend
nickkats Feb 23, 2024
3571df3
added the base url to the workflows, client, and server
nickkats Feb 23, 2024
d7bbc4f
fixed a spacing issue
nickkats Feb 23, 2024
df8209d
added a newline
nickkats Feb 23, 2024
c7ef493
using proper var source
nickkats Feb 23, 2024
bfd29b9
back to vars
nickkats Feb 23, 2024
222bd28
added 'build' target back to build job
nickkats Feb 23, 2024
e7c0893
Renamed the variable for frontend
nickkats Feb 23, 2024
751a551
Removed CI workflow
nickkats Feb 23, 2024
3ddb3e9
more cleaning
nickkats Feb 23, 2024
e4b0013
fixed spacing for server code
nickkats Feb 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
### :scroll: Description

- Describe the general shape of this PR (new feature? refactor? bug fix? one-line change?)
<!--- Add your answer here -->

- Describe what changes are being made
<!--- Add your answer here -->

- Describe why these changes are being made
<!--- Add your answer here -->

- List the use cases and edge cases relevant to this PR
<!--- Add your answer here -->

- Describe how errors will be handled. How will we know if this code breaks in production
<!--- Add your answer here -->

- Describe any external libraries/dependencies added or removed in this PR
<!--- Add your answer here -->

- Describe any security risks are there regarding this change
<!--- Add your answer here -->

- Describe how you tested these changes
<!--- Add your answer here -->

- Link to relevant external documentation
<!--- Add your answer here -->
<!--- Example: API docs, architecture diagrams, MDN docs -->


--------------------------
### :clipboard: Mandatory Checklist

- [x] Example of a checked item (please remove when creating your Pull Request)

- [ ] Linked to the Github Issues being addressed using the right sidebar :arrow_right:
- [ ] Have you discussed these changes with the project leader(s)?
- [ ] Do all variable and function names communicate what they do?
- [ ] Were all the changes commented and / or documented?
- [ ] Is the PR the right size? (If the PR is too large to review, it might be good to break it up into multiple PRs.)
- [ ] Does all work in progress, temporary, or debugger code have a TODO comment with links to Github issues?
- [ ] *If you changed the user interface, did you add before and after screenshots to below?*

--------------------------
### :framed_picture: Screenshots and Screen Recordings

#### Before
<!--- Add before screenshots here -->


#### After
<!--- Add after screenshots here -->


--------------------------
### :blue_book: Glossary
- PR = Pull Request
132 changes: 132 additions & 0 deletions .github/scripts/argocd_deploy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import logging
import typing as t
from argparse import ArgumentParser, Namespace
from json import loads
from logging import Formatter, StreamHandler
from math import floor
from os import environ, getenv
from subprocess import run as subproccess_run
from sys import exit, stdout


# Wrapper for ArgoCD CLI
# https://argo-cd.readthedocs.io/en/stable/user-guide/commands/argocd/
class ArgoCDeployer:
def __init__(self, application: str, docker_tag: str) -> None:
self.application = application or getenv('ARGO_CD_APPLICATION', environ['DEPLOYMENT'])
self.docker_tag = docker_tag or environ['DOCKER_TAG']
self.image_tag_parameter = getenv('IMAGE_TAG_PARAMETER', 'global.image.tag=')
self.argo_cd_credentials = [
f'--username={environ["ARGO_CD_USERNAME"]}',
f'--password={environ["ARGO_CD_PASSWORD"]}',
]

def _execute_cli(self, command: t.List[str], return_stdout: bool = False) -> t.Optional[str]:
command = ['argocd'] + command
LOG.info(' '.join(command).replace(environ['ARGO_CD_PASSWORD'], 'redacted'))
output = subproccess_run(command, capture_output=True, encoding='utf-8')
if output.returncode != 0:
LOG.fatal(f'ArgoCD CLI failed {command=} returncode={output.returncode} standard_error={output.stderr}')
if return_stdout:
return output.stdout

def login(self):
self._execute_cli(
command=[
'login',
'--grpc-web',
environ['ARGO_CD_SERVER'],
*self.argo_cd_credentials,
]
)

def update_image_tag(self):
args = []
parameters = [
f'{self.image_tag_parameter}{self.docker_tag}',
]
for parameter in parameters:
args.extend(['--parameter', parameter])
self._execute_cli(command=['app', 'set', self.application, *args])

def sync_app(self, preview: bool = False, timeout: int = 1200):
args = [
'--assumeYes',
'--prune',
'--retry-backoff-max-duration',
f'{floor(timeout/60)}m',
'--timeout',
str(timeout + 30),
]
if preview:
args.append('--dry-run')
self._execute_cli(command=['app', 'sync', self.application, *args])

def preview_deploy(self):
self.sync_app(preview=True)

def wait_health(self):
args = [
'--health',
'--sync',
'--timeout',
'600',
]
self._execute_cli(command=['app', 'wait', self.application, *args])

def verify_health(self):
args = [
'--output',
'json',
]
info = self._execute_cli(command=['app', 'get', self.application, *args], return_stdout=True)
info = loads(info)
status = info['status']
if status['health']['status'] == 'Degraded':
LOG.critical(
f'Application is unhealthy, aborting deploy\nCheck Application https://ARGO_CD_SERVER/applications/argocd/{self.application}?view=network&resource='
)
exit(1)
sync_status = status['sync']['status']
if sync_status not in ['Synced', 'OutofSync']:
LOG.info('Application non-deployable SyncStatus={sync_status} - Will try to wait before aborting')
self.wait_health()


def Logger(logger_name):
logger = logging.getLogger(logger_name)

logger.setLevel(logging.INFO)

standard_output = StreamHandler(stdout)
standard_output.setFormatter(
Formatter(
'{asctime} {name} {levelname:8} {message}',
'%Y-%m-%d %H:%M:%S',
'{',
)
)
logger.addHandler(standard_output)
return logger


if __name__ == '__main__':
LOG = Logger('argocd.deploy')

parser = ArgumentParser(
prog='ArgoCDeployer',
description='Deploy ArgoCD Applications utilizing argocd CLI',
)
# TODO: Convert arguments to non-positional args and enforce required=True
# Requires CICD changes where command is utilized
parser.add_argument('--application', help='ArgoCD Application Name i.e., foobar-dev')
parser.add_argument('--docker_tag', help='Docker Tag to be deployed, usually the full Git SHA')
args: Namespace = parser.parse_args()

argocd = ArgoCDeployer(application=args.application, docker_tag=args.docker_tag)
argocd.login()
argocd.verify_health()
argocd.update_image_tag()
argocd.sync_app()

LOG.info(f'Deploy succeeded {args.application} docker_tag={args.docker_tag}')
3 changes: 3 additions & 0 deletions .github/scripts/requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
boto3
boto3-stubs[ecr]
botocore
39 changes: 39 additions & 0 deletions .github/scripts/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile requirements.in
#
boto3==1.34.7
# via -r requirements.in
boto3-stubs[ecr]==1.34.7
# via -r requirements.in
botocore==1.34.7
# via
# -r requirements.in
# boto3
# s3transfer
botocore-stubs==1.34.7
# via boto3-stubs
jmespath==1.0.1
# via
# boto3
# botocore
mypy-boto3-ecr==1.34.0
# via boto3-stubs
python-dateutil==2.8.2
# via botocore
s3transfer==0.10.0
# via boto3
six==1.16.0
# via python-dateutil
types-awscrt==0.20.0
# via botocore-stubs
types-s3transfer==0.10.0
# via boto3-stubs
typing-extensions==4.9.0
# via
# boto3-stubs
# mypy-boto3-ecr
urllib3==2.0.7
# via botocore
92 changes: 92 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
name: deploy
run-name: ${{ github.event.inputs.env }} deploy ${{ github.ref_name }}
on:
workflow_dispatch:
inputs:
env:
type: environment
description: 'ArgoCD Deployed environment'
ref:
description: 'Branch, Tag, or Full SHA'
required: true
default: 'master'

concurrency:
group: ${{ github.event.inputs.env }}

env:
REFERENCE: ${{ github.event.pull_request.head.sha || github.event.push.head_commit.id }}

jobs:
build:
environment:
name: ${{ github.event.inputs.env }}
url: ${{ vars.PUBLIC_URL }}
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
strategy:
matrix:
image: [build, backend]
steps:
- uses: actions/checkout@v4
with:
ref: ${{ env.REFERENCE }}
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions
role-session-name: gha
aws-region: us-west-2
- uses: docker/setup-buildx-action@v3
- id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
with:
mask-password: 'true'
- run: make build-${{ matrix.image }}
env:
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
LOG_LEVEL: ${{ vars.SERVER_LOG_LEVEL }}
REACT_APP_API_SERVER: ${{ vars.REACT_APP_API_SERVER }}
PUBLIC_URL: ${{ vars.PUBLIC_URL }}
REACT_APP_API_BASE_URL: ${{ vars.API_BASE_URL }}
API_BASE_URL: ${{ vars.API_BASE_URL }}


deploy:
needs: build
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
environment:
name: ${{ github.event.inputs.env }}
url: ${{ vars.PUBLIC_URL }}
env:
ENVIRONMENT: ${{ github.event.inputs.env }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.ref }}
- uses: clowdhaus/argo-cd-action/@main
with:
version: 2.10.0
command: version
options: --client
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- run: pip install -r .github/scripts/requirements.txt
- name: deploy
run: |-
python .github/scripts/argocd_deploy.py \
--application=heartofthevalley-${{ env.ENVIRONMENT }} \
--docker_tag=$(git rev-parse HEAD)
env:
ARGO_CD_SERVER: ${{ secrets.ARGO_CD_SERVER }}
ARGO_CD_USERNAME: ${{ secrets.ARGO_CD_USERNAME }}
ARGO_CD_PASSWORD: ${{ secrets.ARGO_CD_PASSWORD }}
REACT_APP_API_BASE_URL: ${{ vars.API_BASE_URL }}
API_BASE_URL: ${{ vars.API_BASE_URL }}
Loading