DEV Community

Paulo Ponciano for AWS Community Builders

Posted on • Originally published at Medium

IDP port e Crossplane composition no Amazon EKS [Lab Session]

Seguindo em mais um lab, este de certa forma é uma continuação do Serverless com Crossplane composition no EKS + GitOps [Lab Session]. Vamos adicionar mais alguns elementos, um deles é o Internal Developer Portal (IDP) port. O IDP nos permite abstrair muitos elementos subjacentes, proporcionando aos consumidores maior foco em suas entregas finais.

É claro, ainda é necessário manter esses 'elementos subjacentes' assim como a própria abstração dos mesmos. Esse normalmente é o trabalho do time de Engenharia de Plataforma.

Architecture and combination of elements

Vamos utilizar a mesma composition do crossplane que entrega uma arquitetura serverless na AWS. Desta vez, o input das informações não será diretamente em um yaml de claim e sim no IDP port.

Com crossplane, o yaml utilizado para o claim já é bem enxuto, utilizando um IDP isso fica ainda mais simples para o consumidor.

Repositório.

Port blueprints e actions

No DevPortal Builder do port, vamos criar os blueprints Cluster, Namespace e Order (stack serverless AWS). Blueprints são utilizados como modelos de dados dos nossos elementos e permite a composição dos catálogos.

