Path to Production

Core Platform Path to Production

The P2P enables you to focus on building your business logic and tests without wasting time on CI/CD tooling setup. All your custom logic should be added to your Makefile rather than directly in GitHub actions.

All you need to do is implement the steps in the Makefile in your repository and let the Core Platform do everything else.

Not ready to implement a step yet? Just leave it blank!

Represent change as an Immutable Deployable Versioned Artifact

Regardless of what your change is for production, an application, terraform infrastructure, we build it into an Immutable Deployable Versioned Artifact, specfically an OCI image and promote it through the stages of the P2P.

Pipeline as a contract

  • The P2P is the contract that every application agrees to adhere to for continuous delivery
  • It is light touch in that it doesn’t mandate many steps, just the most important types of testing
    • You can use whatever tools you want from each step

p2p.png p2p.png

Quality Gates

The P2P consists of the following quality gates:

  • Fast Feedback: What you can do in a few minutes
    • Build
      • Unit tests
      • Static Verification
    • Deployed stubbed functional tests
    • Deployed stubbed non-functional tests
    • Deployed integration tests
  • Extended Tests
    • Slower tests such as peak load tests
  • Once a version has been promoted from Extended tests it is ready for production

Two key concepts for the P2P are:

  • Versioning: The P2P promoted an immutable, versioned artifact through the pipeline
  • Promotion: Each time a quality gate is passed the immutable, versioned artifact is promoted

P2P Interface: giving you control

Assuming you’ve started with a software template via corectl you already have a Makefile with the following tasks. All you need to do is implement them for your application and the P2P will execute them at the right time.

p2p-build: ## Build phase
    echo "##### EXECUTING P2P-BUILD #####"

p2p-functional: ## Execute functional tests
    echo "##### EXECUTING P2P-FUNCTIONAL #####"

p2p-nft:  ## Execute non-functional tests
    echo "##### EXECUTING P2P-NFT #####"

p2p-integration:  ## Execute integration tests
    echo "##### EXECUTING P2P-INTEGRATION #####"

p2p-extended-test: ## Runs extended tests
    echo "##### EXECUTING P2P-EXTENDED-TEST #####"

p2p-promote-to-extended-test: ## Promote service to extended test
    echo "##### EXECUTING P2P-PROMOTE-TO-EXTENDED-TEST #####"

p2p-promote-to-prod:  ## Promote service to prod
    echo "##### EXECUTING P2P-PROMOTE-TO-PROD #####"
    
p2p-prod: ## Runs the service
    echo "##### EXECUTING P2P-PROD #####"

These will be the entrypoints of the pipeline. You can then extend these to do your custom actions.

What tool you use in each of the Makefile targets is up to you.

FAQs

What if I don’t have tests for one of the stages?

  • Leave it as a no-op and as you want to increase your maturity and aim for Continuous Delivery you already have the place to run them

Subsections of Path to Production

Versioning

The P2P starts with a versioned immutable artifact that is passed through the pipeline.

Versioning is a critical part of the software development lifecycle. It allows you to track changes to your codebase, and it helps you to understand what changes have been made and why. Versioning also helps you to manage dependencies and to ensure that your code is always up-to-date.

Semantic Versioning Support

The P2P comes with a workflow that creates semantic versions for your application.

  version:
    uses: coreeng/p2p/.github/workflows/p2p-version.yaml@v1
    secrets:
      git-token: ${{ secrets.GITHUB_TOKEN }} 

When not on the main branch this will take the latest tag and append a commit hash.

When on main it increments the next minor version. Everything in the pipeline after this works on that version.

For all future P2P steps the $VERSION variable will be set

Incrementing the major version

To increment the major version, create the tag vX.0.0 where X is the next major version you want to use.

The next time the P2P runs it will pick vX.1.0 as the next version.

Quality Gate: Fast Feedback

Fast feedback gives engineers fast feedback on every change. It is run on every PR and on every commit to main.

Implement the following Make targets:

Usage

If you used corectl to create your application this workflow will already be in your repository.

name: Fast Feedback

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

permissions:
  contents: write
  id-token: write

