summaryrefslogtreecommitdiff
path: root/internal/resource/repository.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/resource/repository.go')
-rw-r--r--internal/resource/repository.go63
1 files changed, 58 insertions, 5 deletions
diff --git a/internal/resource/repository.go b/internal/resource/repository.go
index 8cb88b3..69617d4 100644
--- a/internal/resource/repository.go
+++ b/internal/resource/repository.go
@@ -11,11 +11,11 @@ var (
once sync.Once
)
-func getRepository() repository {
+func getRepository() *repository {
once.Do(func() {
repo = newRepository()
})
- return repo
+ return &repo
}
func resetRepository() {
@@ -24,17 +24,16 @@ func resetRepository() {
type repository struct {
registered map[string]Resource
- mu *sync.Mutex
+ mu sync.Mutex
}
func newRepository() repository {
return repository{
registered: make(map[string]Resource),
- mu: new(sync.Mutex),
}
}
-func (r repository) register(res Resource) error {
+func (r *repository) register(res Resource) error {
r.mu.Lock()
defer r.mu.Unlock()
@@ -47,3 +46,57 @@ func (r repository) register(res Resource) error {
return nil
}
+
+func (r *repository) apply() error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ visited := make(map[string]bool)
+ visiting := make(map[string]bool)
+ var order []Resource
+
+ var visit func(id string) error
+ visit = func(id string) error {
+ if visiting[id] {
+ return fmt.Errorf("circular dependency detected involving %s", id)
+ }
+ if visited[id] {
+ return nil
+ }
+
+ res, ok := r.registered[id]
+ if !ok {
+ return fmt.Errorf("resource %s is depended upon but not registered", id)
+ }
+
+ visiting[id] = true
+ for depID := range res.dependsOn {
+ if err := visit(depID); err != nil {
+ return err
+ }
+ }
+ delete(visiting, id)
+ visited[id] = true
+ order = append(order, res)
+ return nil
+ }
+
+ for id := range r.registered {
+ if err := visit(id); err != nil {
+ return err
+ }
+ }
+
+ for _, res := range order {
+ log.Printf("Applying resource %v", res)
+ if err := res.Apply.Apply(); err != nil {
+ return fmt.Errorf("failed to apply %v: %w", res, err)
+ }
+ }
+
+ return nil
+}
+
+func Apply() error {
+ return getRepository().apply()
+}