Skip to main content

Open Service Portal Workspace

This is a workspace directory containing multiple Open Service Portal repositories for unified development.

Repository Structure

This workspace contains the following repositories:

Core Application

Crossplane Templates & GitOps

Service Templates (Backstage)

Setup

To set up this workspace, clone each repository:

# Clone the workspace (this repository)
git clone git@github.com:open-service-portal/portal-workspace.git open-service-portal
cd open-service-portal

# Clone core application repository
git clone git@github.com:open-service-portal/app-portal.git

# Clone all repositories with the sync script
./scripts/repos-sync.sh

Development

Each repository has its own development workflow. See CLAUDE.md for detailed development commands and architecture information.

Quick Start

# Start Backstage
cd app-portal
yarn install
yarn start

Documentation

Kubernetes Setup

Prerequisites

  • Kubernetes cluster (Kind, Rancher Desktop, Minikube, or cloud)
  • kubectl configured
  • Helm installed

Automated Cluster Setup

# Run unified setup script for any Kubernetes cluster
./scripts/cluster-setup.sh

# This installs:
# - NGINX Ingress Controller
# - Flux GitOps with catalog watcher (for XRDs/Compositions)
# - Flux GitOps with catalog-orders watcher (for XR instances)
# - Crossplane v2.0 with namespaced XRs
# - Composition functions (go-templating, patch-and-transform, etc.)
# - Base environment configurations
# - provider-kubernetes with RBAC
# - provider-helm for chart deployments
# - External-DNS for DNS management (supports multiple providers)
# - Backstage service account + token

Environment Configuration

Configure your cluster after setup:

# Option 1: Auto-detect cluster from kubectl context (recommended)
./scripts/cluster-config.sh

# The config script will:
# - Extract cluster name from context (multiple contexts can share same cluster)
# - Create Backstage configuration (app-config.{cluster}.local.yaml)
# - Configure External-DNS with Cloudflare credentials (if provided)
# - Update EnvironmentConfigs
# - Configure Flux to watch catalog-orders using cluster name

For the generic cluster-config.sh, create an environment file matching your cluster name (not context):

# For rancher-desktop cluster
cp .env.rancher-desktop.example .env.rancher-desktop
# Edit with your settings
vim .env.rancher-desktop

# For OpenPortal cluster (shared by multiple contexts with different auth methods)
cp .env.openportal.example .env.openportal
vim .env.openportal

Note: Configuration uses cluster names, allowing multiple contexts (e.g., different auth methods) to share the same config.

DNS Management with External-DNS

We use External-DNS for DNS management, which supports namespace isolation and multiple DNS providers.

Setup

  1. Configure credentials in your environment file:

    # For production (.env.openportal)
    CLOUDFLARE_API_TOKEN=your-api-token
    CLOUDFLARE_ZONE_ID=your-zone-id
    CLOUDFLARE_ZONE_NAME=openportal.dev

    # For local with real DNS (.env.rancher-desktop)
    BASE_DOMAIN=localhost # For local app access
    CLOUDFLARE_API_TOKEN=your-token # Optional: for real DNS
    CLOUDFLARE_ZONE_NAME=openportal.dev # Zone for DNS records
  2. Apply configuration:

    ./scripts/config.sh  # Auto-detects cluster

Creating DNS Records

DNS records are created via CloudflareDNSRecord XRs or DNSEndpoint resources:

Option 1: Using CloudflareDNSRecord XR (recommended)

apiVersion: openportal.dev/v1alpha1
kind: CloudflareDNSRecord
metadata:
name: my-app
namespace: my-namespace
spec:
name: my-app
type: A
value: "192.168.1.100"
ttl: 300

Option 2: Direct DNSEndpoint

apiVersion: externaldns.openportal.dev/v1alpha1
kind: DNSEndpoint
metadata:
name: my-app-dns
namespace: my-namespace
spec:
endpoints:
- dnsName: my-app.openportal.dev
recordType: A
targets: ['192.168.1.100']

Template Management

We provide scripts to manage Crossplane templates:

# Check status of all templates (releases and PRs)
./scripts/template-status.sh

# Reload all templates in the cluster
./scripts/template-reload.sh

# Create a new release for a template
./scripts/template-release.sh template-name

Crossplane Templates

We use a GitOps catalog pattern for managing Crossplane templates:

  1. Create Template: Follow the pattern in template-dns-record/
  2. Register in Catalog: Add to catalog/templates/
  3. Flux Syncs: Automatically discovers and installs templates
  4. Use Template: Create XRs directly in your namespace (no claims needed!)

See Crossplane Catalog Setup for details.

Key Features

Crossplane v2 with Namespaced XRs

  • Developers create XRs directly in their namespaces
  • No need for separate claim resources
  • Better namespace isolation and standard RBAC

GitOps Everything

  • Flux manages all deployments
  • Central catalog for template discovery
  • Git as single source of truth

Modern Infrastructure as Code

  • Pipeline mode compositions with functions
  • Shared environment configurations
  • Reusable transformation logic

Scripts Reference

Cluster Management

  • cluster-setup.sh - Universal K8s cluster setup with all platform components
  • cluster-config.sh - Auto-detect cluster name and configure (cluster-based, not context-based)
  • cluster-cleanup.sh - Remove all platform components cleanly
  • cluster-kubeconfig.sh - Extract and manage kubeconfig files

Template Management

  • template-status.sh - Check template releases and PR status
  • template-reload.sh - Reload templates with finalizer handling
  • template-release.sh - Automate GitHub releases for templates

Repository Management

  • repos-sync.sh - Clone/update all nested repositories

Custom Plugins

The app-portal includes custom plugins:

  • kubernetes-ingestor - Enhanced Kubernetes resource monitoring
  • scaffolder actions - Custom actions for template processing

Note

This workspace parent directory is version controlled separately to maintain:

  • Workspace-level documentation (this README, CLAUDE.md)
  • Shared configurations and setup scripts
  • Cross-repository documentation
  • Unified cluster setup and management scripts

The actual repository directories are excluded via .gitignore.