[app.getport.io](https://app.getport.io/)app.getport.io

  • Os arquivos json utilizados estão em port/blueprints do repositório.

Cluster blueprint:


{
  "identifier": "cluster",
  "description": "This blueprint represents a Kubernetes Cluster",
  "title": "Cluster",
  "icon": "Cluster",
  "schema": {
    "properties": {},
    "required": []
  },
  "mirrorProperties": {},
  "calculationProperties": {},
  "relations": {}
}
Enter fullscreen mode Exit fullscreen mode

Namespace blueprint:


{
    "identifier": "namespace",
    "description": "This blueprint represents a k8s Namespace",
    "title": "Namespace",
    "icon": "Environment",
    "schema": {
      "properties": {
        "creationTimestamp": {
          "type": "string",
          "title": "Created",
          "format": "date-time",
          "description": "When the Namespace was created"
        },
        "labels": {
          "type": "object",
          "title": "Labels",
          "description": "Labels of the Namespace"
        }
      }
// Removed for brevity
Enter fullscreen mode Exit fullscreen mode

Order blueprint:


{
  "identifier": "order",
  "description": "This blueprint represents an Order App",
  "title": "Order",
  "icon": "Microservice",
  "schema": {
    "properties": {
      "provider": {
        "icon": "AWS",
        "title": "Provider",
        "description": "The provider where the app is deployed",
        "type": "string",
        "default": "n/a",
        "enum": [
          "default",
          "n/a"
        ],
        "enumColors": {
          "default": "lightGray",
          "n/a": "lightGray"
        }
      }
// Removed for brevity
Enter fullscreen mode Exit fullscreen mode

Resultado:

  • Agora vamos criar as actions do blueprint Order. As actions serão responsáveis por acionar o GitHub Actions em eventos de CREATE e DELETE. Podemos editar o blueprint e inserir o json port/actions do repositório em 'Actions':

Este é o trecho em que informamos o workflow que será acionado em cada action:

// Removed for brevity
"invocationMethod": {
      "type": "GITHUB",
      "omitPayload": true,
      "omitUserInputs": false,
      "reportWorkflowStatus": true,
      "org": "paulofponciano",
      "repo": "EKS-Crossplane-ArgoCD",
      "workflow": "create-order-app.yaml"
    },
    "trigger": "CREATE",
    "description": "Create an Order App",
    "requiredApproval": false
  }
// Removed for brevity
Enter fullscreen mode Exit fullscreen mode

Na primeira integração, é necessário conectar o port ao GitHub para que ele possa acionar os workflows — GitHub app installation:

Agora em Self-Service já temos as actions de Create e Delete para a composition do crossplane:

GitHub Actions

Os workflows do GitHub Actions serão responsáveis por receber os inputs enviados no payload do port quando as actions são executadas. Esses inputs são passados no manifesto de claim da composition do crossplane e é feito o commit desse manifesto em um repositório monitorado pelo ArgoCD (deployed_infra):

  • .github/workflows/create-order-app.yaml
name: Create an order app
on:
  workflow_dispatch:
    inputs:
      name:
        required: true
        description: "The name of the app"
      provider:
        required: true
        description: "The provider where the app is deployed"
        default: "aws"
      region:
        required: true
        description: "The provider region"
        default: "us-east-1"
      ownerName:
        required: true
        description: "The owner name"
        default: "ownerName"
      projectName:
        required: true
        description: "The project name"
        default: "projectName"
jobs:
  deploy-app:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          persist-credentials: false
          fetch-depth: 0

      - name: Create an order app
        run: |
          chmod +x scripts/create-app-order.sh
          ./scripts/create-app-order.sh ${{ inputs.name }} ${{ inputs.provider }} ${{ inputs.region }} ${{ inputs.ownerName }} ${{ inputs.projectName }}

      - name: Commit changes
        run: |
          git config --local user.email "github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git add .
          git commit -a -m "Create app order ${{ inputs.name }}"

      - name: push
        uses: ad-m/github-push-action@master
Enter fullscreen mode Exit fullscreen mode
  • scripts/create-app-order.sh
NAME=$1
PROVIDER=$2
REGION=$3
OWNER=$4
PROJECT=$5

FILE_PATH=deployed_infra/${NAME}.yaml

cp crossplane_compositions/order/order-claim-port.yaml $FILE_PATH
yq --inplace ".metadata.name = \"${NAME}\"" $FILE_PATH
yq --inplace ".spec.resourceConfig.providerConfigName = \"${PROVIDER}\"" $FILE_PATH
yq --inplace ".spec.resourceConfig.region = \"${REGION}\"" $FILE_PATH
yq --inplace ".spec.resourceConfig.tags.ownerName = \"${OWNER}\"" $FILE_PATH
yq --inplace ".spec.resourceConfig.tags.projectName = \"${PROJECT}\"" $FILE_PATH
Enter fullscreen mode Exit fullscreen mode
  • .github/workflows/delete-order-app.yaml
name: Delete an order app
on:
  workflow_dispatch:
    inputs:
      name:
        required: true
        description: "The name of the app"
jobs:
  delete-app:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          persist-credentials: false
          fetch-depth: 0

      - name: Delete an order app
        run: |
          rm deployed_infra/${{ inputs.name }}.yaml

      - name: Commit changes
        run: |
          git config --local user.email "github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git add .
          git commit -a -m "Delete app order ${{ inputs.name }}"

      - name: push
        uses: ad-m/github-push-action@master
Enter fullscreen mode Exit fullscreen mode

ArgoCD

Criamos uma application no ArgoCD, definindo como source o repo/path onde teremos o commit do manifesto feito pelo workflow do GitHub Actions:

apiVersion: v1
kind: Secret
metadata:
  name: public-repo-crossplane
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repository
stringData:
  type: git
  url: https://github.com/paulofponciano/EKS-Crossplane-ArgoCD.git
---
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: idp
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  description: "Internal Developer Portal"
  sourceRepos:
    - 'https://github.com/paulofponciano/EKS-Crossplane-ArgoCD.git'
  destinations:
    - namespace: '*'
      server: 'https://kubernetes.default.svc'
      name: 'in-cluster'
  clusterResourceWhitelist:
    - group: '*'
      kind: '*'
  namespaceResourceWhitelist:
    - group: '*'
      kind: '*'
  orphanedResources:
    warn: true
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: idp-infra-aws
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: idp
  source:
    repoURL: https://github.com/paulofponciano/EKS-Crossplane-ArgoCD.git
    targetRevision: HEAD
    path: deployed_infra
  destination:
    server: https://kubernetes.default.svc
  syncPolicy:
    automated:
      selfHeal: true
      prune: true
      allowEmpty: true
Enter fullscreen mode Exit fullscreen mode

kubectl apply -f argocd/application.yaml

Port k8s-exporter

O k8s-exporter permite que o port possa ingerir de volta os dados dos recursos criados pelo crossplane no cluster kubernetes, assim podendo alimentar o catálogo do IDP.

Port Documentation - Kubernetes

  • Precisamos das credenciais de API do port para instalação via helm no cluster. Essas credenciais já existem no port, vamos apenas utiliza-las como variáveis na instalação:

  • Antes de executar a instalação via helm, precisamos informar no arquivo config.yaml os atributos para que os recursos criados sejam 'reconhecidos' como entidades, assim serão exportados para o port:
# Removed for brevity
- kind: api.pauloponciano.pro/v1alpha1/customorder
  selector:
    query: .metadata.namespace | startswith("environment")
  port:
    entity:
      mappings:
        - identifier: .metadata.name + "-" + .metadata.namespace + "-" + "paulop"
          title: .metadata.name
          icon: '"Microservices"'
          blueprint: '"order"'
          properties:
            provider: .spec.resourceConfig.providerConfigName
            region: .spec.resourceConfig.region
            ownerName: .spec.resourceConfig.tags.ownerName
            projectName: .spec.resourceConfig.tags.projectName
            labels: .metadata.labels
            annotations: .metadata.annotations
          relations:
            Namespace: .metadata.namespace + "-" + "paulop"
Enter fullscreen mode Exit fullscreen mode
  • Instalação:

helm repo add port-labs https://port-labs.github.io/helm-charts

helm upgrade --install port-k8s-exporter port-labs/port-k8s-exporter \
--create-namespace --namespace port-k8s-exporter \
--set secret.secrets.portClientId=$CLIENT_ID --set secret.secrets.portClientSecret=$CLIENT_SECRET \
--set-file configMap.config=config.yaml

Agora já podemos verificar o que está sendo ingerido e apresentado no catálogo do port:

Não temos nada em Orders porque ainda não criamos uma, calma jovem! Faremos isso da mesma forma que o usuário fará.

Usando o Self-Service

CREATE:

Na área de self-service do port, vamos executar a action de CREATE, fazendo os inputs necessários para o claim da composition do crossplane:

Sucesso da execução da action:

Sucesso na execução do workflow de create no GitHub Actions:

O manifesto de claim foi criado com os inputs feitos através do IDP:

O ArgoCD fez a entrega do claim no cluster kubernetes que já possui a composition e definition do crossplane, assim os recursos da stack são criados na AWS:

Voltando ao catálogo do port, em Orders:

AWS stack serverless:

DELETE:

Vamos agora executar a action de DELETE, também pelo IDP:

Workflow de delete:

ArgoCD prune resources:

Happy building!

References:

DevOps Toolkit

Top comments (0)