diff --git a/.github/workflows/k8s-deploy.yml b/.github/workflows/k8s-deploy.yml new file mode 100644 index 00000000..7a3e557f --- /dev/null +++ b/.github/workflows/k8s-deploy.yml @@ -0,0 +1,234 @@ +name: Deploy K8s Preview + +on: + pull_request: + branches: [ develop ] + types: [ opened, reopened, synchronize ] + push: + branches: [ develop ] + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + DOCKER_REGISTRY: ghcr.io + DOCKER_PACKAGE: site-composite + + KUBE_CONFIG_DATA: ${{ secrets.KUBECONFIG_BASE64 }} + KUBE_NAMESPACE: code-for-philly + KUBE_HOSTNAME: codeforphilly.sandbox.k8s.phl.io + + DATABASE_NAME: codeforphilly + + HAB_LICENSE: accept-no-persist + HAB_ORIGIN: codeforphilly + +jobs: + + k8s-deploy: + runs-on: ubuntu-latest + steps: + + - name: Cancel superseded runs + uses: styfle/cancel-workflow-action@0.7.0 + with: + access_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure environment + run: | + if [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then + RELEASE_NAME="pr-$(jq --raw-output .pull_request.number "${GITHUB_EVENT_PATH}")" + RELEASE_TRANSIENT='true' + else + RELEASE_NAME="latest" + RELEASE_TRANSIENT='false' + fi + + echo "Using RELEASE_NAME=${RELEASE_NAME}" + echo "RELEASE_NAME=${RELEASE_NAME}" >> $GITHUB_ENV + + echo "Using RELEASE_TRANSIENT=${RELEASE_TRANSIENT}" + echo "RELEASE_TRANSIENT=${RELEASE_TRANSIENT}" >> $GITHUB_ENV + + DOCKER_REPOSITORY="${GITHUB_REPOSITORY,,}" + + echo "Using DOCKER_REPOSITORY=${DOCKER_REPOSITORY}" + echo "DOCKER_REPOSITORY=${DOCKER_REPOSITORY}" >> $GITHUB_ENV + + - name: Create Github Deployment + uses: bobheadxi/deployments@v0.4.3 + id: deployment + with: + step: start + token: ${{ secrets.GITHUB_TOKEN }} + env: '${{ env.RELEASE_NAME }}' + ref: '${{ github.head_ref }}' + transient: ${{ env.RELEASE_TRANSIENT }} + logs: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + no_override: false + + - uses: actions/checkout@v2 + + - name: 'Initialize Chef Habitat environment' + uses: JarvusInnovations/habitat-action@action/v1 + with: + deps: | + jarvus/hologit + + - id: site-projection + name: 'Project holobranch: emergence-site' + uses: JarvusInnovations/hologit@actions/projector/v1 + with: + # use HEAD checked out above by checkout action + ref: HEAD + fetch: false + holobranch: emergence-site + + - id: fixtures-projection + name: 'Project holobranch: fixtures' + uses: JarvusInnovations/hologit@actions/projector/v1 + with: + # use HEAD checked out above by checkout action + ref: HEAD + fetch: false + holobranch: fixtures + + - id: helm-projection + name: 'Project holobranch: helm-chart' + uses: JarvusInnovations/hologit@actions/projector/v1 + with: + # use HEAD checked out above by checkout action + ref: HEAD + fetch: false + holobranch: helm-chart + + - name: Build & push Docker image + uses: whoan/docker-build-with-cache-action@v5 + with: + dockerfile: Dockerfile + username: ${{ github.actor }} + password: ${{ env.GITHUB_TOKEN }} + registry: ${{ env.DOCKER_REGISTRY }} + image_name: ${{ env.DOCKER_REPOSITORY }}/${{ env.DOCKER_PACKAGE }} + image_tag: ${{ env.RELEASE_NAME }} + build_extra_args: | + --build-arg=SITE_TREE=${{ steps.site-projection.outputs.tree }} + --build-arg=SITE_VERSION=0.0.0-${{ env.RELEASE_NAME }} + --build-arg=SOURCE_COMMIT=${{ github.sha }} + --build-arg=SOURCE_TAG=${{ env.RELEASE_NAME }} + --build-arg=HAB_LICENSE=${{ env.HAB_LICENSE }} + + - name: Configure kubectl + run: | + set -e + test -e ~/.kube || mkdir ~/.kube + printf '%s' "$KUBE_CONFIG_DATA" | base64 -d > ~/.kube/config + + - name: Deploy instance via Helm template + run: | + release_hostname="${RELEASE_NAME}.${KUBE_HOSTNAME}" + + echo "Ensuring current context is namespace ${KUBE_NAMESPACE}" + kubectl config set-context --current --namespace="${KUBE_NAMESPACE}" + + echo "Listing pods existing before deploy" + kubectl get pods \ + -l app.kubernetes.io/instance="${RELEASE_NAME}" \ + --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' \ + | sort \ + | tee ./.pods-before + + echo "Extracting projected helm-chart to temporary directory" + temp_dir=$(mktemp -d) + git archive --format=tar "${{ steps.helm-projection.outputs.tree }}" | ( cd "${temp_dir}" && tar -xf - ) + + echo "Using helm upgrade to apply helm-chart to release ${RELEASE_NAME}" + helm upgrade "${RELEASE_NAME}" "${temp_dir}" \ + --install \ + --namespace "${KUBE_NAMESPACE}" \ + --set site.name="${RELEASE_NAME}" \ + --set site.title="laddr/${RELEASE_NAME}" \ + --set site.image.repository="${DOCKER_REGISTRY}/${DOCKER_REPOSITORY}/${DOCKER_PACKAGE}" \ + --set site.image.tag="${RELEASE_NAME}" \ + --set ingress.enabled=true \ + --set site.canonicalHostname="${release_hostname}" \ + --set site.displayErrors=true \ + --set hab.license=accept-no-persist + + echo "Listing pods existing after deploy" + kubectl get pods \ + -l app.kubernetes.io/instance="${RELEASE_NAME}" \ + --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' \ + | sort \ + | tee ./.pods-after + + echo "Deleting stale pods to force image refresh" + comm -12 ./.pods-before ./.pods-after \ + | xargs --no-run-if-empty kubectl delete pod + + - name: Wait for Deployment to be ready + timeout-minutes: 10 + run: | + until kubectl rollout status deployment "${RELEASE_NAME}" 2>/dev/null >/dev/null; do + echo -n "." + sleep .5 + done + + - name: Find new Pod + run: | + POD_NAME=$( + kubectl get pod \ + -l app.kubernetes.io/instance="${RELEASE_NAME}" \ + -o jsonpath='{.items[0].metadata.name}' + ) + + echo "Using POD_NAME=${POD_NAME}" + echo "POD_NAME=${POD_NAME}" >> $GITHUB_ENV + + - name: Wait For Pod to be ready + timeout-minutes: 5 + run: kubectl wait --for condition=ready "pod/${POD_NAME}" --timeout=30s + + - name: Wait for MySQL to be Ready + timeout-minutes: 5 + run: | + until kubectl exec "${POD_NAME}" -- hab pkg exec "${HAB_ORIGIN}/${DOCKER_PACKAGE}" mysqladmin ping; do + sleep .5 + done + + - name: Load fixtures into database + run: | + echo "Dropping any existing database..." + kubectl exec "${POD_NAME}" -- \ + hab pkg exec "${HAB_ORIGIN}/${DOCKER_PACKAGE}" \ + mysqladmin drop "${DATABASE_NAME}" --force \ + || true + + echo "Creating an empty database..." + kubectl exec "${POD_NAME}" -- \ + hab pkg exec "${HAB_ORIGIN}/${DOCKER_PACKAGE}" \ + mysqladmin create "${DATABASE_NAME}" + + echo "Loading fixtures..." + ( + for fixture_file in $(git ls-tree -r --name-only ${{ steps.fixtures-projection.outputs.tree }}); do + git cat-file -p "${{ steps.fixtures-projection.outputs.tree }}:${fixture_file}" + done + ) | kubectl exec -i "${POD_NAME}" -- \ + hab pkg exec "${HAB_ORIGIN}/${DOCKER_PACKAGE}" \ + mysql "${DATABASE_NAME}" + + echo "Running migrations..." + kubectl exec "${POD_NAME}" -- \ + hab pkg exec "${HAB_ORIGIN}/${DOCKER_PACKAGE}" \ + emergence-console-run migrations:execute --all + + - name: Update Github Deployment + uses: bobheadxi/deployments@v0.4.3 + if: ${{ always() }} + with: + step: finish + token: ${{ secrets.GITHUB_TOKEN }} + status: ${{ job.status }} + deployment_id: ${{ steps.deployment.outputs.deployment_id }} + env_url: 'https://${{ env.RELEASE_NAME}}.${{ env.KUBE_HOSTNAME }}/' + logs: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' diff --git a/.github/workflows/k8s-destroy.yml b/.github/workflows/k8s-destroy.yml new file mode 100644 index 00000000..1093338e --- /dev/null +++ b/.github/workflows/k8s-destroy.yml @@ -0,0 +1,38 @@ +name: Destroy K8s Preview + +on: + pull_request: + branches: [ develop ] + types: [ closed ] + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + KUBE_CONFIG_DATA: ${{ secrets.KUBECONFIG_BASE64 }} + KUBE_NAMESPACE: code-for-philly + + RELEASE_NAME: pr-${{ github.event.number }} + +jobs: + + k8s-destroy: + runs-on: ubuntu-latest + steps: + + - name: Configure kubectl + run: | + test -e ~/.kube || mkdir ~/.kube + printf '%s' "$KUBE_CONFIG_DATA" | base64 -d > ~/.kube/config + + - name: Delete PR Deployment + run: | + kubectl config set-context --current --namespace="${KUBE_NAMESPACE}" + kubectl delete deployment,replicaset,ingress,all -l "app.kubernetes.io/instance=${RELEASE_NAME}" + kubectl delete secret "${RELEASE_NAME}-tls" + + - name: Deactivate Github Deployment + uses: bobheadxi/deployments@v0.4.3 + with: + step: deactivate-env + token: ${{ secrets.GITHUB_TOKEN }} + env: '${{ env.RELEASE_NAME }}' diff --git a/.holo/sources/laddr.toml b/.holo/sources/laddr.toml index eb6ab5fa..da1e17d9 100644 --- a/.holo/sources/laddr.toml +++ b/.holo/sources/laddr.toml @@ -1,3 +1,3 @@ [holosource] url = "https://github.com/CodeForPhilly/laddr" -ref = "refs/tags/v3.0.3" +ref = "refs/tags/v3.0.5" diff --git a/Dockerfile b/Dockerfile index cbe9c74b..c98b8f7c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,13 +54,13 @@ RUN hab pkg exec core/hab-plan-build hab-plan-build /src/habitat/composite FROM habitat as runtime # configure persistent volumes -RUN hab pkg exec core/coreutils mkdir -p '/hab/svc/mysql/data' '/hab/svc/codeforphilly/data' '/hab/svc/nginx/files' \ - && hab pkg exec core/coreutils chown hab:hab -R '/hab/svc/mysql/data' '/hab/svc/codeforphilly/data' '/hab/svc/nginx/files' +RUN hab pkg exec core/coreutils mkdir -p '/hab/svc/mysql/data' '/hab/svc/site/data' '/hab/svc/nginx/files' \ + && hab pkg exec core/coreutils chown hab:hab -R '/hab/svc/mysql/data' '/hab/svc/site/data' '/hab/svc/nginx/files' # configure entrypoint -VOLUME ["/hab/svc/mysql/data", "/hab/svc/codeforphilly/data", "/hab/svc/nginx/files"] +VOLUME ["/hab/svc/mysql/data", "/hab/svc/site/data", "/hab/svc/nginx/files"] ENTRYPOINT ["hab", "sup", "run"] -CMD ["codeforphilly/codeforphilly-composite"] +CMD ["codeforphilly/site-composite"] # install .hart artifact from builder stage COPY --from=projector /hab/cache/artifacts/$HAB_ORIGIN-* /hab/cache/artifacts/ diff --git a/habitat/composite/plan.sh b/habitat/composite/plan.sh index 00b36161..c7ed7f9d 100644 --- a/habitat/composite/plan.sh +++ b/habitat/composite/plan.sh @@ -1,4 +1,4 @@ -composite_app_pkg_name=codeforphilly +composite_app_pkg_name=site pkg_name="${composite_app_pkg_name}-composite" pkg_origin=codeforphilly pkg_maintainer="Code for Philly " diff --git a/habitat/plan.sh b/habitat/plan.sh index a329f724..8beddd65 100644 --- a/habitat/plan.sh +++ b/habitat/plan.sh @@ -1,4 +1,4 @@ -pkg_name=codeforphilly +pkg_name=site pkg_origin=codeforphilly pkg_maintainer="Code for Philly " pkg_scaffolding=emergence/scaffolding-site