package buildstep import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "strings" "time" ) // The states `pauses` of the build seats, whose three seats hold the same layouts (the part `build-step`): a node's // pause under `paused.`, its capacity under `capacity.`. // PausesState is the name of the state. const PausesState = "pauses" // PauseKey is the key of a node's pause. func PauseKey(node string) (string, error) { if err := checkNode(node); err != nil { return "", err } return "paused." + node, nil } // CapacityKey is the key of a node's capacity. func CapacityKey(node string) (string, error) { if err := checkNode(node); err != nil { return "", err } return "capacity." + node, nil } // Pause is whether a node takes no new ask, and who said so. type Pause struct { V int `json:"v"` Paused bool `json:"paused"` By string `json:"by,omitempty"` At time.Time `json:"at,omitzero"` } // EncodePause is p as a pauses value. func EncodePause(p Pause) ([]byte, error) { p.V = Version p.At = p.At.UTC() return json.Marshal(p) } // DecodePause reads a pause, refusing another version. func DecodePause(data []byte) (Pause, error) { var p Pause if err := decodeVersioned(data, &p, func() int { return p.V }); err != nil { return Pause{}, err } return p, nil } // Capacity is how many steps a node runs at once, and why. type Capacity struct { V int `json:"v"` Effective int `json:"effective"` Configured int `json:"configured"` ConfiguredFrom string `json:"configured-from,omitempty"` QuotaCores float64 `json:"quota-cores,omitempty"` QuotaFrom string `json:"quota-from"` CoresPerStep float64 `json:"cores-per-step"` ByCPU int `json:"by-cpu,omitempty"` BuildHours string `json:"build-hours,omitempty"` OutsideHours bool `json:"outside-hours,omitempty"` BuildHoursFault string `json:"build-hours-fault,omitempty"` Ineligible string `json:"ineligible,omitempty"` Why string `json:"why"` At time.Time `json:"at,omitzero"` } // EncodeCapacity is c as a pauses value. func EncodeCapacity(c Capacity) ([]byte, error) { c.V = Version c.At = c.At.UTC() return json.Marshal(c) } // DecodeCapacity reads a capacity, refusing another version. func DecodeCapacity(data []byte) (Capacity, error) { var c Capacity if err := decodeVersioned(data, &c, func() int { return c.V }); err != nil { return Capacity{}, err } return c, nil } func decodeVersioned(data []byte, into any, version func() int) error { if err := json.Unmarshal(data, into); err != nil { return fmt.Errorf("buildstep: value does not fit its shape: %w", err) } if v := version(); v != Version { return fmt.Errorf("%w: a value of version %d, this reader reads %d", ErrUnknownVersion, v, Version) } return nil } // ComposerKey is the key a composer build is kept under: the fetched commit, the directory, the package and the // toolchain it ran with. func ComposerKey(repository, commit, dir, pkg string, toolchain ToolchainUsed) string { sum := sha256.Sum256([]byte(strings.Join([]string{repository, commit, dir, pkg, toolchain.Version, toolchain.Recipe}, "\n"))) return "composer-" + hex.EncodeToString(sum[:8]) }