jobs:
  version:
    uses: coreeng/p2p/.github/workflows/p2p-version.yaml@v1
    secrets:
      git-token: ${{ secrets.GITHUB_TOKEN }} 

  fastfeedback:
    needs: [version]
    uses: coreeng/p2p/.github/workflows/p2p-workflow-fastfeedback.yaml@v1
    with:
     version: ${{ needs.version.outputs.version }}

Subsections of Quality Gate: Fast Feedback

Build

The purpose of P2P build is to

  • Run any local tests + static verification
  • Build an immutable versioned artifact to pass through the rest of the P2P.

Pushing to the fast feedback registry

For deployed testing to take place push a versioned artifact to the registry required for fast feedback:

docker image push $(REGISTRY)/$(FAST_FEEDBACK_PATH)/<app-name>:$(VERSION)

Variables set by the P2P that you can use:

  • REGISTRY
  • VERSION

Deployed Stubbed Functional Test

Make target: p2p-functional

Shift left the verification of your application.

Stub out any external dependencies apart from databases / caches / queues to enable reliable, fast feedback, with the ability to test failure scenarios.

Deploy Application

Deploy to the following namespace

<app_name>-functional

Or if you have multiple apps in the same tenancy

<tenant>-<app-name>-functional

Run Tests

Execute functional tests, we recommend using a BDD framework. All software templates come with example tests.

Deployed Stubbed NFT

Make target: p2p-nft

Shift left the non-functional verification of your application.

Stub out any external dependencies apart from databases / caches / queues to enable reliable, fast feedback, with the ability to test failure scenarios.

Deploy Application

Deploy to the following namespace

<app_name>-nft

Or if you have multiple apps in the same tenancy

<tenant>-<app-name>-nft

Run Tests

Execute non-functional tests. We recommend using k6 and software templates come with example non-functional tests.

Integration Test

Make target: p2p-integration

Integration is the first time your application is integrated with other applications. Different levels of integration will be appropriate depending on your architecture.

Deploy Application

Deploy to the following namespace

<app_name>-integration

Or if you have multiple apps in the same tenancy

<tenant>-<app-name>-integration

Run Tests

Execute integrated functional tests, we recommend using a BDD framework. All software templates come with example tests.

Promote to Extended Test

Make target: p2p-promote-to-extended-test

After successful run of p2p-functional, p2p-integration, and p2p-nft the version is considered ready for Extended Test.

The default implementation as provided by software templates rarely needs changing:

p2p-promote-to-extended-test:
    corectl p2p promote <app-name>:${VERSION} \
        --source-stage $(FAST_FEEDBACK_PATH) \
        --dest-registry $(REGISTRY) \
        --dest-stage $(EXTENDED_TEST_PATH)

This moves the immutable versioned artifact into the registry for extended test and will be picked up next time Extended Test runs

Production Deployment

Production deployment, by default once a day, takes the latest version that has passed extended test and deploys it to production.

Implement the following Make targets:

Usage

If you’ve created your application with corectl from a software template the following workflow will be already configured.

name: Prod

on:
  workflow_dispatch:
  schedule:
    - cron: '35 5 * * 1,5'

permissions:
  contents: read
  id-token: write

jobs:
  get-latest-version:
    uses: coreeng/p2p/.github/workflows/p2p-get-latest-image-prod.yaml@v1
    with:
      image-name: knowledge-platform

  prod:
    needs: [get-latest-version]
    uses: coreeng/p2p/.github/workflows/p2p-workflow-prod.yaml@v1
    with:
      version: ${{ needs.get-latest-version.outputs.version }}

This task will get the latest version that’s on the prod registry and execute the prod deployment task.

Subsections of Production Deployment

Prod Deploy

The purpose of P2P prod is to

  • Deploy to production

Deploy Application

Deploy to the following namespace

<app_name>-prod

Or if you have multiple apps in the same tenancy

<tenant>-<app-name>-prod

Quality Gate: Extended Test

Extended tests, by default, run once a day and is the placeholder for longer running tests that are too slow or expensive to run on every commit.

Implement the following Make targets:

See Types of extended tests for more information.

Subsections of Quality Gate: Extended Test

Extended Test

Make target: p2p-extended-test

Run any tests that take too long to go into the fast feedback stages.

