rdev/internal/domain/deployment.go
jordan 1790afd0ee feat: add path-based ingress management for component lifecycle
Adds AddIngressPath and RemoveIngressPath to the Deployer interface
for managing per-component ingress rules in monorepo projects.

- Implement conflict retry logic for concurrent ingress updates
- Add K8s client interface for testability
- Add comprehensive unit tests for ingress path operations
- Add component deployment and teardown methods to ComponentService
- Update service templates with OpenAPI spec improvements
- Add evolving-app cookbook tree for reference
- Split resources.go into resources_ingress.go for path-based routing
- Split component.go into component_deploy.go for deployment helpers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 01:31:50 -07:00

101 lines
3.7 KiB
Go

// Package domain contains pure domain models with no external dependencies.
package domain
import "time"
// DeploySpec defines a deployment request.
type DeploySpec struct {
ProjectName string // Project identifier
ComponentPath string // Component path within monorepo (e.g., "services/auth-api"), empty for single-app projects
Image string // Container image (e.g., zot.threesix.svc:5000/myapp:latest)
Domain string // Domain for ingress (e.g., myapp.threesix.ai)
Port int // Container port to expose
Replicas int // Number of replicas
EnvVars map[string]string // Plain environment variables
Secrets map[string]string // Secret environment variables (stored in K8s Secret)
BasePath string // URL path prefix for Ingress (e.g., "/api/auth", "/"); empty defaults to "/"
}
// DeploymentName returns the K8s resource name for this deployment.
// For monorepo components, it's "{project}-{component}", for single apps it's just "{project}".
func (s DeploySpec) DeploymentName() string {
if s.ComponentPath == "" {
return s.ProjectName
}
// Extract component name from path (e.g., "services/auth-api" -> "auth-api")
parts := splitPath(s.ComponentPath)
if len(parts) > 0 {
return s.ProjectName + "-" + parts[len(parts)-1]
}
return s.ProjectName
}
// splitPath splits a path like "services/auth-api" into ["services", "auth-api"].
func splitPath(path string) []string {
var parts []string
current := ""
for _, c := range path {
if c == '/' {
if current != "" {
parts = append(parts, current)
current = ""
}
} else {
current += string(c)
}
}
if current != "" {
parts = append(parts, current)
}
return parts
}
// DeployStatus represents the current state of a deployment.
type DeployStatus struct {
ProjectName string
ComponentPath string // Component path if this is a component deployment
Image string
Replicas int
ReadyReplicas int
URL string
Status DeploymentStatus
CreatedAt time.Time
UpdatedAt time.Time
}
// ComponentDeployStatus holds deployment status for a component.
type ComponentDeployStatus struct {
ComponentPath string // e.g., "services/auth-api"
ComponentName string // e.g., "auth-api"
ComponentType string // e.g., "service", "worker", "app"
Image string // Container image
Replicas int // Desired replicas
ReadyReplicas int // Ready replicas
URL string // URL if component has ingress
Status DeploymentStatus // Current status
}
// ProjectDeployStatus holds overall deployment status for a project.
type ProjectDeployStatus struct {
ProjectName string // Project identifier
Components []ComponentDeployStatus // Status of each deployed component
OverallURL string // Primary URL (usually app or main service)
}
// DeploymentStatus represents the state of a deployment.
type DeploymentStatus string
const (
DeploymentStatusNone DeploymentStatus = "none" // No deployment exists
DeploymentStatusPending DeploymentStatus = "pending" // Deployment created, waiting for pods
DeploymentStatusRunning DeploymentStatus = "running" // All pods are ready
DeploymentStatusFailed DeploymentStatus = "failed" // Pods failed to start
DeploymentStatusUpdating DeploymentStatus = "updating" // Rolling update in progress
DeploymentStatusUnknown DeploymentStatus = "unknown" // Status could not be determined
)
// IsReady returns true if the deployment is fully available.
func (s DeploymentStatus) IsReady() bool {
return s == DeploymentStatusRunning
}