store: Add blueprint change order tracking
Previously the order that changes were made to blueprints was not being saved. I worked around this by sorting by timestamp, but it only has 1s resolution so it is very likely to end up with changes having the same timestamp, especially when running tests. This adds a new variable to the Store, it is a list of the commit hashes for each blueprint, in the order they were made. Since this is a change to the Store schema the first time the new code is run with the old store state it needs to populate the commit list, as best it can, with the existing data. To do that it sorts the changes for each blueprint by timestamp and version and saves this ordering into the new BlueprintsCommits list.
This commit is contained in:
parent
eeef1e289c
commit
aed28ccf72
2 changed files with 75 additions and 25 deletions
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/osbuild/osbuild-composer/internal/rpmmd"
|
||||
"github.com/osbuild/osbuild-composer/internal/target"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
|
|
@ -40,6 +41,7 @@ type Store struct {
|
|||
Composes map[uuid.UUID]compose.Compose `json:"composes"`
|
||||
Sources map[string]SourceConfig `json:"sources"`
|
||||
BlueprintsChanges map[string]map[string]blueprint.Change `json:"changes"`
|
||||
BlueprintsCommits map[string][]string `json:"commits"`
|
||||
|
||||
mu sync.RWMutex // protects all fields
|
||||
pendingJobs chan Job
|
||||
|
|
@ -193,6 +195,49 @@ func New(stateDir *string, distroArg distro.Distro, distroRegistryArg distro.Reg
|
|||
if s.BlueprintsChanges == nil {
|
||||
s.BlueprintsChanges = make(map[string]map[string]blueprint.Change)
|
||||
}
|
||||
if s.BlueprintsCommits == nil {
|
||||
s.BlueprintsCommits = make(map[string][]string)
|
||||
}
|
||||
|
||||
// Populate BlueprintsCommits for existing blueprints without commit history
|
||||
// BlueprintsCommits tracks the order of the commits in BlueprintsChanges,
|
||||
// but may not be in-sync with BlueprintsChanges because it was added later.
|
||||
// This will sort the existing commits by timestamp and version to update
|
||||
// the store. BUT since the timestamp resolution is only 1s it is possible
|
||||
// that the order may be slightly wrong.
|
||||
for name := range s.BlueprintsChanges {
|
||||
if len(s.BlueprintsChanges[name]) != len(s.BlueprintsCommits[name]) {
|
||||
changes := make([]blueprint.Change, 0, len(s.BlueprintsChanges[name]))
|
||||
|
||||
for commit := range s.BlueprintsChanges[name] {
|
||||
changes = append(changes, s.BlueprintsChanges[name][commit])
|
||||
}
|
||||
|
||||
// Sort the changes by Timestamp then version, ascending
|
||||
sort.Slice(changes, func(i, j int) bool {
|
||||
if changes[i].Timestamp == changes[j].Timestamp {
|
||||
vI, err := semver.NewVersion(changes[i].Blueprint.Version)
|
||||
if err != nil {
|
||||
vI = semver.New("0.0.0")
|
||||
}
|
||||
vJ, err := semver.NewVersion(changes[j].Blueprint.Version)
|
||||
if err != nil {
|
||||
vJ = semver.New("0.0.0")
|
||||
}
|
||||
|
||||
return vI.LessThan(*vJ)
|
||||
}
|
||||
return changes[i].Timestamp < changes[j].Timestamp
|
||||
})
|
||||
|
||||
commits := make([]string, 0, len(changes))
|
||||
for _, c := range changes {
|
||||
commits = append(commits, c.Commit)
|
||||
}
|
||||
|
||||
s.BlueprintsCommits[name] = commits
|
||||
}
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
|
@ -320,14 +365,15 @@ func (s *Store) GetBlueprintChange(name string, commit string) *blueprint.Change
|
|||
return &change
|
||||
}
|
||||
|
||||
// GetBlueprintChanges returns the list of changes, oldest first
|
||||
func (s *Store) GetBlueprintChanges(name string) []blueprint.Change {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
var changes []blueprint.Change
|
||||
|
||||
for _, change := range s.BlueprintsChanges[name] {
|
||||
changes = append(changes, change)
|
||||
for _, commit := range s.BlueprintsCommits[name] {
|
||||
changes = append(changes, s.BlueprintsChanges[name][commit])
|
||||
}
|
||||
|
||||
return changes
|
||||
|
|
@ -359,6 +405,8 @@ func (s *Store) PushBlueprint(bp blueprint.Blueprint, commitMsg string) error {
|
|||
s.BlueprintsChanges[bp.Name] = make(map[string]blueprint.Change)
|
||||
}
|
||||
s.BlueprintsChanges[bp.Name][commit] = change
|
||||
// Keep track of the order of the commits
|
||||
s.BlueprintsCommits[bp.Name] = append(s.BlueprintsCommits[bp.Name], commit)
|
||||
|
||||
if old, ok := s.Blueprints[bp.Name]; ok {
|
||||
if bp.Version == "" || bp.Version == old.Version {
|
||||
|
|
@ -409,31 +457,32 @@ func (s *Store) TagBlueprint(name string) error {
|
|||
return errors.New("Unknown blueprint")
|
||||
}
|
||||
|
||||
// Get the latest revision for this blueprint, and the most recent commit
|
||||
var revision int
|
||||
var timestamp string
|
||||
var commit blueprint.Change
|
||||
for _, c := range s.BlueprintsChanges[name] {
|
||||
if c.Revision != nil && *c.Revision > revision {
|
||||
revision = *c.Revision
|
||||
}
|
||||
if c.Timestamp > timestamp {
|
||||
timestamp = c.Timestamp
|
||||
commit = c
|
||||
}
|
||||
}
|
||||
if len(timestamp) == 0 {
|
||||
if len(s.BlueprintsCommits[name]) == 0 {
|
||||
return errors.New("No commits for blueprint")
|
||||
}
|
||||
|
||||
// The most recent commit already has a revision, don't bump it
|
||||
if commit.Revision != nil {
|
||||
latest := s.BlueprintsCommits[name][len(s.BlueprintsCommits[name])-1]
|
||||
// If the most recent commit already has a revision, don't bump it
|
||||
if s.BlueprintsChanges[name][latest].Revision != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the latest revision for this blueprint
|
||||
var revision int
|
||||
var change blueprint.Change
|
||||
for i := len(s.BlueprintsCommits[name]) - 1; i >= 0; i-- {
|
||||
commit := s.BlueprintsCommits[name][i]
|
||||
change = s.BlueprintsChanges[name][commit]
|
||||
if change.Revision != nil && *change.Revision > revision {
|
||||
revision = *change.Revision
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Bump the revision (if there was none it will start at 1)
|
||||
revision++
|
||||
commit.Revision = &revision
|
||||
s.BlueprintsChanges[name][commit.Commit] = commit
|
||||
change.Revision = &revision
|
||||
s.BlueprintsChanges[name][latest] = change
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1117,13 +1117,14 @@ func (api *API) blueprintsChangesHandler(writer http.ResponseWriter, request *ht
|
|||
name = name[1:]
|
||||
}
|
||||
bpChanges := api.store.GetBlueprintChanges(name)
|
||||
// Reverse the changes, newest first
|
||||
reversed := make([]blueprint.Change, 0, len(bpChanges))
|
||||
for i := len(bpChanges) - 1; i >= 0; i-- {
|
||||
reversed = append(reversed, bpChanges[i])
|
||||
}
|
||||
if bpChanges != nil {
|
||||
// Sort the changes by Timestamp, descending
|
||||
sort.Slice(bpChanges, func(i, j int) bool {
|
||||
return bpChanges[i].Timestamp > bpChanges[j].Timestamp
|
||||
})
|
||||
change := change{
|
||||
Changes: bpChanges,
|
||||
Changes: reversed,
|
||||
Name: name,
|
||||
Total: len(bpChanges),
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue