Skip to content

Latest commit

 

History

History
399 lines (306 loc) · 12.2 KB

File metadata and controls

399 lines (306 loc) · 12.2 KB

Plugin Architecture

Stratos uses a plugin system to extend both the backend (Jetstream) and frontend (Angular) with modular, independently loadable capabilities. Plugins add endpoint types, HTTP routes, middleware, and frontend UI packages to the console.

Overview

flowchart TD
    cfg["plugin-config.yaml (single source of truth)
    plugins: cloudfoundry, cfapppush, cfappssh, userinvite,
    autoscaler, kubernetes, analysis, monocular"]
    be["extra_plugins.go
    (backend build)"]
    fe["extra_plugins.go
    (frontend build)"]
    cfg -- go generate --> be
    cfg -- frontend prebuild --> fe
Loading

Backend Plugins

Plugin Interface

Every backend plugin implements api.StratosPlugin (src/jetstream/api/plugin.go):

type StratosPlugin interface {
    Init() error
    GetMiddlewarePlugin() (MiddlewarePlugin, error)
    GetEndpointPlugin() (EndpointPlugin, error)
    GetRoutePlugin() (RoutePlugin, error)
}

A plugin returns a concrete implementation from one or more of these methods and nil for the rest. The sub-interfaces are:

Interface Purpose Methods
EndpointPlugin Defines an endpoint type (CF, K8s, metrics) GetType, Register, Connect, Validate, Info, UpdateMetadata
RoutePlugin Adds HTTP routes to the Echo server AddAdminGroupRoutes, AddSessionGroupRoutes
MiddlewarePlugin Injects request/response middleware EchoMiddleware, SessionEchoMiddleware

Two optional interfaces provide additional lifecycle hooks:

Interface Purpose
StratosPluginCleanup Called on shutdown (Destroy())
EndpointNotificationPlugin Notified when endpoints are added/removed

A ConfigPlugin interface allows early configuration before plugin loading via RegisterJetstreamConfigPlugin().

Registration

Plugins register themselves in a Go init() function using blank imports. The init() function calls api.AddPlugin with the plugin name, an optional dependency list, and an initializer function:

package cloudfoundry

func init() {
    api.AddPlugin("cloudfoundry", nil, Init)
}

func Init(portalProxy api.PortalProxy) (api.StratosPlugin, error) {
    return &CloudFoundrySpecification{portalProxy: portalProxy}, nil
}

Plugins that depend on other plugins declare them explicitly:

func init() {
    api.AddPlugin("cfapppush", []string{"cloudfoundry"}, Init)
}

The loader in load_plugins.go resolves dependencies recursively. A plugin with an unmet dependency is skipped with a log warning.

Import Files

Two Go files control which plugins are compiled into the binary:

File Purpose Maintained by
src/jetstream/default_plugins.go Core plugins always included Developer (manual)
src/jetstream/extra_plugins.go Feature plugins from plugin-config.yaml Build system (auto-generated)
src/jetstream/plugin-config.yaml Extra plugin list (single source of truth) Developer (manual)

default_plugins.go — hardcoded imports for plugins that every deployment needs regardless of which frontend packages are enabled:

import (
    _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/backup"
    _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/cloudfoundryhosting"
    _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/metrics"
    _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/userfavorites"
    _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/userinfo"
)

extra_plugins.go — auto-generated by the prebuild step. Do not edit manually. See Decoupled Builds for details.

Plugin Loading Order

main() → portalProxy.loadPlugins()
    1. yamlgenerated.MakePluginsFromConfig()   ← reads plugins.yaml
    2. for each name in api.PluginInits:
         addPlugin(name)
           → resolve dependencies (recursive)
           → call Init(portalProxy)
           → store in pp.Plugins map

Plugin Catalog

Default Plugins

These are always compiled in via default_plugins.go.

Plugin Type Purpose
backup Route Endpoint and token backup/restore (admin-only)
cloudfoundryhosting Middleware, Config Stratos-in-CF deployment support — auto-registration, session affinity, CF UAA config
metrics Endpoint, Route Prometheus endpoint type; correlates metrics with CF and K8s endpoints
userfavorites Route User favorites/bookmarks persistence in the database
userinfo Route User profile management — get/update info and password; supports UAA, local, and no-auth modes

Extra Plugins

These are compiled in via the auto-generated extra_plugins.go based on frontend package declarations.

Plugin Depends On Frontend Package Purpose
cloudfoundry — cloud-foundry CF endpoint type, auth, firehose and app stream endpoints
cfapppush cloudfoundry cloud-foundry Application deployment/push workflow from the UI
cfappssh cloudfoundry cloud-foundry SSH into CF app instances via WebSocket proxy
userinvite cloudfoundry cloud-foundry User invitation system with email and UAA client auth
autoscaler cloudfoundry cf-autoscaler Autoscaling policies, metrics, and events for CF apps
kubernetes — kubernetes K8s endpoint type with multi-auth (GKE, AWS, Azure, OIDC, cert, token)
analysis kubernetes kubernetes Cluster analysis and reporting via Popeye (tech preview)
monocular kubernetes kubernetes Helm chart repository management and ArtifactHub integration

Special Plugins

Plugin Purpose
desktop Desktop hosting mode — local endpoint/token store overlay; declared by desktop-extensions frontend package
yamlgenerated Generates endpoint type plugins at runtime from plugins.yaml (currently GitHub and GitLab git endpoints)

Dependency Graph

cloudfoundry
├── cfapppush
├── cfappssh
├── userinvite
└── autoscaler

kubernetes
├── analysis
└── monocular

(no dependencies)
├── backup
├── cloudfoundryhosting
├── metrics
├── userfavorites
├── userinfo
└── desktop

YAML-Generated Plugins

src/jetstream/plugins.yaml defines lightweight endpoint types that are created at runtime by the yamlgenerated plugin. These do not require a Go source directory — they are configured entirely via YAML:

- name: git
  sub_type: github
  auth_type: Token
  user_info: /user
  user_info_path: login

- name: git
  sub_type: gitlab
  auth_type: Bearer
  user_info: /user
  user_info_path: username

Each entry generates a plugin that implements EndpointPlugin and RoutePlugin with standard connect/validate/info flows. This is the preferred way to add simple token-authenticated endpoint types without writing Go code.

Frontend Integration

Frontend Package Metadata

Frontend packages declare their Angular integration metadata in package.json under the stratos key:

{
  "name": "@stratosui/cloud-foundry",
  "stratos": {
    "module": "CloudFoundryPackageModule",
    "routingModule": "CloudFoundryRoutingModule",
    "theming": "sass/_all-theme#apply-theme-stratos-cloud-foundry"
  }
}

The stratos metadata fields are:

Field Purpose
module Angular standalone module class name
routingModule Routing module for lazy-loaded routes
theming SASS theme file and mixin to apply
assets Asset configuration

Backend plugin declarations are managed centrally in src/jetstream/plugin-config.yaml, not in individual package.json files.

Plugin Configuration

The list of extra plugins is defined in src/jetstream/plugin-config.yaml, which serves as the single source of truth for both the backend and frontend builds.

Backend Build (go generate)

The backend uses a standard go generate directive to produce extra_plugins.go from plugin-config.yaml:

cd src/jetstream && go generate ./...
  └── cmd/gen-plugins/main.go
        → reads plugin-config.yaml
        → validates plugin dirs exist
        → writes extra_plugins.go

make build backend runs go generate automatically before go build.

Frontend Build (prebuild)

The frontend prebuild pipeline also reads plugin-config.yaml to generate the same extra_plugins.go:

bun run prebuild
  └── build-orchestrator.js
        └── backend.ts → reads plugin-config.yaml
                        → validates plugin dirs exist
                        → writes extra_plugins.go

bun run build (ng build)
  └── Angular CLI compiles frontend

Decoupled Builds

Either build can run independently. The backend only needs Go tooling (go generate ./...). The frontend prebuild reads the same YAML file with Node.js. Both produce identical extra_plugins.go output.

The stratos.yaml backend key is still supported as an override mechanism for custom builds.

Creating a New Plugin

Backend Plugin

# 1. Create plugin directory and main file
mkdir -p src/jetstream/plugins/myplugin
// src/jetstream/plugins/myplugin/main.go
package myplugin

import (
    "github.com/cloudfoundry/stratos/src/jetstream/api"
    "github.com/labstack/echo/v4"
)

func init() {
    api.AddPlugin("myplugin", nil, Init)
}

type MyPlugin struct {
    portalProxy api.PortalProxy
}

func Init(portalProxy api.PortalProxy) (api.StratosPlugin, error) {
    return &MyPlugin{portalProxy: portalProxy}, nil
}

func (p *MyPlugin) Init() error { return nil }

func (p *MyPlugin) GetMiddlewarePlugin() (api.MiddlewarePlugin, error) {
    return nil, errors.New("not implemented")
}

func (p *MyPlugin) GetEndpointPlugin() (api.EndpointPlugin, error) {
    return nil, errors.New("not implemented")
}

func (p *MyPlugin) GetRoutePlugin() (api.RoutePlugin, error) {
    return p, nil
}

func (p *MyPlugin) AddAdminGroupRoutes(echoGroup *echo.Group) {
    // Admin-only routes
}

func (p *MyPlugin) AddSessionGroupRoutes(echoGroup *echo.Group) {
    echoGroup.GET("/myplugin/data", p.getData)
}

func (p *MyPlugin) getData(c echo.Context) error {
    return c.JSON(200, map[string]string{"status": "ok"})
}

Enabling an Extra Plugin

Add the plugin name to src/jetstream/plugin-config.yaml:

plugins:
  - cloudfoundry
  - cfapppush
  # ...
  - myplugin

Run go generate ./... from src/jetstream/ to regenerate extra_plugins.go.

Adding a Default Plugin

If the plugin should always be compiled in (regardless of frontend packages), add its import to default_plugins.go instead:

import (
    _ "github.com/cloudfoundry/stratos/src/jetstream/plugins/myplugin"
)

Adding a YAML-Generated Endpoint

For simple token-authenticated endpoints, add an entry to src/jetstream/plugins.yaml instead of writing Go code:

- name: myservice
  sub_type: myvariant
  auth_type: Token        # Token, Bearer, or HttpBasic
  user_info: /api/user    # REST endpoint to fetch user info
  user_info_path: username # JSON path to extract username

Key Files Reference

File Purpose
src/jetstream/api/plugin.go Plugin interfaces and registration API
src/jetstream/load_plugins.go Plugin loader with dependency resolution
src/jetstream/default_plugins.go Hardcoded default plugin imports
src/jetstream/extra_plugins.go Auto-generated plugin imports (do not edit)
src/jetstream/plugin-config.yaml Extra plugin list (single source of truth)
src/jetstream/generate.go go:generate directive for extra_plugins.go
src/jetstream/cmd/gen-plugins/main.go Go generator that reads plugin-config.yaml
src/jetstream/plugins.yaml YAML config for runtime-generated endpoint types
src/jetstream/plugins/yamlgenerated/main.go YAML plugin generator
src/frontend/packages/devkit/src/backend.ts Prebuild script that generates extra_plugins.go from plugin-config.yaml
src/frontend/packages/devkit/src/lib/stratos.config.ts Config parser that reads stratos/package metadata
build/build-orchestrator.js Prebuild pipeline orchestrator