manager/runner.go
func (r *Runner) runTemplate(tmpl *template.Template, runCtx *templateRunCtx) (*RenderEvent, error) {
	log.Printf("[DEBUG] (runner) checking template %s", tmpl.ID())

	// Grab the last event
	r.renderEventsLock.RLock()
	lastEvent := r.renderEvents[tmpl.ID()]
	r.renderEventsLock.RUnlock()

	// Create the event
	event := &RenderEvent{
		Template:        tmpl,
		TemplateConfigs: r.templateConfigsFor(tmpl),
	}

	if lastEvent != nil {
		event.LastWouldRender = lastEvent.LastWouldRender
		event.LastDidRender = lastEvent.LastDidRender
	}

	// Check if we are currently the leader instance
	isLeader := true
	if r.dedup != nil {
		isLeader = r.dedup.IsLeader(tmpl)
	}

	// If we are in once mode and this template was already rendered, move
	// onto the next one. We do not want to re-render the template if we are
	// in once mode, and we certainly do not want to re-run any commands.
	if r.once {
		r.renderEventsLock.RLock()
		event, ok := r.renderEvents[tmpl.ID()]
		r.renderEventsLock.RUnlock()
		if ok && (event.WouldRender || event.DidRender) {
			log.Printf("[DEBUG] (runner) once mode and already rendered")
			return nil, nil
		}
	}

	// Attempt to render the template, returning any missing dependencies and
	// the rendered contents. If there are any missing dependencies, the
	// contents cannot be rendered or trusted!
	result, err := tmpl.Execute(&template.ExecuteInput{
		Brain: r.brain,
		Env:   r.childEnv(),
	})
	if err != nil {
		return nil, errors.Wrap(err, tmpl.Source())
	}

	// Grab the list of used and missing dependencies.
	missing, used := result.Missing, result.Used

	// Add the dependency to the list of dependencies for this runner.
	for _, d := range used.List() {
		// If we've taken over leadership for a template, we may have data
		// that is cached, but not have the watcher. We must treat this as
		// missing so that we create the watcher and re-run the template.
		if isLeader && !r.watcher.Watching(d) {
			missing.Add(d)
		}
		if _, ok := runCtx.depsMap[d.String()]; !ok {
			runCtx.depsMap[d.String()] = d
		}
	}

	// Diff any missing dependencies the template reported with dependencies
	// the watcher is watching.
	unwatched := new(dep.Set)
	for _, d := range missing.List() {
		if !r.watcher.Watching(d) {
			unwatched.Add(d)
		}
	}

	// Update the event with the new dependency information
	event.MissingDeps = missing
	event.UnwatchedDeps = unwatched
	event.UsedDeps = used
	event.UpdatedAt = time.Now().UTC()

	// If there are unwatched dependencies, start the watcher and exit since we
	// won't have data.
	if l := unwatched.Len(); l > 0 {
		log.Printf("[DEBUG] (runner) was not watching %d dependencies", l)
		for _, d := range unwatched.List() {
			// If we are deduplicating, we must still handle non-sharable
			// dependencies, since those will be ignored.
			if isLeader || !d.CanShare() {
				r.watcher.Add(d)
			}
		}
		return event, nil
	}

	// If the template is missing data for some dependencies then we are not
	// ready to render and need to move on to the next one.
	if l := missing.Len(); l > 0 {
		log.Printf("[DEBUG] (runner) missing data for %d dependencies", l)
		return event, nil
	}

	// Trigger an update of the de-duplication manager
	if r.dedup != nil && isLeader {
		if err := r.dedup.UpdateDeps(tmpl, used.List()); err != nil {
			log.Printf("[ERR] (runner) failed to update dependency data for de-duplication: %v", err)
		}
	}

	// If quiescence is activated, start/update the timers and loop back around.
	// We do not want to render the templates yet.
	if q, ok := r.quiescenceMap[tmpl.ID()]; ok {
		q.tick()
		// This event is being returned early for quiescence
		event.ForQuiescence = true
		return event, nil
	}

	// For each template configuration that is tied to this template, attempt to
	// render it to disk and accumulate commands for later use.
	for _, templateConfig := range r.templateConfigsFor(tmpl) {
		log.Printf("[DEBUG] (runner) rendering %s", templateConfig.Display())

		// Render the template, taking dry mode into account
		result, err := renderer.Render(&renderer.RenderInput{
			Backup:         config.BoolVal(templateConfig.Backup),
			Contents:       result.Output,
			CreateDestDirs: config.BoolVal(templateConfig.CreateDestDirs),
			Dry:            r.dry,
			DryStream:      r.outStream,
			Path:           config.StringVal(templateConfig.Destination),
			Perms:          config.FileModeVal(templateConfig.Perms),
		})
		if err != nil {
			return nil, errors.Wrap(err, "error rendering "+templateConfig.Display())
		}

		renderTime := time.Now().UTC()

		// If we would have rendered this template (but we did not because the
		// contents were the same or something), we should consider this template
		// rendered even though the contents on disk have not been updated. We
		// will not fire commands unless the template was _actually_ rendered to
		// disk though.
		if result.WouldRender {
			// This event would have rendered
			event.WouldRender = true
			event.LastWouldRender = renderTime
		}

		// If we _actually_ rendered the template to disk, we want to run the
		// appropriate commands.
		if result.DidRender {
			log.Printf("[INFO] (runner) rendered %s", templateConfig.Display())

			// This event did render
			event.DidRender = true
			event.LastDidRender = renderTime

			// Update the contents
			event.Contents = result.Contents

			if !r.dry {
				// If the template was rendered (changed) and we are not in dry-run mode,
				// aggregate commands, ignoring previously known commands
				//
				// Future-self Q&A: Why not use a map for the commands instead of an
				// array with an expensive lookup option? Well I'm glad you asked that
				// future-self! One of the API promises is that commands are executed
				// in the order in which they are provided in the TemplateConfig
				// definitions. If we inserted commands into a map, we would lose that
				// relative ordering and people would be unhappy.
				// if config.StringPresent(ctemplate.Command)
				if c := config.StringVal(templateConfig.Exec.Command); c != "" {
					existing := findCommand(templateConfig, runCtx.commands)
					if existing != nil {
						log.Printf("[DEBUG] (runner) skipping command %q from %s (already appended from %s)",
							c, templateConfig.Display(), existing.Display())
					} else {
						log.Printf("[DEBUG] (runner) appending command %q from %s",
							c, templateConfig.Display())
						runCtx.commands = append(runCtx.commands, templateConfig)
					}
				}
			}
		}
	}

	return event, nil
}
func (r *Runner) runTemplate(tmpl *template.Template, runCtx *templateRunCtx) (*RenderEvent, error) {
	log.Printf("[DEBUG] (runner) checking template %s", tmpl.ID())

	// Grab the last event
	r.renderEventsLock.RLock()
	lastEvent := r.renderEvents[tmpl.ID()]
	r.renderEventsLock.RUnlock()

	// Create the event
	event := &RenderEvent{
		Template:        tmpl,
		TemplateConfigs: r.templateConfigsFor(tmpl),
	}

	if lastEvent != nil {
		event.LastWouldRender = lastEvent.LastWouldRender
		event.LastDidRender = lastEvent.LastDidRender
	}

	// Check if we are currently the leader instance
	isLeader := true
	if r.dedup != nil {
		isLeader = r.dedup.IsLeader(tmpl)
	}

	// If we are in once mode and this template was already rendered, move
	// onto the next one. We do not want to re-render the template if we are
	// in once mode, and we certainly do not want to re-run any commands.
	if r.once {
		r.renderEventsLock.RLock()
		event, ok := r.renderEvents[tmpl.ID()]
		r.renderEventsLock.RUnlock()
		if ok && (event.WouldRender || event.DidRender) {
			log.Printf("[DEBUG] (runner) once mode and already rendered")
			return nil, nil
		}
	}

	// Attempt to render the template, returning any missing dependencies and
	// the rendered contents. If there are any missing dependencies, the
	// contents cannot be rendered or trusted!
	result, err := tmpl.Execute(&template.ExecuteInput{
		Brain: r.brain,
		Env:   r.childEnv(),
	})
	if err != nil {
		return nil, errors.Wrap(err, tmpl.Source())
	}

	// Grab the list of used and missing dependencies.
	missing, used := result.Missing, result.Used

	// Add the dependency to the list of dependencies for this runner.
	for _, d := range used.List() {
		// If we've taken over leadership for a template, we may have data
		// that is cached, but not have the watcher. We must treat this as
		// missing so that we create the watcher and re-run the template.
		if isLeader && !r.watcher.Watching(d) {
			missing.Add(d)
		}
		if _, ok := runCtx.depsMap[d.String()]; !ok {
			runCtx.depsMap[d.String()] = d
		}
	}

	// Diff any missing dependencies the template reported with dependencies
	// the watcher is watching.
	unwatched := new(dep.Set)
	for _, d := range missing.List() {
		if !r.watcher.Watching(d) {
			unwatched.Add(d)
		}
	}

	// Update the event with the new dependency information
	event.MissingDeps = missing
	event.UnwatchedDeps = unwatched
	event.UsedDeps = used
	event.UpdatedAt = time.Now().UTC()

	// If there are unwatched dependencies, start the watcher and exit since we
	// won't have data.
	if l := unwatched.Len(); l > 0 {
		log.Printf("[DEBUG] (runner) was not watching %d dependencies", l)
		for _, d := range unwatched.List() {
			// If we are deduplicating, we must still handle non-sharable
			// dependencies, since those will be ignored.
			if isLeader || !d.CanShare() {
				r.watcher.Add(d)
			}
		}
		return event, nil
	}

	// If the template is missing data for some dependencies then we are not
	// ready to render and need to move on to the next one.
	if l := missing.Len(); l > 0 {
		log.Printf("[DEBUG] (runner) missing data for %d dependencies", l)
		return event, nil
	}

	// Trigger an update of the de-duplication manager
	if r.dedup != nil && isLeader {
		if err := r.dedup.UpdateDeps(tmpl, used.List()); err != nil {
			log.Printf("[ERR] (runner) failed to update dependency data for de-duplication: %v", err)
		}
	}

	// If quiescence is activated, start/update the timers and loop back around.
	// We do not want to render the templates yet.
	if q, ok := r.quiescenceMap[tmpl.ID()]; ok {
		q.tick()
		// This event is being returned early for quiescence
		event.ForQuiescence = true
		return event, nil
	}

	// For each template configuration that is tied to this template, attempt to
	// render it to disk and accumulate commands for later use.
	for _, templateConfig := range r.templateConfigsFor(tmpl) {
		log.Printf("[DEBUG] (runner) rendering %s", templateConfig.Display())

		// Render the template, taking dry mode into account
		result, err := renderer.Render(&renderer.RenderInput{
			Backup:         config.BoolVal(templateConfig.Backup),
			Contents:       result.Output,
			CreateDestDirs: config.BoolVal(templateConfig.CreateDestDirs),
			Dry:            r.dry,
			DryStream:      r.outStream,
			Path:           config.StringVal(templateConfig.Destination),
			Perms:          config.FileModeVal(templateConfig.Perms),
		})
		if err != nil {
			return nil, errors.Wrap(err, "error rendering "+templateConfig.Display())
		}

		renderTime := time.Now().UTC()

		// If we would have rendered this template (but we did not because the
		// contents were the same or something), we should consider this template
		// rendered even though the contents on disk have not been updated. We
		// will not fire commands unless the template was _actually_ rendered to
		// disk though.
		if result.WouldRender {
			// This event would have rendered
			event.WouldRender = true
			event.LastWouldRender = renderTime
		}

		// If we _actually_ rendered the template to disk, we want to run the
		// appropriate commands.
		if result.DidRender {
			log.Printf("[INFO] (runner) rendered %s", templateConfig.Display())

			// This event did render
			event.DidRender = true
			event.LastDidRender = renderTime

			// Update the contents
			event.Contents = result.Contents

			if !r.dry {
				// If the template was rendered (changed) and we are not in dry-run mode,
				// aggregate commands, ignoring previously known commands
				//
				// Future-self Q&A: Why not use a map for the commands instead of an
				// array with an expensive lookup option? Well I'm glad you asked that
				// future-self! One of the API promises is that commands are executed
				// in the order in which they are provided in the TemplateConfig
				// definitions. If we inserted commands into a map, we would lose that
				// relative ordering and people would be unhappy.
				// if config.StringPresent(ctemplate.Command)
				if c := config.StringVal(templateConfig.Exec.Command); c != "" {
					existing := findCommand(templateConfig, runCtx.commands)
					if existing != nil {
						log.Printf("[DEBUG] (runner) skipping command %q from %s (already appended from %s)",
							c, templateConfig.Display(), existing.Display())
					} else {
						log.Printf("[DEBUG] (runner) appending command %q from %s",
							c, templateConfig.Display())
						runCtx.commands = append(runCtx.commands, templateConfig)
					}
				}
			}
		}
	}

	return event, nil
}
// runTemplate is used to run a particular template. It takes as input the
// template to run and a shared run context that allows sharing of information
// between templates. The run returns a potentially nil render event and any
// error that occured. The render event is nil in the case that the template has
// been already rendered and is a once template or if there is an error.