Skip to main content

Let developers enrich services using Gitops

This guide takes 10 minutes to complete, and aims to demonstrate Port's flexibility when working with Gitops.

Prerequisites
  • This guide assumes you have a Port account and that you have finished the onboarding process. We will use the Service blueprint that was created during the onboarding process.
  • You will need a Git repository (Github, GitLab, or Bitbucket) in which you can place a workflow/pipeline that we will use in this guide. If you don't have one, we recommend creating a new repository named Port-actions.

The goal of this guide

In this guide we will enrich a service in Port using Gitops. In reality, this can be used by developers to independently add additional valuable data about their services to Port.

After completing it, you will get a sense of how it can benefit different personas in your organization:

  • Developers will be able to enrich their services without needing to nag devops engineers.
  • Platform engineers will be able to create RBAC-controlled actions for developers, empowering their independence.
  • R&D managers will be able to track additional, valuable data about services in the organization.

Add new properties to your Service blueprint

Let's start by adding two new properties to the Service blueprint, that we will later populate using Gitops.

  1. Go to your Builder, expand the Service blueprint, and click on New property.

  2. The first property will be the service's type, chosen from a predefined list of options. Fill out the form like this, then click Create:



  1. The second property will be the lifecycle state of the service, also chosen from a predefined list of options. Fill out the form like this, then click Create:

Note the colors of the inputs, this will make it easier to see a service's lifecycle in your catalog 😎



Model domains for your services

Services that share a business purpose (e.g. payments, shipping) are often grouped together using domains. Let's create a blueprint to represent a domain in Port:

  1. In your Builder, click on the Add button in the top right corner, then choose Custom blueprint:


  1. Click on the Edit JSON button in the top right corner, replace the content with the following definition, then click Create:
