mirror of https://github.com/jetkvm/kvm.git
184 lines
3.8 KiB
Go
184 lines
3.8 KiB
Go
package usbgadget
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/rs/zerolog"
|
|
"github.com/sourcegraph/tf-dag/dag"
|
|
)
|
|
|
|
type ChangeSetResolver struct {
|
|
changeset *ChangeSet
|
|
|
|
l *zerolog.Logger
|
|
g *dag.AcyclicGraph
|
|
|
|
changesMap map[string]*FileChange
|
|
conditionalChangesMap map[string]*FileChange
|
|
|
|
orderedChanges []dag.Vertex
|
|
resolvedChanges []*FileChange
|
|
additionalResolveRequired bool
|
|
}
|
|
|
|
func (c *ChangeSetResolver) toOrderedChanges() error {
|
|
for key, change := range c.changesMap {
|
|
v := c.g.Add(key)
|
|
|
|
for _, dependsOn := range change.DependsOn {
|
|
c.g.Connect(dag.BasicEdge(dependsOn, v))
|
|
}
|
|
for _, dependsOn := range change.resolvedDeps {
|
|
c.g.Connect(dag.BasicEdge(dependsOn, v))
|
|
}
|
|
}
|
|
|
|
cycles := c.g.Cycles()
|
|
if len(cycles) > 0 {
|
|
return fmt.Errorf("cycles detected: %v", cycles)
|
|
}
|
|
|
|
orderedChanges := c.g.TopologicalOrder()
|
|
c.orderedChanges = orderedChanges
|
|
return nil
|
|
}
|
|
|
|
func (c *ChangeSetResolver) doResolveChanges(initial bool) error {
|
|
resolvedChanges := make([]*FileChange, 0)
|
|
|
|
for _, key := range c.orderedChanges {
|
|
change := c.changesMap[key.(string)]
|
|
if !initial {
|
|
change.ResetActionResolution()
|
|
}
|
|
|
|
resolvedAction := change.Action()
|
|
|
|
resolvedChanges = append(resolvedChanges, change)
|
|
// no need to check the triggers if there's no change
|
|
if resolvedAction == FileChangeResolvedActionDoNothing {
|
|
continue
|
|
}
|
|
|
|
if !initial {
|
|
continue
|
|
}
|
|
|
|
if change.BeforeChange != nil {
|
|
change.resolvedDeps = append(change.resolvedDeps, change.BeforeChange...)
|
|
c.additionalResolveRequired = true
|
|
|
|
// add the dependencies to the changes map
|
|
for _, dep := range change.BeforeChange {
|
|
depChange, ok := c.conditionalChangesMap[dep]
|
|
if !ok {
|
|
return fmt.Errorf("dependency %s not found", dep)
|
|
}
|
|
|
|
c.changesMap[dep] = depChange
|
|
}
|
|
}
|
|
}
|
|
|
|
c.resolvedChanges = resolvedChanges
|
|
return nil
|
|
}
|
|
|
|
func (c *ChangeSetResolver) resolveChanges(initial bool) error {
|
|
// get the ordered changes
|
|
err := c.toOrderedChanges()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// resolve the changes
|
|
err = c.doResolveChanges(initial)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, change := range c.resolvedChanges {
|
|
c.l.Trace().Str("change", change.String()).Msg("resolved change")
|
|
}
|
|
|
|
if !c.additionalResolveRequired || !initial {
|
|
return nil
|
|
}
|
|
|
|
return c.resolveChanges(false)
|
|
}
|
|
|
|
func (c *ChangeSetResolver) applyChanges() error {
|
|
for _, change := range c.resolvedChanges {
|
|
change.ResetActionResolution()
|
|
action := change.Action()
|
|
actionStr := FileChangeResolvedActionString[action]
|
|
|
|
l := c.l.Info()
|
|
if action == FileChangeResolvedActionDoNothing {
|
|
l = c.l.Trace()
|
|
}
|
|
|
|
l.Str("action", actionStr).Str("change", change.String()).Msg("applying change")
|
|
|
|
err := c.changeset.applyChange(change)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *ChangeSetResolver) GetChanges() ([]*FileChange, error) {
|
|
localChanges := c.changeset.Changes
|
|
changesMap := make(map[string]*FileChange)
|
|
conditionalChangesMap := make(map[string]*FileChange)
|
|
|
|
// build the map of the changes
|
|
for _, change := range localChanges {
|
|
key := change.Key
|
|
if key == "" {
|
|
key = change.Path
|
|
}
|
|
|
|
// remove it from the map first
|
|
if change.When != "" {
|
|
conditionalChangesMap[key] = &change
|
|
continue
|
|
}
|
|
|
|
if _, ok := changesMap[key]; ok {
|
|
if changesMap[key].IsSame(&change.RequestedFileChange) {
|
|
continue
|
|
}
|
|
return nil, fmt.Errorf(
|
|
"duplicate change: %s, current: %s, requested: %s",
|
|
key,
|
|
changesMap[key].String(),
|
|
change.String(),
|
|
)
|
|
}
|
|
|
|
changesMap[key] = &change
|
|
}
|
|
|
|
c.changesMap = changesMap
|
|
c.conditionalChangesMap = conditionalChangesMap
|
|
|
|
err := c.resolveChanges(true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c.resolvedChanges, nil
|
|
}
|
|
|
|
func (c *ChangeSetResolver) Apply() error {
|
|
if _, err := c.GetChanges(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.applyChanges()
|
|
}
|