|
|
@@ -0,0 +1,68 @@
|
|
|
+package preview
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+
|
|
|
+ "github.com/porter-dev/switchboard/pkg/types"
|
|
|
+)
|
|
|
+
|
|
|
+type dependencyResolver struct {
|
|
|
+ resources []*types.Resource
|
|
|
+ graph map[string][]string
|
|
|
+ resolved map[string]bool
|
|
|
+ unresolved map[string]bool
|
|
|
+}
|
|
|
+
|
|
|
+func newDependencyResolver(resources []*types.Resource) *dependencyResolver {
|
|
|
+ return &dependencyResolver{
|
|
|
+ resources: resources,
|
|
|
+ graph: make(map[string][]string),
|
|
|
+ resolved: make(map[string]bool),
|
|
|
+ unresolved: make(map[string]bool),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (r *dependencyResolver) Resolve() error {
|
|
|
+ // construct dependency graph
|
|
|
+ for _, resource := range r.resources {
|
|
|
+ // check for duplicate resource
|
|
|
+ if _, ok := r.graph[resource.Name]; ok {
|
|
|
+ return fmt.Errorf("duplicate resource detected: '%s'", resource.Name)
|
|
|
+ }
|
|
|
+
|
|
|
+ r.graph[resource.Name] = append(r.graph[resource.Name], resource.DependsOn...)
|
|
|
+ }
|
|
|
+
|
|
|
+ err := r.depResolve(r.resources[0].Name)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (r *dependencyResolver) depResolve(name string) error {
|
|
|
+ r.unresolved[name] = true
|
|
|
+
|
|
|
+ for _, dep := range r.graph[name] {
|
|
|
+ if _, ok := r.graph[dep]; !ok {
|
|
|
+ return fmt.Errorf("no such resource as: '%s'", dep)
|
|
|
+ }
|
|
|
+
|
|
|
+ if _, ok := r.resolved[dep]; !ok {
|
|
|
+ if _, ok = r.unresolved[dep]; ok {
|
|
|
+ return fmt.Errorf("circular depedency detected: '%s' -> '%s'", name, dep)
|
|
|
+ }
|
|
|
+ err := r.depResolve(dep)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ r.resolved[name] = true
|
|
|
+ delete(r.unresolved, name)
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|