Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"os"
"unicode/utf8"

"github.com/google/uuid"
"github.com/h2non/filetype"

"github.com/cloudbase/garm-provider-common/cloudconfig"
Expand All @@ -32,6 +33,20 @@ import (
"github.com/cloudbase/garm/runner/common"
)

// scaleSetPoolIDNamespace is the UUIDv5 namespace used to derive pseudo pool
// IDs for scale sets. Instances in providers are tagged with IDs derived from
// this namespace; changing it (or the derivation input format) orphans every
// instance created with the previous value.
var scaleSetPoolIDNamespace = uuid.MustParse("3f5bfba0-d82c-4f91-a5f6-ddc1c9209b01")

// ScaleSetPseudoPoolID derives a stable, UUID-shaped pseudo pool ID for a
// scale set, from the forge entity ID and the internal scale set database ID.
// Both inputs are immutable, so the result never changes for the lifetime of
// the scale set and always fits within provider tag/label length limits.
func ScaleSetPseudoPoolID(entityID string, scaleSetID uint) string {
return uuid.NewSHA1(scaleSetPoolIDNamespace, fmt.Appendf(nil, "%s-%d", entityID, scaleSetID)).String()
}

func FetchTools(ctx context.Context, cli common.GithubClient) ([]commonParams.RunnerApplicationDownload, error) {
tools, ghResp, err := cli.ListEntityRunnerApplicationDownloads(ctx)
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions workers/provider/instance_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ func (i *instanceManager) getEntity() (params.ForgeEntity, error) {
}

func (i *instanceManager) pseudoPoolID() string {
// This is temporary. We need to extend providers to know about scale sets.
return fmt.Sprintf("%s-%s", i.scaleSet.Name, i.scaleSetEntity.ID)
// nolint:golangci-lint,godox
// TODO(gabriel-samfira): extend providers to know about scale sets.
return garmUtil.ScaleSetPseudoPoolID(i.scaleSetEntity.ID, i.scaleSet.ID)
}

func (i *instanceManager) handleCreateInstanceInProvider(instance params.Instance) error {
Expand Down
48 changes: 41 additions & 7 deletions workers/scaleset/scaleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func NewWorker(ctx context.Context, store dbCommon.Store, scaleSet params.ScaleS
scaleSet: scaleSet,
entity: entity,
runners: make(map[string]params.Instance),
pseudoPoolID: garmUtil.ScaleSetPseudoPoolID(scalesetEntity.ID, scaleSet.ID),
}, nil
}

Expand All @@ -76,6 +77,14 @@ type Worker struct {
entity params.ForgeEntity
runners map[string]params.Instance

// pseudoPoolID is the stable pool ID reported to providers for this scale
// set. It is derived from immutable IDs, so it is computed once.
pseudoPoolID string
// legacyPoolIDDrained indicates that the provider no longer has any
// instances tagged with the legacy name-based pseudo pool ID, so we can
// stop querying for it.
legacyPoolIDDrained bool

consumer dbCommon.Consumer

listener *scaleSetListener
Expand Down Expand Up @@ -551,10 +560,6 @@ func (w *Worker) consolidateRunnerState(runners []params.RunnerReference) error
// DB runners with no provider instance are marked as pending_delete. Must be
// called with w.mux held.
func (w *Worker) consolidateProviderState() error {
pseudoPoolID, err := w.pseudoPoolID()
if err != nil {
return fmt.Errorf("getting pseudo pool ID: %w", err)
}
listParams := common.ListInstancesParams{
ListInstancesV011: common.ListInstancesV011Params{
ProviderBaseParams: common.ProviderBaseParams{
Expand All @@ -563,7 +568,7 @@ func (w *Worker) consolidateProviderState() error {
},
}

providerRunners, err := w.provider.ListInstances(w.ctx, pseudoPoolID, listParams)
providerRunners, err := w.provider.ListInstances(w.ctx, w.pseudoPoolID, listParams)
if err != nil {
return fmt.Errorf("listing instances: %w", err)
}
Expand All @@ -573,6 +578,27 @@ func (w *Worker) consolidateProviderState() error {
providerRunnersByName[runner.Name] = runner
}

if !w.legacyPoolIDDrained {
legacyID, err := w.legacyPseudoPoolID()
if err != nil {
return fmt.Errorf("getting legacy pseudo pool ID: %w", err)
}
legacyRunners, err := w.provider.ListInstances(w.ctx, legacyID, listParams)
if err != nil {
return fmt.Errorf("listing instances by legacy pseudo pool ID: %w", err)
}
if len(legacyRunners) == 0 {
w.legacyPoolIDDrained = true
}
for _, runner := range legacyRunners {
if _, ok := providerRunnersByName[runner.Name]; ok {
continue
}
providerRunnersByName[runner.Name] = runner
providerRunners = append(providerRunners, runner)
}
}

deleteInstanceParams := common.DeleteInstanceParams{
DeleteInstanceV011: common.DeleteInstanceV011Params{
ProviderBaseParams: common.ProviderBaseParams{
Expand Down Expand Up @@ -637,8 +663,16 @@ func (w *Worker) consolidateProviderState() error {
return nil
}

func (w *Worker) pseudoPoolID() (string, error) {
// This is temporary. We need to extend providers to know about scale sets.
// legacyPseudoPoolID returns the deprecated name-based pseudo pool ID. Older
// GARM versions tagged provider instances with this ID, so we keep listing by
// it until no instances tagged with it remain. It must be recomputed on every
// call, because the scale set name can change (which is bad and have failed to
// consider this until now).
//
// Deprecated: This function is "born" deprecated because it transitions from
// the old way to tag instances, to the new stable uuid V5 computed from the
// entity ID and the internal scaleset ID.
func (w *Worker) legacyPseudoPoolID() (string, error) {
entity, err := w.scaleSet.GetEntity()
if err != nil {
return "", fmt.Errorf("getting entity: %w", err)
Expand Down