diff --git a/pkg/cli/context.go b/pkg/cli/context.go index 22857ec79..415d1271c 100644 --- a/pkg/cli/context.go +++ b/pkg/cli/context.go @@ -428,14 +428,7 @@ func (c *Context) Funcs() []template.Function { // loadBackend determines the backend to use for executing the rendered template text (e.g. run in shell). // During this phase, the template delimiters are changed to =% %= so put this in the comment {{/* */}} -func (c *Context) loadBackends() error { - t, err := template.NewTemplate(c.src, template.Options{ - DelimLeft: "=%", - DelimRight: "%=", - }) - if err != nil { - return err - } +func (c *Context) loadBackends(t *template.Template) error { t.AddFunc("print", func() string { c.run = func(script string) error { @@ -567,35 +560,61 @@ func (c *Context) loadBackends() error { return "" }) - _, err = t.Render(c) + _, err := t.Render(c) + + // clean up after we rendered... remove the functions + t.RemoveFunc("sh", "print", "instanceProvision", "managerCommit") return err } +func (c *Context) getTemplate() (*template.Template, error) { + if c.template == nil { + t, err := template.NewTemplate(c.src, template.Options{}) + if err != nil { + return nil, err + } + c.template = t + } + return c.template, nil +} + // BuildFlags from parsing the body which is a template -func (c *Context) BuildFlags() error { - t, err := template.NewTemplate(c.src, template.Options{}) +func (c *Context) BuildFlags() (err error) { + var t *template.Template + + t, err = c.getTemplate() if err != nil { - return err + return } - + t.SetOptions(template.Options{}) _, err = configureTemplate(t, c.plugins).Render(c) - return err + return } // Execute runs the command -func (c *Context) Execute() error { +func (c *Context) Execute() (err error) { + var t *template.Template - if err := c.loadBackends(); err != nil { - return err + t, err = c.getTemplate() + if err != nil { + return } - t, err := template.NewTemplate(c.src, template.Options{ - Stderr: func() io.Writer { return os.Stderr }, + // First pass to get the backends + t.SetOptions(template.Options{ + DelimLeft: "=%", + DelimRight: "%=", }) - if err != nil { + + if err := c.loadBackends(t); err != nil { return err } + // Now regular processing + t.SetOptions(template.Options{ + Stderr: func() io.Writer { return os.Stderr }, + }) + c.exec = true c.template = t script, err := configureTemplate(t, c.plugins).Render(c) diff --git a/pkg/template/template.go b/pkg/template/template.go index 73c430946..cb2f760e3 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -143,6 +143,15 @@ func NewTemplateFromBytes(buff []byte, contextURL string, opt Options) (*Templat }, nil } +// NewFromTemplate creates a new template from existing, without any fetches (hence network calls) +func NewFromTemplate(from *Template, opt Options) *Template { + return &Template{ + options: opt, + url: from.url, + body: from.body, + } +} + // SetOptions sets the runtime flags for the engine func (t *Template) SetOptions(opt Options) *Template { t.lock.Lock() @@ -167,6 +176,16 @@ func (t *Template) AddFunc(name string, f interface{}) *Template { return t } +// RemoveFunc remove the functions +func (t *Template) RemoveFunc(name ...string) *Template { + t.lock.Lock() + defer t.lock.Unlock() + for _, n := range name { + delete(t.funcs, n) + } + return t +} + // Ref returns the value keyed by name in the context of this template. See 'ref' template function. func (t *Template) Ref(name string) interface{} { if found, has := t.globals[name]; has { @@ -275,10 +294,6 @@ func (t *Template) build(context Context) error { t.lock.Lock() defer t.lock.Unlock() - if t.parsed != nil { - return nil - } - registered := []Function{} fm := map[string]interface{}{}