Blueprint JSON (click to expand)
{
"identifier": "domain",
"title": "Domain",
"icon": "TwoUsers",
"schema": {
"properties": {
"architecture": {
"title": "Architecture",
"type": "string",
"format": "url",
"spec": "embedded-url"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"relations": {}
}

Connect your services to their domains

Now that we have a blueprint to represent a domain, let's connect it to our services. We will do this by adding a relation to the Service blueprint:

  1. Go to your Builder, expand the Service blueprint, and click on New relation:


  1. Fill out the form like this, then click Create:


Create domains via Gitops

Now that we have a Domain blueprint, we can create some domains in Port. This can be done manually from the UI, or via Gitops which is the method we will use in this guide.

  1. In your Port-actions (or equivalent) Github repository, create a new file named port.yml in the root directory, and use the following snippet as its content:
port.yml (click to expand)
- identifier: payment
title: Payment
blueprint: domain
properties:
architecture: https://lucid.app/documents/embedded/c3d64493-a5fe-4b18-98d5-66d355080de3
- identifier: shipping
title: Shipping
blueprint: domain
properties:
architecture: https://lucid.app/documents/embedded/c3d64493-a5fe-4b18-98d5-66d355080de3

  1. Head back to your software catalog, you will see that Port has created two new domain entities:

The architecture property is a URL to a Lucidchart diagram. This is a handy way to track a domain's architecture in your software catalog.


Create an action to enrich services

As platform engineers, we want to enable our developers to perform certain actions on their own. Let's create an action that developers can use to add data to a service, and allocate it to a domain.

Setup the action's frontend

Onboarding

As part of the onboarding process, you should already have an action named Enrich service in your self-service tab. In that case, you can skip to the Define backend type step.

If you skipped the onboarding, or you want to create the action from scratch, complete steps 1-5 below.

Create the action's frontend (steps 1-5)
  1. Go to your Self-service page, then click on the + New action button in the top right corner.

  2. From the dropdown, choose the Service blueprint.

  3. Fill out the basic details like this, then click Next:



  1. We want our developers to be able to choose the domain to which the service will be assigned. Click on New input, fill out the form like this, then click Next:


  1. Let's add two more inputs for our new service properties - type and lifecycle. Create two new inputs, fill out their forms like this, then click Next:



Define backend type

Now we'll define the backend of the action. Port supports multiple invocation types, for this tutorial we will use a Github workflow, a GitLab pipeline, or a Jenkins pipeline(choose this option if you are using Bitbucket).

Note that you will need to have Port's Github app installed in your Github organization (the one that contains the repository you'll work with).

Fill out the form with your values:

  • Replace the Organization and Repository values with your values (this is where the workflow will reside and run).

  • Name the workflow port-enrich-service.yml.

  • Fill out your workflow details:

  • Scroll down to the Configure the invocation payload section.
    This is where you can define which data will be sent to your backend each time the action is executed.

    For this example, we will send some details that our backend needs to know - the user inputs, the entity, and the id of the action run.
    Copy the following JSON snippet and paste it in the payload code box:

    {
    "port_payload": {
    "context": {
    "entity": "{{ .entity.identifier }}",
    "runId": "{{ .run.id }}"
    },
    "payload": {
    "properties": {
    "domain": "{{ .inputs.domain }}",
    "type": "{{ .inputs.type }}",
    "lifecycle": "{{ .inputs.lifecycle }}",
    }
    }
    }
    }

The last step is customizing the action's permissions. For simplicity's sake, we will use the default settings. For more information, see the permissions page. Click Create.

Setup the action's backend

Our action will create a pull-request in the service's repository, containing a port.yml file that will add data to the service in Port. Choose a backend type below to setup the workflow:

  1. First, let's create the necessary token and secrets. If you've already completed the scaffold a new service guide, you should already have these configured and you can skip this step.
  • Go to your Github tokens page, create a personal access token with repo and admin:org scope, and copy it (this token is needed to create a pull-request from our workflow).

  • Go to your Port application, click on the ... in the top right corner, then click Credentials. Copy your Client ID and Client secret.

  1. In your Port-actions (or equivalent) Github repository, create 3 new secrets under Settings->Secrets and variables->Actions:
  • ORG_ADMIN_TOKEN - the personal access token you created in the previous step.
  • PORT_CLIENT_ID - the client ID you copied from your Port app.
  • PORT_CLIENT_SECRET - the client secret you copied from your Port app.

We will now create a YML file that will serve as a template for our services' port.yml configuration file.

  • In your repository, create a file named enrichService.yml under /templates/ (its path should be /templates/enrichService.yml).
  • Copy the following snippet and paste it in the file's contents:
enrichService.yml (click to expand)
# enrichService.yml

- identifier: "{{ service_identifier }}"
blueprint: service
properties:
type: "{{ service_type }}"
lifecycle: "{{ service_lifecycle }}"
relations:
domain: "{{ domain_identifier }}"

Now let's create the file that contains our logic:

In the same repository, under .github/workflows, create a new file named port-enrich-service.yml and use the following snippet as its content:

Github workflow (click to expand)
name: Enrich service
on:
workflow_dispatch:
inputs:
port_payload:
required: true
description: Port's payload, including details for who triggered the action and general context
type: string
jobs:
enrichService:
runs-on: ubuntu-latest
steps:
# Checkout the workflow's repository
- uses: actions/checkout@v3
# Checkout the service's repository
- uses: actions/checkout@v3
with:
repository: "${{ github.repository_owner }}/${{fromJson(inputs.port_payload).context.entity}}"
path: ./targetRepo
token: ${{ secrets.ORG_ADMIN_TOKEN }}
- name: Copy template yml file
run: |
cp templates/enrichService.yml ./targetRepo/port.yml
- name: Update new file data
run: |
sed -i 's/{{ service_identifier }}/${{fromJson(inputs.port_payload).context.entity}}/' ./targetRepo/port.yml
sed -i 's/{{ domain_identifier }}/${{fromJson(inputs.port_payload).payload.properties.domain}}/' ./targetRepo/port.yml
sed -i 's/{{ service_type }}/${{fromJson(inputs.port_payload).payload.properties.type}}/' ./targetRepo/port.yml
sed -i 's/{{ service_lifecycle }}/${{fromJson(inputs.port_payload).payload.properties.lifecycle}}/' ./targetRepo/port.yml
- name: Open a pull request
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.ORG_ADMIN_TOKEN }}
path: ./targetRepo
commit-message: Enrich service - ${{fromJson(inputs.port_payload).context.entity}}
committer: GitHub <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
signoff: false
branch: add-port-yml
delete-branch: true
title: Create port.yml - ${{fromJson(inputs.port_payload).context.entity}}
body: |
Add port.yaml to enrich service in Port.
draft: false
- name: Create a log message
uses: port-labs/port-github-action@v1
with:
clientId: ${{ secrets.PORT_CLIENT_ID }}
clientSecret: ${{ secrets.PORT_CLIENT_SECRET }}
operation: PATCH_RUN
runId: ${{fromJson(inputs.port_payload).context.runId}}
logMessage: Pull request to add port.yml created successfully for service "${{fromJson(inputs.port_payload).context.entity}}" 🚀

The action is ready to be executed 🚀

Execute the action

  1. After creating an action, it will appear under the Self-service page. Find your new Enrich service action, and click on Execute.

  2. Choose a service from the dropdown, a domain to assign it to, and any values for its type and lifecycle, then click Execute:



  1. A small popup will appear, click on View details:


This page provides details about the action run. We can see that the backend returned Success and the pull-request was created successfully.

  1. Head over to your service's repository, you will see that a new pull-request was created:

  1. Merge the pull-request, then head back to your software catalog.

  2. Find your service, and click on its identifier. This will take you to the service's catalog page, where you can see your new properties populated with data:



All done! 💪🏽

Possible daily routine integrations

  • Fetch data from a Sentry project and reflect it in your software catalog.
  • Create and onboard services with a few clicks from your developer portal.

Conclusion

Gitops is a common practice in modern software development, as it ensures that the state of your infrastructure is always in sync with your codebase.
Port allows you to easily integrate your Gitops practices with your software catalog, reflecting the state of your infrastructure, and allowing you to empower your developers with controlled actions.

More guides & tutorials will be available soon, in the meantime feel free to reach out with any questions via our community slack or Github project.