Skip to content
Draft
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
1 change: 0 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ make e2e-authz-infra-down # Stop authorization test infrastructure
- **internal/**: Internal packages (not importable by external modules)
- **docs/**: API documentation and design references
- **openapi/**: OpenAPI/Swagger specifications
- **deployment/**: Kubernetes deployment manifests
- **test/**: Integration and E2E test suites
- **hack/**: Development scripts and utilities

Expand Down
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Build stage
FROM golang:1.25-alpine AS builder
ARG TARGETPLATFORM
FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Use an approved or digest-pinned builder image.

golang:1.26-alpine is a mutable non-Red Hat image tag. Switch to an approved catalog.redhat.com builder image or pin the non-RH image by digest.

As per path instructions, “Base image: UBI minimal or distroless from catalog.redhat.com” and “non-RH images: pin by digest.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` at line 3, The builder stage is using a mutable non-Red Hat image
tag, so update the Dockerfile’s FROM statement to use an approved
catalog.redhat.com builder image or pin the existing golang:1.26-alpine image by
digest. Keep the change scoped to the builder base image reference and ensure it
follows the repository’s base image requirements.

Source: Path instructions


# Build arguments for OS and architecture support
ARG TARGETOS=linux
ARG TARGETARCH=amd64
ARG TARGETOS
ARG TARGETARCH

WORKDIR /app

Expand Down
39 changes: 15 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,14 @@ flowchart LR

## Configuration

| Flag | Default | Description |
| ------------------- | ------------------------------------------------ | ------------------------ |
| `--api-port` | `8000` | API server port |
| `--maestro-url` | `http://maestro:8000` | Maestro API URL |
| `--hyperfleet-url` | `http://hyperfleet-api.hyperfleet-system:8000` | Hyperfleet API base URL |
| `--dynamodb-table` | `rosa-customer-accounts` | DynamoDB table |
| `--dynamodb-region` | `us-east-1` | AWS region |
| `--zoa.enabled` | `false` | Enable ZOA Trusted Actions |
| `--zoa.table-name` | `rosa-zoa-actions` | ZOA DynamoDB table |
| `--zoa.audit-table-name` | `rosa-zoa-audit` | ZOA audit log table |
| `--zoa.bucket-name` | `rosa-zoa-artifacts` | ZOA S3 artifacts bucket |
| `--zoa.aws-region` | `us-east-1` | ZOA AWS region |
| `--zoa.templates-dir` | `/etc/zoa/templates` | ZOA action templates dir |
| `--zoa.job-config-dir` | `/etc/zoa/jobs` | ZOA job configuration dir |
| `--zoa.poll-interval` | `30s` | ZOA job poll interval |
| Flag | Default | Description |
| ------------------------ | ------------------ | ------------------------------ |
| `--api-port` | `8000` | API server port |
| `--fleet-db-cluster-name`| (required) | EKS cluster name for fleet-db |
| `--aws-region` | `us-east-1` | AWS region for fleet-db and DynamoDB |
| `--dynamodb-region` | (same as aws-region) | AWS region for DynamoDB |
| `--dynamodb-prefix` | `rosa` | Prefix for DynamoDB table names |
| `--allowed-accounts` | (none) | Comma-separated allowed AWS account IDs |

## Build

Expand Down Expand Up @@ -431,7 +424,7 @@ The e2e suite includes several test categories:
2. **Platform API Tests** (`e2e_test.go`)
- Tests API endpoints (`/live`, `/ready`, etc.)
- Management cluster operations
- ManifestWork creation and distribution
- Cluster and nodepool operations

3. **Authorization Tests** (`authz_e2e_test.go`)
- Tests Cedar-based authorization
Expand Down Expand Up @@ -472,23 +465,21 @@ awscurl -X POST https://z11111111.execute-api.us-east-2.amazonaws.com/prod/api/v
--service execute-api \
--region us-east-2 \
-H "Content-Type: application/json" \
-d '{"name": "management-01", "labels": {"cluster_type": "management", "cluster_id": "management-01"}}'
-d '{"id": "mc-useast2-1", "region": "us-east-2", "account_id": "123456789012"}'
```

### Get the current resource bundles
### List management clusters
```bash
awscurl https://z11111111.execute-api.us-east-2.amazonaws.com/prod/api/v0/resource_bundles \
awscurl https://z11111111.execute-api.us-east-2.amazonaws.com/prod/api/v0/management_clusters \
--service execute-api \
--region us-east-2
```

### Create a manifestwork for management-01
### List clusters
```bash
# see swagger for reference for the payload struct
awscurl -X POST https://z11111111.execute-api.us-east-2.amazonaws.com/prod/api/v0/work \
awscurl https://z11111111.execute-api.us-east-2.amazonaws.com/prod/api/v0/clusters \
--service execute-api \
--region us-east-2 \
-d @payload.json
--region us-east-2
```


Expand Down
89 changes: 55 additions & 34 deletions cmd/rosa-regional-platform-api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,35 @@ import (
"context"
"fmt"
"log/slog"
"net/url"
"os"
"os/signal"
"strings"
"syscall"

awsconfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"

"github.com/openshift/rosa-regional-platform-api/pkg/clients/fleetdb"
"github.com/openshift/rosa-regional-platform-api/pkg/config"
"github.com/openshift/rosa-regional-platform-api/pkg/server"
)

var (
// Config flags
logLevel string
logFormat string
maestroURL string
maestroGRPCURL string
hyperfleetURL string
allowedAccounts string
dynamodbRegion string
dynamodbPrefix string
apiPort int
healthPort int
metricsPort int
logLevel string
logFormat string
fleetDBClusterName string
allowedAccounts string
dynamodbRegion string
dynamodbPrefix string
oidcIssuerBaseURL string
apiPort int
healthPort int
metricsPort int
)

func main() {
Expand All @@ -53,12 +57,11 @@ var serveCmd = &cobra.Command{
func init() {
serveCmd.Flags().StringVar(&logLevel, "log-level", "info", "Log level (debug, info, warn, error)")
serveCmd.Flags().StringVar(&logFormat, "log-format", "json", "Log format (json, text)")
serveCmd.Flags().StringVar(&maestroURL, "maestro-url", "http://maestro:8000", "Maestro service base URL")
serveCmd.Flags().StringVar(&allowedAccounts, "allowed-accounts", "", "Comma-separated list of allowed AWS account IDs")
serveCmd.Flags().StringVar(&maestroGRPCURL, "maestro-grpc-url", "maestro-grpc.maestro-server:8090", "Maestro gRPC service base URL")
serveCmd.Flags().StringVar(&hyperfleetURL, "hyperfleet-url", "http://hyperfleet-api.hyperfleet-system:8000", "Hyperfleet service base URL")
serveCmd.Flags().StringVar(&dynamodbRegion, "dynamodb-region", "", "AWS region for DynamoDB (defaults to us-east-1)")
serveCmd.Flags().StringVar(&fleetDBClusterName, "fleet-db-cluster-name", "", "EKS cluster name for fleet-db")
serveCmd.Flags().StringVar(&dynamodbRegion, "dynamodb-region", "", "AWS region for DynamoDB (defaults to auto-detected region)")
serveCmd.Flags().StringVar(&dynamodbPrefix, "dynamodb-prefix", "rosa", "Prefix for DynamoDB table names (default: rosa)")
serveCmd.Flags().StringVar(&oidcIssuerBaseURL, "oidc-issuer-base-url", "", "Base URL for OIDC issuer (e.g. https://<cloudfront-domain>)")
serveCmd.Flags().IntVar(&apiPort, "api-port", 8000, "API server port")
serveCmd.Flags().IntVar(&healthPort, "health-port", 8080, "Health check server port")
serveCmd.Flags().IntVar(&metricsPort, "metrics-port", 9090, "Metrics server port")
Expand All @@ -75,34 +78,38 @@ func runServe(cmd *cobra.Command, args []string) error {
"log_format", logFormat,
)

// Detect AWS region from SDK default chain (IMDS, AWS_REGION env var, etc.)
awsCfg, err := awsconfig.LoadDefaultConfig(context.Background())
if err != nil {
return fmt.Errorf("failed to detect AWS region: %w", err)
}
if awsCfg.Region == "" {
return fmt.Errorf("AWS region could not be detected from environment; set AWS_REGION")
}
logger.Info("detected AWS region", "region", awsCfg.Region)

// Create config
cfg := config.NewConfig()
cfg.Logging.Level = logLevel
cfg.Logging.Format = logFormat
cfg.Maestro.BaseURL = maestroURL
cfg.Maestro.GRPCBaseURL = maestroGRPCURL

// Validate Hyperfleet URL
parsedURL, err := url.ParseRequestURI(hyperfleetURL)
if err != nil {
logger.Error("invalid hyperfleet URL", "url", hyperfleetURL, "error", err)
return fmt.Errorf("invalid hyperfleet URL: %w", err)
}
if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
logger.Error("hyperfleet URL must have http or https scheme", "url", hyperfleetURL, "scheme", parsedURL.Scheme)
return fmt.Errorf("hyperfleet URL must have http or https scheme, got: %s", parsedURL.Scheme)
if fleetDBClusterName == "" {
return fmt.Errorf("--fleet-db-cluster-name is required")
}
cfg.Hyperfleet.BaseURL = hyperfleetURL
cfg.FleetDB.ClusterName = fleetDBClusterName
cfg.FleetDB.AWSRegion = awsCfg.Region

cfg.Regional.OIDCIssuerBaseURL = oidcIssuerBaseURL
cfg.AllowedAccounts = parseAllowedAccounts(allowedAccounts)
cfg.Server.APIPort = apiPort
cfg.Server.HealthPort = healthPort
cfg.Server.MetricsPort = metricsPort

// Set DynamoDB region from flag if provided
// Set DynamoDB region: --dynamodb-region if set, otherwise fall back to auto-detected region
if dynamodbRegion != "" {
cfg.Authz.AWSRegion = dynamodbRegion
logger.Info("using DynamoDB region from flag", "region", dynamodbRegion)
} else {
cfg.Authz.AWSRegion = awsCfg.Region
Comment on lines +107 to +112

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reuse the detected region for ZOA when --dynamodb-region is omitted.

cfg.Authz.AWSRegion falls back to awsCfg.Region, but the ZOA block still uses the raw dynamodbRegion. With ZOA_ENABLED=true and no flag, ZOA starts with an empty AWS region.

Proposed fix
 	if dynamodbRegion != "" {
 		cfg.Authz.AWSRegion = dynamodbRegion
 		logger.Info("using DynamoDB region from flag", "region", dynamodbRegion)
 	} else {
 		cfg.Authz.AWSRegion = awsCfg.Region
 	}
+	cfg.Zoa.AWSRegion = cfg.Authz.AWSRegion
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/rosa-regional-platform-api/main.go` around lines 107 - 112, Reuse the
detected AWS region for ZOA when --dynamodb-region is not provided by ensuring
the ZOA startup path in main.go uses the same resolved region as
cfg.Authz.AWSRegion. Update the ZOA block to reference the effective region
value (dynamodbRegion if set, otherwise awsCfg.Region) instead of the raw flag
variable, so ZOA starts with a non-empty AWS region when ZOA_ENABLED is true.
Focus the change around the region resolution logic in main and the ZOA
initialization code.

}

// Set DynamoDB table name prefix
Expand Down Expand Up @@ -163,8 +170,23 @@ func runServe(cmd *cobra.Command, args []string) error {
)
}

// Create fleet-db client (reuses awsCfg from region detection above)
fleetDBClient, err := fleetdb.NewClient(context.Background(), awsCfg, cfg.FleetDB.ClusterName, logger)
if err != nil {
return fmt.Errorf("failed to create fleet-db client: %w", err)
}

// Create in-cluster client for the RC (local cluster) — used by MC handler
// to manage the hyperfleet-mc-config ConfigMap.
scheme := runtime.NewScheme()
_ = corev1.AddToScheme(scheme)
rcClient, err := ctrlclient.New(ctrl.GetConfigOrDie(), ctrlclient.Options{Scheme: scheme})
Comment on lines +181 to +183

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant file and nearby startup code
git ls-files cmd/rosa-regional-platform-api/main.go
wc -l cmd/rosa-regional-platform-api/main.go
sed -n '140,230p' cmd/rosa-regional-platform-api/main.go

# Find the runServe definition and any existing error handling around startup
rg -n "func .*runServe|GetConfigOrDie|AddToScheme|ctrlclient.New|GetConfig\\(" cmd/rosa-regional-platform-api/main.go

Repository: openshift-online/rosa-hyperfleet-api

Length of output: 3157


Avoid GetConfigOrDie() in startup setup. ctrl.GetConfigOrDie() can terminate the process before runServe returns, so use ctrl.GetConfig() and propagate the error; handle corev1.AddToScheme instead of discarding its return.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/rosa-regional-platform-api/main.go` around lines 181 - 183, The startup
setup in main.go is using ctrl.GetConfigOrDie() and discarding the result of
corev1.AddToScheme, which can hide initialization failures and terminate the
process unexpectedly. Update the setup around scheme creation and ctrlclient.New
to call ctrl.GetConfig() and return or propagate any error instead of exiting,
and check the error from corev1.AddToScheme before constructing the client so
initialization failures are handled cleanly within runServe.

Source: Path instructions

if err != nil {
return fmt.Errorf("failed to create RC in-cluster client: %w", err)
}

// Create server
srv, err := server.New(cfg, logger)
srv, err := server.New(cfg, fleetDBClient, rcClient, logger)
if err != nil {
return fmt.Errorf("failed to create server: %w", err)
}
Expand All @@ -178,9 +200,8 @@ func runServe(cmd *cobra.Command, args []string) error {
"api_port", cfg.Server.APIPort,
"health_port", cfg.Server.HealthPort,
"metrics_port", cfg.Server.MetricsPort,
"maestro_url", cfg.Maestro.BaseURL,
"maestro_grpc_url", cfg.Maestro.GRPCBaseURL,
"hyperfleet_url", cfg.Hyperfleet.BaseURL,
"fleet_db_cluster", cfg.FleetDB.ClusterName,
"aws_region", cfg.FleetDB.AWSRegion,
"allowed_accounts_count", len(cfg.AllowedAccounts),
)

Expand Down
1 change: 0 additions & 1 deletion cmd/rosa-regional-platform-api/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ func TestServeCmd(t *testing.T) {
expectedFlags := []string{
"log-level",
"log-format",
"maestro-url",
"allowed-accounts",
"api-port",
"health-port",
Expand Down
31 changes: 0 additions & 31 deletions deployment/argocd/application-helm-hook.yaml

This file was deleted.

31 changes: 0 additions & 31 deletions deployment/argocd/application.yaml

This file was deleted.

23 changes: 0 additions & 23 deletions deployment/helm/rosa-regional-frontend/.helmignore

This file was deleted.

6 changes: 0 additions & 6 deletions deployment/helm/rosa-regional-frontend/Chart.yaml

This file was deleted.

34 changes: 0 additions & 34 deletions deployment/helm/rosa-regional-frontend/README.md

This file was deleted.

Loading