Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/platform-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ jobs:

- name: Setup conftest
run: |
CONFTEST_VERSION=$(curl -sL https://api.github.com/repos/open-policy-agent/conftest/releases/latest | grep tag_name | cut -d'"' -f4)
CONFTEST_VERSION="v0.68.2"
wget -q "https://github.com/open-policy-agent/conftest/releases/download/${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION#v}_Linux_x86_64.tar.gz" -O /tmp/conftest.tar.gz
tar xzf /tmp/conftest.tar.gz -C /usr/local/bin/ conftest

Expand Down Expand Up @@ -167,7 +167,7 @@ jobs:

- name: Setup conftest
run: |
CONFTEST_VERSION=$(curl -sL https://api.github.com/repos/open-policy-agent/conftest/releases/latest | grep tag_name | cut -d'"' -f4)
CONFTEST_VERSION="v0.68.2"
wget -q "https://github.com/open-policy-agent/conftest/releases/download/${CONFTEST_VERSION}/conftest_${CONFTEST_VERSION#v}_Linux_x86_64.tar.gz" -O /tmp/conftest.tar.gz
tar xzf /tmp/conftest.tar.gz -C /usr/local/bin/ conftest

Expand Down
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ MAKEFLAGS += --no-print-directory
CLUSTER_NAME ?= platform-demo
NAMESPACE ?= argocd
KIND_IMG ?= kindest/node:v1.30.0
AWS_ACCOUNT_ID ?= 944684220857
AWS_REGION ?= eu-north-1
IMAGE_REPO ?= $(AWS_ACCOUNT_ID).dkr.ecr.$(AWS_REGION).amazonaws.com/app

.PHONY: help
help:
Expand Down Expand Up @@ -66,11 +69,13 @@ deploy:
@echo "=== Deploying simple-app ==="
helm template simple-app-dev standardized-path/app \
-f platform/apps/dev/values.yaml \
--set image.repository=$(IMAGE_REPO) \
| kubectl apply -f - 2>&1 | grep -v 'unchanged' || true

@echo "=== Deploying app-b ==="
helm template app-b-dev standardized-path/app \
-f platform/apps/app-b/dev/values.yaml \
--set image.repository=$(IMAGE_REPO) \
| kubectl apply -f - 2>&1 | grep -v 'unchanged' || true

@echo "=== App status ==="
Expand Down
53 changes: 53 additions & 0 deletions backstage/app-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
app:
title: Internal Developer Platform
baseUrl: http://backstage.platform.example.com

backend:
baseUrl: http://backstage.platform.example.com
listen:
port: 7007
cors:
origin: http://backstage.platform.example.com

organization:
name: Platform Engineering

auth:
environment: development
providers:
guest: {}

integrations:
github:
- host: github.com
apps: []

catalog:
import:
entityFilename: catalog-info.yaml
locations:
- type: url
target: https://github.com/tukue/simpleAppInfraCode/blob/main/catalog-info.yaml
- type: url
target: https://github.com/tukue/simpleAppInfraCode/blob/main/standardized-path/app/catalog-info.yaml
- type: url
target: https://github.com/tukue/simpleAppInfraCode/blob/main/platform/apps/simple-app/catalog-info.yaml
- type: url
target: https://github.com/tukue/simpleAppInfraCode/blob/main/platform/apps/app-b/catalog-info.yaml

scaffolder:
locations:
- type: url
target: https://github.com/tukue/simpleAppInfraCode/blob/main/backstage/templates/new-tenant-app/template.yaml

kubernetes:
serviceLocatorMethod:
type: multiTenant
clusterLocatorMethods:
- type: config
clusters:
- name: in-cluster
url: https://kubernetes.default.svc
authProvider: serviceAccount
skipTLSVerify: true
skipMetricsLookup: true
77 changes: 77 additions & 0 deletions backstage/catalog-ui.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>Backstage Catalog</title>
<style>
* {margin:0;padding:0;box-sizing:border-box}
body {font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#1a1d23;color:#e0e0e0;padding:24px}
h1 {font-size:24px;margin-bottom:4px}
.stats {display:flex;gap:16px;margin:16px 0}
.stat {background:#252830;border-radius:8px;padding:16px 20px;flex:1}
.stat-v {font-size:28px;font-weight:600}
.stat-l {font-size:12px;color:#888;text-transform:uppercase;margin-top:4px}
.sec {background:#252830;border-radius:8px;padding:20px;margin-bottom:16px}
.st {font-size:16px;font-weight:600;margin-bottom:12px;color:#9d9dff}
table {width:100%;border-collapse:collapse;font-size:13px}
th {text-align:left;padding:8px 12px;color:#888;font-weight:500;border-bottom:1px solid #333}
td {padding:8px 12px;border-bottom:1px solid #333}
.ok {color:#6cff8b}
</style></head><body>
<h1>Backstage Catalog</h1>
<p style="color:#888;font-size:14px;margin-bottom:8px">Internal Developer Platform — Entity overview</p>

<div class="stats">
<div class="stat"><div class="stat-v">9</div><div class="stat-l">Entities</div></div>
<div class="stat"><div class="stat-v">1</div><div class="stat-l">Templates</div></div>
<div class="stat"><div class="stat-v">0</div><div class="stat-l">Errors</div></div>
</div>
<div class="sec"><div class="st">Entities</div>
<table><tr><th>Name</th><th>Kind</th><th>Owner</th><th>System</th><th>Description</th></tr><tr><td><b>simple-app-infra-code</b></td><td><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;background:#1a3a5c;color:#6cb2ff">Component</span></td><td>platform-team</td><td>platform</td><td style="color:#888;font-size:12px">Internal Developer Platform for EKS and OpenShift</td></tr><tr><td><b>platform</b></td><td><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;background:#2a3a1a;color:#8bff6c">System</span></td><td>platform-team</td><td></td><td style="color:#888;font-size:12px">Internal Developer Platform providing golden path, GitOps, and observability</td></tr><tr><td><b>platform-team</b></td><td><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;background:#3a1a2a;color:#ff6cb2">Group</span></td><td></td><td></td><td style="color:#888;font-size:12px">Platform engineering team</td></tr><tr><td><b>app-developers</b></td><td><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;background:#3a1a2a;color:#ff6cb2">Group</span></td><td></td><td></td><td style="color:#888;font-size:12px">Application development teams</td></tr><tr><td><b>all-catalog-files</b></td><td><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;background:#2a2a3a;color:#b2b2ff">Location</span></td><td></td><td></td><td style="color:#888;font-size:12px">Discovers all catalog-info.yaml files across the repository</td></tr><tr><td><b>app-chart</b></td><td><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;background:#1a3a5c;color:#6cb2ff">Component</span></td><td>platform-team</td><td>platform</td><td style="color:#888;font-size:12px">Golden path Helm chart — the tenant contract for deploying services on the platform</td></tr><tr><td><b>tenant-contract</b></td><td><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;background:#3a2a1a;color:#ffb86c">API</span></td><td>platform-team</td><td>platform</td><td style="color:#888;font-size:12px">Contract defining what app teams provide and what the platform guarantees</td></tr><tr><td><b>simple-app</b></td><td><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;background:#1a3a5c;color:#6cb2ff">Component</span></td><td>app-developers</td><td>platform</td><td style="color:#888;font-size:12px">Demo tenant application deployed via the platform golden path</td></tr><tr><td><b>app-b</b></td><td><span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;background:#1a3a5c;color:#6cb2ff">Component</span></td><td>app-developers</td><td>platform</td><td style="color:#888;font-size:12px">Second tenant application proving contract reusability across dev/stage/prod</td></tr></table></div>
<div class="sec"><div class="st">Dependency Graph</div><div style="display:flex;align-items:center;gap:16px;padding:8px 0"><div style="background:#2a2d35;border-radius:8px;padding:12px 16px;min-width:140px;text-align:center"><div style="font-weight:500">simple-app-infra-code</div><div style="font-size:11px;color:#888">Component</div></div><span style="color:#ffb86c;font-size:13px">⊢ provides</span><span style="color:#888">tenant-contract</span></div><div style="display:flex;align-items:center;gap:16px;padding:8px 0"><div style="background:#2a2d35;border-radius:8px;padding:12px 16px;min-width:140px;text-align:center"><div style="font-weight:500">app-chart</div><div style="font-size:11px;color:#888">Component</div></div><span style="color:#ffb86c;font-size:13px">⊢ provides</span><span style="color:#888">tenant-contract</span></div><div style="display:flex;align-items:center;gap:16px;padding:8px 0"><div style="background:#2a2d35;border-radius:8px;padding:12px 16px;min-width:140px;text-align:center"><div style="font-weight:500">simple-app</div><div style="font-size:11px;color:#888">Component</div></div><span style="color:#6cb2ff;font-size:13px">→ dependsOn</span><span style="color:#888">app-chart</span></div><div style="display:flex;align-items:center;gap:16px;padding:8px 0"><div style="background:#2a2d35;border-radius:8px;padding:12px 16px;min-width:140px;text-align:center"><div style="font-weight:500">app-b</div><div style="font-size:11px;color:#888">Component</div></div><span style="color:#6cb2ff;font-size:13px">→ dependsOn</span><span style="color:#888">app-chart</span></div></div>
<div class="sec"><div class="st">Scaffolder Templates</div><div style="background:#2a2d35;border-radius:8px;padding:16px;margin-top:8px"><h3 style="margin:0 0 4px">New Tenant Application</h3><p style="color:#888;font-size:13px;margin-bottom:8px">Scaffold a new application using the platform's golden path Helm chart</p><b>Parameters:</b><pre style="background:#1a1d23;padding:12px;border-radius:4px;font-size:12px;overflow:auto">[
{
"appName": {
"title": "Application Name",
"type": "string",
"description": "Unique name for the application (e.g., app-c)",
"pattern": "^[a-z][a-z0-9-]*$"
},
"description": {
"title": "Description",
"type": "string",
"description": "Purpose of the application"
},
"owner": {
"title": "Owner",
"type": "string",
"description": "Backstage Group or User that owns this app",
"default": "app-developers",
"ui:field": "OwnerPicker",
"ui:options": {
"allowedKinds": [
"Group"
]
}
}
},
{
"awsAccountId": {
"title": "AWS Account ID",
"type": "string",
"description": "12-digit AWS account number for ECR",
"pattern": "^\\d{12}$"
},
"awsRegion": {
"title": "AWS Region",
"type": "string",
"description": "AWS region for ECR (e.g., eu-north-1, us-east-1)",
"default": "eu-north-1"
},
"port": {
"title": "Application Port",
"type": "integer",
"default": 8080
}
}
]</pre><b>Steps:</b><pre style="background:#1a1d23;padding:12px;border-radius:4px;font-size:12px"> fetch-skeleton: Fetch Skeleton (fetch:template)
publish: Publish to GitHub (publish:github)
register: Register in Backstage (catalog:register)</pre></div></div>
<div class="sec"><div class="st">Catalog Files</div><div style="font-size:12px;padding:4px 0;color:#888">📄 catalog-info.yaml</div><div style="font-size:12px;padding:4px 0;color:#888">📄 standardized-path/app/catalog-info.yaml</div><div style="font-size:12px;padding:4px 0;color:#888">📄 platform/apps/simple-app/catalog-info.yaml</div><div style="font-size:12px;padding:4px 0;color:#888">📄 platform/apps/app-b/catalog-info.yaml</div><div style="font-size:12px;padding:4px 0;color:#888">📄 backstage/templates/new-tenant-app/template.yaml</div></div></body></html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
replicaCount: 1

image:
repository: ${{ imageRepo }}/${{ appName }}
tag: "1.0.0-dev.1"

ingress:
enabled: true
hosts:
- host: ${{ appName }}-dev.example.com
paths:
- path: /
pathType: Prefix

env:
- name: ENVIRONMENT
value: "dev"
- name: PORT
value: "${{ port }}"

externalSecret:
enabled: true
data:
- secretKey: db-password
remoteRef:
key: dev/${{ appName }}/db-password
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
replicaCount: 3

image:
repository: ${{ imageRepo }}/${{ appName }}
tag: "1.0.0"

ingress:
enabled: true
hosts:
- host: ${{ appName }}.example.com
paths:
- path: /
pathType: Prefix

env:
- name: ENVIRONMENT
value: "prod"
- name: PORT
value: "${{ port }}"

externalSecret:
enabled: true
data:
- secretKey: db-password
remoteRef:
key: prod/${{ appName }}/db-password
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
replicaCount: 2

image:
repository: ${{ imageRepo }}/${{ appName }}
tag: "1.0.0-rc1"

ingress:
enabled: true
hosts:
- host: ${{ appName }}-stage.example.com
paths:
- path: /
pathType: Prefix

env:
- name: ENVIRONMENT
value: "stage"
- name: PORT
value: "${{ port }}"

externalSecret:
enabled: true
data:
- secretKey: db-password
remoteRef:
key: stage/${{ appName }}/db-password
132 changes: 132 additions & 0 deletions backstage/templates/new-tenant-app/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
apiVersion: backstage.io/v1beta2
kind: Template
metadata:
name: new-tenant-app
title: New Tenant Application
description: Scaffold a new application using the platform's golden path Helm chart
tags:
- platform
- helm
- golden-path
spec:
owner: platform-team
type: service

parameters:
- title: Application Details
required:
- appName
- description
- owner
properties:
appName:
title: Application Name
type: string
description: Unique name for the application (e.g., app-c)
pattern: '^[a-z][a-z0-9-]*$'
description:
title: Description
type: string
description: Purpose of the application
owner:
title: Owner
type: string
description: Backstage Group or User that owns this app
default: app-developers
ui:field: OwnerPicker
ui:options:
allowedKinds:
- Group

- title: Container Configuration
required:
- awsAccountId
- awsRegion
properties:
awsAccountId:
title: AWS Account ID
type: string
description: 12-digit AWS account number for ECR
pattern: '^\d{12}$'
awsRegion:
title: AWS Region
type: string
description: AWS region for ECR (e.g., eu-north-1, us-east-1)
default: eu-north-1
port:
title: Application Port
type: integer
default: 8080

steps:
- id: fetch-skeleton
name: Fetch Skeleton
action: fetch:template
input:
url: ./skeleton
values:
appName: ${{ parameters.appName }}
description: ${{ parameters.description }}
awsAccountId: ${{ parameters.awsAccountId }}
awsRegion: ${{ parameters.awsRegion }}
imageRepo: ${{ parameters.awsAccountId }}.dkr.ecr.${{ parameters.awsRegion }}.amazonaws.com
port: ${{ parameters.port }}
owner: ${{ parameters.owner }}

- id: publish
name: Publish to GitHub
action: publish:github
input:
repoUrl: github.com?owner=tukue&repo=simpleAppInfraCode
branch: feature/${{ parameters.appName }}
defaultBranch: main
repoVisibility: public

- id: register
name: Register in Backstage
action: catalog:register
input:
repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
catalogInfoPath: /catalog-info.yaml

output:
links:
- title: Open in GitHub
url: ${{ steps.publish.output.remoteUrl }}
- title: Open Pull Request
url: ${{ steps.publish.output.pullRequestUrl }}
text:
- title: Next Steps
content: |
### After Scaffolding

1. Add the app entries to `argocd/appsets/environments.yaml`:
```yaml
- env: dev
app: ${{ parameters.appName }}
valuesPath: platform/apps/${{ parameters.appName }}/dev/values.yaml
- env: stage
app: ${{ parameters.appName }}
valuesPath: platform/apps/${{ parameters.appName }}/stage/values.yaml
- env: prod
app: ${{ parameters.appName }}
valuesPath: platform/apps/${{ parameters.appName }}/prod/values.yaml
```

2. Add a `catalog-info.yaml` at `platform/apps/${{ parameters.appName }}/catalog-info.yaml`:
```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: ${{ parameters.appName }}
description: ${{ parameters.description }}
spec:
type: service
owner: ${{ parameters.owner }}
system: platform
dependsOn:
- component:app-chart
```

3. Open a PR to merge the new app into main.
4. Argo CD will automatically pick up the new ApplicationSet entries.
Loading
Loading