236 lines
5.4 KiB
Go
236 lines
5.4 KiB
Go
package deployment
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"sync"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// DeploymentStatus represents deployment status
|
||
|
|
type DeploymentStatus string
|
||
|
|
|
||
|
|
const (
|
||
|
|
StatusPending DeploymentStatus = "pending"
|
||
|
|
StatusBuilding DeploymentStatus = "building"
|
||
|
|
StatusPushing DeploymentStatus = "pushing"
|
||
|
|
StatusApplying DeploymentStatus = "applying"
|
||
|
|
StatusSuccess DeploymentStatus = "success"
|
||
|
|
StatusFailed DeploymentStatus = "failed"
|
||
|
|
)
|
||
|
|
|
||
|
|
// DeploymentInfo represents a deployment attempt
|
||
|
|
type DeploymentInfo struct {
|
||
|
|
ID string
|
||
|
|
Version string
|
||
|
|
Status DeploymentStatus
|
||
|
|
StartedAt time.Time
|
||
|
|
CompletedAt time.Time
|
||
|
|
Container string
|
||
|
|
Registry string
|
||
|
|
Manifest string
|
||
|
|
}
|
||
|
|
|
||
|
|
// SelfDeployer handles orchestrator self-deployment
|
||
|
|
type SelfDeployer struct {
|
||
|
|
mu sync.RWMutex
|
||
|
|
deployments map[string]*DeploymentInfo
|
||
|
|
currentVersion string
|
||
|
|
registry string
|
||
|
|
kubeConfig string
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewSelfDeployer creates a new self deployer
|
||
|
|
func NewSelfDeployer(registry, kubeConfig string) *SelfDeployer {
|
||
|
|
return &SelfDeployer{
|
||
|
|
deployments: make(map[string]*DeploymentInfo),
|
||
|
|
currentVersion: "1.0.0",
|
||
|
|
registry: registry,
|
||
|
|
kubeConfig: kubeConfig,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// BuildContainer builds a Docker container image
|
||
|
|
func (sd *SelfDeployer) BuildContainer(version string) (string, error) {
|
||
|
|
if version == "" {
|
||
|
|
return "", fmt.Errorf("version required")
|
||
|
|
}
|
||
|
|
|
||
|
|
sd.mu.Lock()
|
||
|
|
defer sd.mu.Unlock()
|
||
|
|
|
||
|
|
deploymentID := fmt.Sprintf("deploy-%s-%d", version, len(sd.deployments))
|
||
|
|
|
||
|
|
deployment := &DeploymentInfo{
|
||
|
|
ID: deploymentID,
|
||
|
|
Version: version,
|
||
|
|
Status: StatusBuilding,
|
||
|
|
StartedAt: time.Now(),
|
||
|
|
Container: fmt.Sprintf("%s/orchestrator:%s", sd.registry, version),
|
||
|
|
Registry: sd.registry,
|
||
|
|
}
|
||
|
|
|
||
|
|
sd.deployments[deploymentID] = deployment
|
||
|
|
|
||
|
|
// Simulate build
|
||
|
|
deployment.Status = StatusPushing
|
||
|
|
|
||
|
|
return deploymentID, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// PushImage pushes the container image to registry
|
||
|
|
func (sd *SelfDeployer) PushImage(deploymentID string) error {
|
||
|
|
sd.mu.Lock()
|
||
|
|
defer sd.mu.Unlock()
|
||
|
|
|
||
|
|
deployment, exists := sd.deployments[deploymentID]
|
||
|
|
if !exists {
|
||
|
|
return fmt.Errorf("deployment not found: %s", deploymentID)
|
||
|
|
}
|
||
|
|
|
||
|
|
if deployment.Status != StatusPushing {
|
||
|
|
return fmt.Errorf("invalid status for push: %s", deployment.Status)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Simulate push
|
||
|
|
deployment.Status = StatusApplying
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// GenerateManifest generates K8s manifests
|
||
|
|
func (sd *SelfDeployer) GenerateManifest(deploymentID string, replicas int) (string, error) {
|
||
|
|
sd.mu.Lock()
|
||
|
|
defer sd.mu.Unlock()
|
||
|
|
|
||
|
|
deployment, exists := sd.deployments[deploymentID]
|
||
|
|
if !exists {
|
||
|
|
return "", fmt.Errorf("deployment not found: %s", deploymentID)
|
||
|
|
}
|
||
|
|
|
||
|
|
manifest := fmt.Sprintf(`
|
||
|
|
apiVersion: apps/v1
|
||
|
|
kind: Deployment
|
||
|
|
metadata:
|
||
|
|
name: poimen-orchestrator
|
||
|
|
spec:
|
||
|
|
replicas: %d
|
||
|
|
selector:
|
||
|
|
matchLabels:
|
||
|
|
app: poimen-orchestrator
|
||
|
|
template:
|
||
|
|
metadata:
|
||
|
|
labels:
|
||
|
|
app: poimen-orchestrator
|
||
|
|
spec:
|
||
|
|
containers:
|
||
|
|
- name: orchestrator
|
||
|
|
image: %s
|
||
|
|
ports:
|
||
|
|
- containerPort: 7233
|
||
|
|
- containerPort: 8081
|
||
|
|
`, replicas, deployment.Container)
|
||
|
|
|
||
|
|
deployment.Manifest = manifest
|
||
|
|
|
||
|
|
return manifest, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Deploy applies the deployment to K8s
|
||
|
|
func (sd *SelfDeployer) Deploy(deploymentID string) error {
|
||
|
|
sd.mu.Lock()
|
||
|
|
defer sd.mu.Unlock()
|
||
|
|
|
||
|
|
deployment, exists := sd.deployments[deploymentID]
|
||
|
|
if !exists {
|
||
|
|
return fmt.Errorf("deployment not found: %s", deploymentID)
|
||
|
|
}
|
||
|
|
|
||
|
|
if deployment.Status != StatusApplying {
|
||
|
|
return fmt.Errorf("invalid status for deploy: %s", deployment.Status)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Simulate deployment
|
||
|
|
deployment.Status = StatusSuccess
|
||
|
|
deployment.CompletedAt = time.Now()
|
||
|
|
|
||
|
|
// Update current version
|
||
|
|
sd.currentVersion = deployment.Version
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Rollback rolls back to previous version
|
||
|
|
func (sd *SelfDeployer) Rollback(previousVersion string) error {
|
||
|
|
sd.mu.Lock()
|
||
|
|
defer sd.mu.Unlock()
|
||
|
|
|
||
|
|
// Create a new deployment for rollback
|
||
|
|
deploymentID := fmt.Sprintf("rollback-%s-%d", previousVersion, len(sd.deployments))
|
||
|
|
|
||
|
|
deployment := &DeploymentInfo{
|
||
|
|
ID: deploymentID,
|
||
|
|
Version: previousVersion,
|
||
|
|
Status: StatusSuccess,
|
||
|
|
StartedAt: time.Now(),
|
||
|
|
CompletedAt: time.Now(),
|
||
|
|
Container: fmt.Sprintf("%s/orchestrator:%s", sd.registry, previousVersion),
|
||
|
|
}
|
||
|
|
|
||
|
|
sd.deployments[deploymentID] = deployment
|
||
|
|
sd.currentVersion = previousVersion
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetDeploymentInfo retrieves deployment info
|
||
|
|
func (sd *SelfDeployer) GetDeploymentInfo(deploymentID string) (*DeploymentInfo, bool) {
|
||
|
|
sd.mu.RLock()
|
||
|
|
defer sd.mu.RUnlock()
|
||
|
|
|
||
|
|
deployment, exists := sd.deployments[deploymentID]
|
||
|
|
return deployment, exists
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetCurrentVersion returns the current orchestrator version
|
||
|
|
func (sd *SelfDeployer) GetCurrentVersion() string {
|
||
|
|
sd.mu.RLock()
|
||
|
|
defer sd.mu.RUnlock()
|
||
|
|
|
||
|
|
return sd.currentVersion
|
||
|
|
}
|
||
|
|
|
||
|
|
// ListDeployments returns all deployments
|
||
|
|
func (sd *SelfDeployer) ListDeployments() map[string]*DeploymentInfo {
|
||
|
|
sd.mu.RLock()
|
||
|
|
defer sd.mu.RUnlock()
|
||
|
|
|
||
|
|
result := make(map[string]*DeploymentInfo)
|
||
|
|
for id, deployment := range sd.deployments {
|
||
|
|
result[id] = deployment
|
||
|
|
}
|
||
|
|
|
||
|
|
return result
|
||
|
|
}
|
||
|
|
|
||
|
|
// HealthCheck checks if the deployed orchestrator is healthy
|
||
|
|
func (sd *SelfDeployer) HealthCheck(deploymentID string) (bool, error) {
|
||
|
|
sd.mu.RLock()
|
||
|
|
defer sd.mu.RUnlock()
|
||
|
|
|
||
|
|
deployment, exists := sd.deployments[deploymentID]
|
||
|
|
if !exists {
|
||
|
|
return false, fmt.Errorf("deployment not found: %s", deploymentID)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Simulate health check
|
||
|
|
return deployment.Status == StatusSuccess, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// SetVersion sets the target version
|
||
|
|
func (sd *SelfDeployer) SetVersion(version string) {
|
||
|
|
sd.mu.Lock()
|
||
|
|
defer sd.mu.Unlock()
|
||
|
|
|
||
|
|
sd.currentVersion = version
|
||
|
|
}
|