// hasFailedDependencies checks if a resource has failed dependencies. func (c *Catalog) hasFailedDependencies(r resource.Resource) error { c.status.Lock() defer c.status.Unlock() for _, dep := range r.Dependencies() { item := c.status.Items[dep] if item.Err != nil { return fmt.Errorf("failed dependency for %s", dep) } } return nil }
// processResource processes a single resource func (c *Catalog) processResource(r resource.Resource) error { if err := r.Validate(); err != nil { return err } state, err := r.Evaluate() if err != nil { return err } if c.config.DryRun { return nil } // Current and wanted states for the resource want := utils.NewString(state.Want) current := utils.NewString(state.Current) // The list of present and absent states for the resource present := utils.NewList(r.GetPresentStates()...) absent := utils.NewList(r.GetAbsentStates()...) var action func() error switch { case want.IsInList(present) && current.IsInList(absent): action = r.Create case want.IsInList(absent) && current.IsInList(present): action = r.Delete case state.Outdated: action = r.Update } if action != nil { return action() } return nil }
// runTriggers executes the triggers for each // monitored resource if it's state has changed func (c *Catalog) runTriggers(r resource.Resource) error { c.status.Lock() defer c.status.Unlock() for subscribed, trigger := range r.SubscribedTo() { item := c.status.Items[subscribed] if !item.StateChanged { continue } c.config.Logger.Printf("%s running trigger, because %s has changed\n", r.ID(), subscribed) c.config.L.Push(trigger) if err := c.config.L.PCall(0, 0, nil); err != nil { c.config.Logger.Printf("%s trigger exited with an error: %s\n", r.ID(), err) return err } } return nil }
// execute processes a single resource func (c *Catalog) execute(r resource.Resource) *StatusItem { if err := c.hasFailedDependencies(r); err != nil { return &StatusItem{Err: err} } if err := r.Validate(); err != nil { return &StatusItem{Err: err} } if err := r.Initialize(); err != nil { return &StatusItem{Err: err} } defer r.Close() state, err := r.Evaluate() if err != nil { return &StatusItem{Err: err} } if c.config.DryRun { return &StatusItem{} } // Current and wanted states for the resource want := utils.NewString(state.Want) current := utils.NewString(state.Current) // The list of present and absent states for the resource present := utils.NewList(r.PresentStates()...) absent := utils.NewList(r.AbsentStates()...) // Process resource id := r.ID() var action func() error switch { case want.IsInList(present) && current.IsInList(absent): action = r.Create c.config.Logger.Printf("%s is %s, should be %s\n", id, current, want) case want.IsInList(absent) && current.IsInList(present): action = r.Delete c.config.Logger.Printf("%s is %s, should be %s\n", id, current, want) default: // No-op: resource is in sync } stateChanged := false if action != nil { stateChanged = true if err := action(); err != nil { return &StatusItem{StateChanged: true, Err: err} } } // Process resource properties for _, p := range r.Properties() { synced, err := p.IsSynced() if err != nil { // Some properties make no sense if the resource is absent, e.g. // setting up file permissions requires that the file managed by the // resource is present, therefore we ignore errors for properties // which make no sense if the resource is absent. if err == resource.ErrResourceAbsent { continue } e := fmt.Errorf("unable to evaluate property %s: %s\n", p.Name, err) return &StatusItem{StateChanged: true, Err: e} } if !synced { stateChanged = true c.config.Logger.Printf("%s property '%s' is out of date\n", id, p.Name()) if err := p.Set(); err != nil { e := fmt.Errorf("unable to set property %s: %s\n", p.Name, err) return &StatusItem{StateChanged: true, Err: e} } } } if err := c.runTriggers(r); err != nil { return &StatusItem{StateChanged: stateChanged, Err: err} } return &StatusItem{StateChanged: stateChanged, Err: nil} }