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
}
func ( r * Runner ) runTemplate ( tmpl * template . Template , runCtx * templateRunCtx ) ( * RenderEvent , error ) { log . Printf ( " " , 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 ( " " ) 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 ( " " , 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 ( " " , 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 ) } } // 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 ( " " , 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 , " " + 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 ( " " , 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 ( " " , c , templateConfig . Display ( ) , existing . Display ( ) ) } else { log . Printf ( " " , 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.
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 .