Deploy Application

Deploy to the following namespace

<app_name>-extended

Or if you have multiple apps in the same tenancy

<tenant>-<app-name>-extended

Promote to Prod

Make target: p2p-promote-to-prod

After successful run of p2p-extended the version is ready for production deployment.

The default implementation as provided by software templates rarely needs changing:

p2p-promote-to-prod:
    corectl p2p promote <app-name>:${VERSION} \
        --source-stage $(EXTENDED_TEST_PATH) \
        --dest-registry $(REGISTRY) \
        --dest-stage $(PROD_PATH)

This moves the immutable versioned artifact into the registry for production and will be picked up next time prod deploy runs.

Reference

Additional Information about how the P2P works.

Automatic GH Action authentication

As part of your tenancy you define GitHub repos.

All of those repos get passwordless access to deploy to your namespaces and any sub namespace you create.

Requirements

In order to use this pipeline, you’ll need to be a tenant of a Core Platform.

For any repo that wasn’t created out of Core Platform templates, but you would like to follow a recommended p2p standard, please use corectl as below:

corectl p2p env sync <app repository> <tenant> [flags]

If the repo was created by corectl, it will automatically set the required variables.

Having these, you’re set to start deploying!

GitHub Variables

P2P pipelines expect some GitHub Variables to be configured. You can configure it either automatically using corectl or manually.

Automatically

You can automatically set/update variables using corectl:

corectl p2p env sync <app-repository> <tenant-name>

Manually

Create your environments with the following variables:

  • BASE_DOMAIN e.g. gcp-dev.cecg.platform.cecg.io
  • INTERNAL_SERVICES_DOMAIN e.g. gcp-dev-internal.cecg.platform.cecg.io
  • DPLATFORM environment name from platform-environments e.g. dev
  • PROJECT_ID project id from platform environments e.g. core-platform-efb3c84c
  • PROJECT_NUMBER project number for the project id above

Git Environments

Usually you need at least two environments, e.g.

  • dev
  • prod

For an instance of the CECG Core Platform on GCP.

A single dev environment is enough for Fast Feedback.

Set the following repository variables (these may be set globally for your org):

  • FAST_FEEDBACK to {"include": [{"deploy_env": "dev"}]}
  • EXTENDED_TEST to {"include": [{"deploy_env": "dev"}]}
  • PROD to {"include": [{"deploy_env": "prod"}]}

And specifically for your app set:

  • TENANT_NAME as configured in your tenancy in platform environments

Subsections of Reference

Types of extended tests

The extended test quality gate is for any test that you do not want to run on every commit e.g.

  • Longer peak load tests
  • Soak tests

Running these tests can be expensive and typically these are run once a day on the latest version of the application that has been promoted from fast feedback.

These are typically based on Non-Functional-Requirements (NFRs) which should specify what’s the average load expected, peak traffic (both requests per second and concurrent users) and response times in the different percentiles. To simulate processing time of a downstream application, a delay can be added on all integration endpoints.

Types of test

Peak Load Test

Taken from the peak traffic. These tests typically run for ~1h and ensure the application under peak traffic does not exceed the expected response times.

Soak Test

Tests based on the average throughput from the NFRs. These tests typically run longer than peak, running from 4 h duration to a 24/7 environment. They are good to evaluate the stability of the system, catching any degradation of the system like memory leaks.

Rolling Update

This test ensures that a new deployment is successful and doesn’t cause errors even when the ingress controller pods restart. To validate that, the test should ensure that it:

  • Stops accepting new connections.
  • Processes the inbound connection that it has already accepted.
  • Exits after everything is processed with exit code 0.

Resilience Tests

This type of test, like the name says, is used to test the resilience of the system. Taking the same example of the Ingress feature, we can use this to remove all pods in a certain availability zone and ensure that all requests are successful.

How to use this on the pipeline?

These should be triggered by a cron on git actions, and the pipeline will look like:

Extended-tests

Usage

name: Extended Test

on:
  workflow_dispatch:
  schedule:
    - cron: '30 22 * * *'

permissions:
  contents: read
  id-token: write

