package updateprotocol import ( "fmt" "regexp" "strconv" "strings" "time" ) var releaseVersionPattern = regexp.MustCompile(`^v(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)$`) const ( PhaseIdle = "idle" PhaseQueued = "queued" PhasePulling = "pulling" PhaseRestarting = "restarting" PhaseVerifying = "verifying" PhaseComplete = "complete" PhaseFailed = "failed" ) type Request struct { Version string `json:"version"` CurrentVersion string `json:"current_version"` RegistryUsername string `json:"registry_username"` // RegistryToken keeps the v2 wire name for compatibility and carries either // a registry password or an API token. RegistryToken string `json:"registry_token"` } type Status struct { // Optional capability; older v2 agents require registry credentials. AnonymousPull bool `json:"anonymous_pull,omitempty"` Phase string `json:"phase"` TargetVersion string `json:"target_version,omitempty"` Message string `json:"message,omitempty"` StartedAt time.Time `json:"started_at,omitempty"` UpdatedAt time.Time `json:"updated_at,omitempty"` AgentVersion string `json:"agent_version,omitempty"` Protocol int `json:"protocol_version,omitempty"` } func (s Status) Busy() bool { return s.Phase == PhaseQueued || s.Phase == PhasePulling || s.Phase == PhaseRestarting || s.Phase == PhaseVerifying } func ValidVersion(version string) bool { return len(version) <= 64 && releaseVersionPattern.MatchString(version) } // CompareVersions compares strict stable vMAJOR.MINOR.PATCH versions. It does // not require callers to install intermediate releases. func CompareVersions(left, right string) (int, error) { a, err := parseVersion(left) if err != nil { return 0, err } b, err := parseVersion(right) if err != nil { return 0, err } for index := range a { if a[index] < b[index] { return -1, nil } if a[index] > b[index] { return 1, nil } } return 0, nil } func parseVersion(raw string) ([3]int, error) { if !ValidVersion(raw) { return [3]int{}, fmt.Errorf("version must use vMAJOR.MINOR.PATCH") } parts := strings.Split(strings.TrimPrefix(raw, "v"), ".") var values [3]int for index, part := range parts { value, err := strconv.Atoi(part) if err != nil || value < 0 || value > 1_000_000 { return [3]int{}, fmt.Errorf("version contains an invalid number") } values[index] = value } return values, nil }