Files
DoGaMa-serv/internal/instance/configuration.go
T

92 lines
3.3 KiB
Go

package instance
import (
"context"
"errors"
"strings"
)
type ConfigurationRepository interface {
GetGlobalLabels(context.Context) (map[string]string, error)
SetGlobalLabels(context.Context, map[string]string, bool) (affected int, running int, err error)
SaveInstanceConfiguration(context.Context, string, Preview, bool) error
ClearContainerConfigPending(context.Context, string, string) error
}
type ConfigurationStatus struct {
InstanceID string `json:"instance_id"`
ContainerConfigPending bool `json:"container_config_pending"`
Preview Preview `json:"configuration"`
}
func (s *LifecycleService) Configure(ctx context.Context, instanceID, labels string, tag ImageTag, immediate bool) (OperationResult, error) {
repository, ok := s.repository.(ConfigurationRepository)
if !ok {
return OperationResult{}, errors.New("container configuration is unavailable")
}
return s.exclusive(instanceID, func() (OperationResult, error) {
current, err := s.repository.GetInstance(ctx, instanceID)
if err != nil {
return OperationResult{}, err
}
parsed, err := ParseLabels(labels)
if err != nil {
return OperationResult{}, err
}
defaultTag := current.Preview.TemplateDefaultTag
if defaultTag == "" {
parts := strings.Split(current.Preview.Image, ":")
defaultTag = parts[len(parts)-1]
}
validated, err := ValidateImageTag(tag, defaultTag)
if err != nil {
return OperationResult{}, err
}
preview := current.Preview
preview.CustomLabels, preview.ImageTag = parsed, validated
base := preview.Image[:strings.LastIndex(preview.Image, ":")]
preview.Image = base + ":" + validated.Tag
if err := repository.SaveInstanceConfiguration(ctx, instanceID, preview, !immediate); err != nil {
return OperationResult{}, err
}
if !immediate || current.ContainerID == "" {
updated, _ := s.repository.GetInstance(ctx, instanceID)
return resultFrom(updated, ""), nil
}
agent, ok := s.agent.(replacementAgent)
if !ok {
return OperationResult{}, errors.New("container replacement is unavailable")
}
operationID, err := operationToken()
if err != nil {
return OperationResult{}, err
}
current, err = s.repository.BeginOperation(ctx, operationID, instanceID, "restart", "update")
if err != nil {
return OperationResult{}, err
}
plan, err := preview.DeploymentPlan(instanceID)
if err != nil {
return s.fail(ctx, operationID, instanceID, "invalid_plan", err)
}
state, err := agent.ReplaceInstance(ctx, plan)
if err != nil {
return s.fail(ctx, operationID, instanceID, "agent_replace_failed", err)
}
if current.DesiredRunning {
state, err = s.agent.StartInstance(ctx, instanceID)
if err != nil {
return s.fail(ctx, operationID, instanceID, "agent_start_failed", err)
}
}
lifecycle, observed := stateToLifecycle(state)
if err := s.repository.FinishOperation(ctx, operationID, lifecycle, observed, state.ContainerID, plan.PlanDigest, current.DesiredRunning, ""); err != nil {
return OperationResult{}, err
}
if err := repository.ClearContainerConfigPending(ctx, instanceID, plan.PlanDigest); err != nil {
return OperationResult{}, err
}
return OperationResult{OperationID: operationID, InstanceID: instanceID, State: lifecycle, Observed: observed, ContainerID: state.ContainerID, AgentState: state}, nil
})
}