92 lines
3.3 KiB
Go
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
|
|
})
|
|
}
|