jobs:
  get-latest-version:
    uses: coreeng/p2p/.github/workflows/p2p-get-latest-image-extended-test.yaml@v1
    with:
      image-name: knowledge-platform

  extendedtests:
    needs: [get-latest-version]
    uses: coreeng/p2p/.github/workflows/p2p-workflow-extended-test.yaml@v1
    with:
      version: ${{ needs.get-latest-version.outputs.version }}

This task will get the latest version that’s on the /extended-test registry and execute the extended tests. If these are successful, it will promote the image to prod.

Promotion

Promotion is the process of marking an immutable, versioned artifact as being ready for deployment to the next class of environment.

What is used to promote?

The two main mechanisms for promotion are:

  • Tests e.g. functional or non-functional tests
  • Stability in an environment e.g. run a longer soak tests in a stage environment and check alerts or canary deployments

Both are possible with the Core Platform P2P. The reference steps all show how to do test based promotion, future reference apps will show alert based promotion in later environments.

Promotion Mechanism

The Core Platform uses Container Registry artifact copying as the promotion mechanism.

All reference applications and skeletons come with a helper task to do this:

.PHONY: p2p-promote-generic
p2p-promote-generic:  ## Generic promote functionality
    corectl p2p promote $(image_name):${image_tag} \
        --source-stage $(source_repo_path) \
        --dest-registry $(REGISTRY) \
        --dest-stage $(dest_repo_path)

The task:

  • Authenticates with the source registry
  • Pulls the image to be promoted
  • Tags the image with the destination registry
  • Authenticates with the destination registry
  • Pushes the image to the destination registry

The P2P sets all the variables:

  • SOURCE_REGISTRY
  • REGISTRY for the destination registry
  • SOURCE_AUTH_OVERRIDE
  • DEST_AUTH_OVERRIDE

corectl p2p promote defaults to environment variables set by P2P, so you don’t need to set them explicitly.

Here is the full list of flags:

corectl p2p promote -h
Promotes image from source to destination registry. Only GCP is supported for now

Usage:
  corectl p2p promote <image_with_tag> [flags]

Flags:
      --dest-auth-override string     optional, defaults to environment variable: DEST_AUTH_OVERRIDE
      --dest-registry string          required, defaults to environment variable: DEST_REGISTRY
      --dest-stage string             required, defaults to environment variable: DEST_STAGE
  -h, --help                          help for promote
      --source-auth-override string   optional, defaults to environment variable: SOURCE_AUTH_OVERRIDE
      --source-registry string        required, defaults to environment variable: SOURCE_REGISTRY
      --source-stage string           required, defaults to environment variable: SOURCE_STAGE

For lower environments these may be the same registry so the source_repo_path and dest_repo_path are included

The steps are:

  • After Fastfeedback on the main branch p2p-promote-to-extended-test is executed where the default implementation for all applications is:
.PHONY: p2p-promote-to-extended-test
p2p-promote-to-extended-test: source_repo_path=$(FAST_FEEDBACK_PATH)
p2p-promote-to-extended-test: dest_repo_path=$(EXTENDED_TEST_PATH)
p2p-promote-to-extended-test:  p2p-promote-generic
  • After extended test the p2p-promote-to-prod is executed where the default implementation for all applications is:
.PHONY: p2p-promote-to-prod
p2p-promote-to-prod:  source_repo_path=$(EXTENDED_TEST_PATH)
p2p-promote-to-prod:  dest_repo_path=$(PROD_PATH)
p2p-promote-to-prod:  p2p-promote-generic

Deployment Frequency

The Core Platform P2P can be configured to deploy your application in one of three modes:

  • Right away: Fast feedback runs right away, followed by extended test, followed by production deployment.
  • On a schedule: Fast feedback runs right away, extended test and production run on a schedule.
  • Manually: Fast feedback runs right away, extended test and production are manually triggered.

Or a hybrid of one of the above e.g. Fast feedback and extended test run right away, production is manually triggered.

Right away

Create a single CD.yaml workflow file in your repo in ./github/workflows with the following content:

name: Continuous Deployment

on:
  workflow_dispatch:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

permissions:
  contents: write
  id-token: write

