Lock service deployment
During peak periods such as campaigns, holidays, or system outages, it becomes crucial to maintain stability and avoid unexpected changes or disruptions to services. Implementing a service locking mechanism using Port's Github Action helps to enforce this stability by temporarily preventing deployments during these critical times.
The CI/CD deployment check described in this guide will follow these steps:
- New code is pushed to the
main
branch of a Git repository - A GitHub workflow is triggered by the push event
- The Github workflow queries Port's Entity API and returns a response for the service
- If the value of the
locked_in_prod
field istrue
, the deployment check will fail - If the value of the
locked_in_prod
field isfalse
, the deployment check will succeed
Prerequisites
- This guide assumes you have a Port account and that you have finished the onboarding process
- You will need a GitHub repository in which you can trigger a workflow that we will use in this guide
Below you can find the JSON for the Service
blueprint required for the guide:
Service blueprint (click to expand)
{
"identifier": "service",
"title": "Service",
"icon": "Github",
"schema": {
"properties": {
"readme": {
"title": "README",
"type": "string",
"format": "markdown",
"icon": "Book"
},
"url": {
"title": "URL",
"format": "url",
"type": "string",
"icon": "Link"
},
"language": {
"icon": "Git",
"type": "string",
"title": "Language",
"enum": [
"GO",
"Python",
"Node",
"React"
],
"enumColors": {
"GO": "red",
"Python": "green",
"Node": "blue",
"React": "yellow"
}
},
"slack": {
"icon": "Slack",
"type": "string",
"title": "Slack",
"format": "url"
},
"code_owners": {
"title": "Code owners",
"description": "This service's code owners",
"type": "string",
"icon": "TwoUsers"
},
"type": {
"title": "Type",
"description": "This service's type",
"type": "string",
"enum": [
"Backend",
"Frontend",
"Library"
],
"enumColors": {
"Backend": "purple",
"Frontend": "pink",
"Library": "green"
},
"icon": "DefaultProperty"
},
"lifecycle": {
"title": "Lifecycle",
"type": "string",
"enum": [
"Production",
"Experimental",
"Deprecated"
],
"enumColors": {
"Production": "green",
"Experimental": "yellow",
"Deprecated": "red"
},
"icon": "DefaultProperty"
},
"locked_in_prod": {
"icon": "DefaultProperty",
"title": "Locked in Prod",
"type": "boolean",
"default": false
},
"locked_reason_prod": {
"icon": "DefaultProperty",
"title": "Locked Reason Prod",
"type": "string"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {}
}
Our Service blueprint has a property called locked_in_prod
with a boolean value. We will use the value of this field to determine whether new deployments of the service are allowed.
Now that you have your blueprint created, let's manually create a Notification Service
entity in our software catalog:
{
"identifier": "notification-service",
"title": "Notification Service",
"icon": "Github",
"properties": {
"url": "https://github.com/my-repo",
"language": "Python",
"slack": "https://app.slack.com/client",
"type": "Backend",
"lifecycle": "Production",
"locked_in_prod": true
},
"relations": {}
}
Reading the lock status during deployment
In order to use the locked_in_prod
field in your CI/CD pipeline, you will use Port's GitHub Action.
Let's go ahead and create a GitHub workflow file in a GitHub repository meant for the Notification Service
:
- Create a GitHub repository (or use an existing one)
- Create a
.github
directory- Inside it create a
workflows
directory
- Inside it create a
Inside the /.github/workflows
directory create a file called check-service-lock.yml
with the following content:
GitHub workflow configuration (click to expand)
name: Check Service Lock Status
on:
push:
branches:
- "main"
jobs:
get-entity:
runs-on: ubuntu-latest
outputs:
entity: ${{ steps.port-github-action.outputs.entity }}
steps:
- id: port-github-action
name: Get entity from Port
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
baseUrl: https://api.getport.io
identifier: notification-service
blueprint: service
operation: GET
check-lock-status:
runs-on: ubuntu-latest
needs: get-entity
steps:
- name: Get entity lock status
run: echo "LOCK_STATUS=$(echo '${{needs.get-entity.outputs.entity}}' | jq -r .properties.locked_in_prod)" >> $GITHUB_ENV
- name: Check lock status 🚧
if: ${{ env.LOCK_STATUS == 'true' }}
run: |
echo "Service in production is locked, stopping deployment"
exit 1
run-deployment:
runs-on: ubuntu-latest
needs: [check-lock-status]
steps:
- name: Run deployment
run: echo "Service in production is not locked, continuing deployment"
The baseUrl
, port_region
, port.baseUrl
, portBaseUrl
, port_base_url
and OCEAN__PORT__BASE_URL
parameters are used to select which instance or Port API will be used.
Port exposes two API instances, one for the EU region of Port, and one for the US region of Port.
- If you use the EU region of Port (https://app.getport.io), your API URL is
https://api.getport.io
. - If you use the US region of Port (https://app.us.getport.io), your API URL is
https://api.us.getport.io
.
For security reasons it is recommended to save the PORT_CLIENT_ID
and PORT_CLIENT_SECRET
as GitHub Secrets, and access them as shown in the example above.
If you try to push code to your repository when the locked_in_prod
field is set to true
, the deployment workflow will stop:
When you will look at the step that failed, you will see that the failure is due to the value of the locked field:
If you set the value of the locked_in_prod
field to false
, the workflow will perform the deployment without any issue: