package inventory import ( "context" "errors" "fmt" "time" "github.com/jackc/pgx/v5" "github.com/novox/mesh-controller/internal/catalogue" ) // The list of predefined keys the mesh publishes (novox/hq ADR 0348 §7, design 57 package K2): the newest trunk // build's of the catalogue, by the generation of the commit built. // PredefinedRecord is the list kept, and the build it came from. type PredefinedRecord struct { Keys catalogue.PredefinedList Repository string Commit string Generation int Build string KeptAt time.Time } // KeepPredefined keeps a trunk build's list when its commit is newer than the one kept — a larger generation — // and answers whether it did. An older build's list, or one of the same generation, heard later is not kept: the // newer one stands, whatever order the outcomes arrive in. One statement, so two builds heard at once cannot both // win. func (i *Inventory) KeepPredefined(ctx context.Context, r PredefinedRecord) (bool, error) { if r.Keys == nil { return false, errors.New("no list to keep") } if r.Generation <= 0 { return false, fmt.Errorf("the build %s did not say its commit's generation, so whether its list is newer "+ "than the one kept cannot be said", r.Build) } if problems := catalogue.PredefinedProblems(r.Keys); len(problems) > 0 { return false, fmt.Errorf("the list of %s is refused: %v", r.Build, problems) } text, err := r.Keys.Canonical() if err != nil { return false, err } tag, err := i.store.Pool().Exec(ctx, ` insert into predefined_list (keys, repository, commit, generation, build) values ($1, $2, $3, $4, $5) on conflict (one) do update set keys = excluded.keys, repository = excluded.repository, commit = excluded.commit, generation = excluded.generation, build = excluded.build, kept_at = now() where predefined_list.generation < excluded.generation`, text, r.Repository, r.Commit, r.Generation, r.Build) if err != nil { return false, err } return tag.RowsAffected() == 1, nil } // PredefinedKept is the list kept, and false when none is yet. func (i *Inventory) PredefinedKept(ctx context.Context) (PredefinedRecord, bool, error) { var r PredefinedRecord var text []byte err := i.store.Pool().QueryRow(ctx, `select keys, repository, commit, generation, build, kept_at from predefined_list`). Scan(&text, &r.Repository, &r.Commit, &r.Generation, &r.Build, &r.KeptAt) if errors.Is(err, pgx.ErrNoRows) { return PredefinedRecord{}, false, nil } if err != nil { return PredefinedRecord{}, false, err } if r.Keys, err = catalogue.ParsePredefined(text); err != nil { return PredefinedRecord{}, false, fmt.Errorf("the list kept from %s cannot be read: %w", r.Build, err) } return r, true, nil }