jobs:
  version:
    uses: coreeng/p2p/.github/workflows/p2p-version.yaml@v1
    secrets:
      git-token: ${{ secrets.GITHUB_TOKEN }} 

  fastfeedback:
    needs: [version]
    uses: coreeng/p2p/.github/workflows/p2p-workflow-fastfeedback.yaml@v1
    with:
      version: ${{ needs.version.outputs.version }}

  extended-test:
    needs: [version, fastfeedback]
    uses: coreeng/p2p/.github/workflows/p2p-workflow-extended-test.yaml@v1
    with:
      version: ${{ needs.version.outputs.version }}

  prod:
    needs: [version, extended-test]
    uses: coreeng/p2p/.github/workflows/p2p-workflow-prod.yaml@v1
    with:
      version: ${{ needs.version.outputs.version }}

Schedule

This is the configuration all templates come with where:

  • Fast feedback runs for every commit to main
  • Extended test runs once a day, on the latest version that passed fastfeedback
  • Production runs once a day, on the latest version that passed extended test

Create three workflows in your repo in ./github/workflows with the following content:

Fast Feedback

name: Fast Feedback

on:
  workflow_dispatch:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

permissions:
  contents: write
  id-token: write

jobs:
  version:
    uses: coreeng/p2p/.github/workflows/p2p-version.yaml@v1
    secrets:
      git-token: ${{ secrets.GITHUB_TOKEN }}
    
  fastfeedback:
    needs: [version]
    uses: coreeng/p2p/.github/workflows/p2p-workflow-fastfeedback.yaml@v1
    with:
     version: ${{ needs.version.outputs.version }}

Extended Test

name: Extended Test

on:
  workflow_dispatch:
  schedule:
    - cron: '0 22 * * *'

permissions:
  contents: read
  id-token: write

jobs:
  get-latest-version:
    uses: coreeng/p2p/.github/workflows/p2p-get-latest-image-extended-test.yaml@v1
    with:
      image-name: go-reference-app

  extendedtests:
    needs: [get-latest-version]
    uses: coreeng/p2p/.github/workflows/p2p-workflow-extended-test.yaml@v1
    with:
      version: ${{ needs.get-latest-version.outputs.version }}

Prod

name: Prod

on:
  workflow_dispatch:
  schedule:
    - cron: '30 5 * * 1,5'

permissions:
  contents: read
  id-token: write

jobs:
  get-latest-version:
    uses: coreeng/p2p/.github/workflows/p2p-get-latest-image-prod.yaml@v1
    with:
      image-name: go-reference-app

  prod:
    needs: [get-latest-version]
    uses: coreeng/p2p/.github/workflows/p2p-workflow-prod.yaml@v1
    with:
      version: ${{ needs.get-latest-version.outputs.version }}

Manually

For manually, create the same workflows as scheduled but remove the schedule. The manual trigger is available in the Actions tab in your repo.

Local development

Some Makefile targets within your app require specific variables to successfully execute. Those include registry details, tenancy or versioning. Corectl allows you to export those based on your existing local tenant and repository configuration. This corectl feature is designed to ease local development and enable seamless p2p targets execution.

To construct and print out a list of variables run:

corectl p2p export --tenant <your_tenant_name> --environment <your_target_env> --repoPath <filepath_to_app_repository>

The command will print out all variables as export statements, ex.:

export BASE_DOMAIN="gcp-dev.cecg.platform.cecg.io" # domain pulled in from environments project                                 
export REPO_PATH="/path/to/my/app/repo" # path to your git local app repository                                                 
export REGION="europe-west2" # region pulled in from environments project                                                         
export REGISTRY="europe-west2-docker.pkg.dev/myproject/tenant/mytenant" # registry url constructed based on environments project
export VERSION="53b9c85" # short git hash commit pointing to current HEAD (latest commit in currently checked out branch in that repo)                                                 
export TENANT_NAME="mytenant" 

You can copy all the output and paste again in currently running shell to export all envs. Once done you can run most of the p2p Makefile targets without additional configuration.

To export those automatically in the current shell, run:

eval $(corectl p2p export --tenant <your_tenant_name> --environment <your_target_env> --repoPath <filepath_to_app_repository>)

If you omit -repoPath flag, the tool will default to current directory.

For further details run:

corectl p2p export -h