@@ -18,6 +18,8 @@ import (
1818 "strings"
1919 "syscall"
2020 "text/template"
21+
22+ "github.com/flosch/pongo2"
2123)
2224
2325func exists (path string ) (bool , error ) {
@@ -453,6 +455,84 @@ func newTemplate(name string) *template.Template {
453455 return tmpl
454456}
455457
458+ // Takes a template function that returns an error as its second return value,
459+ // and returns a function that takes a pongo2 ExecutionContext as its first
460+ // argument and calls ExecutionContext.OrigError() if the second return value
461+ // of the original function is not nil when called. Otherwise returns the first
462+ // return value.
463+ func pongoWrap (fn interface {}) func (* pongo2.ExecutionContext , ... interface {}) interface {} {
464+ fv := reflect .ValueOf (fn )
465+ ft := reflect .TypeOf (fn )
466+ return func (ctx * pongo2.ExecutionContext , args ... interface {}) interface {} {
467+ if ft .NumIn () != len (args ) {
468+ msg := fmt .Sprintf ("Wrong number of arguments; expected %d, got %d" , ft .NumIn (), len (args ))
469+ return ctx .Error (msg , nil )
470+ }
471+ vals := make ([]reflect.Value , len (args ))
472+ for i , v := range args {
473+ vt := reflect .TypeOf (v )
474+ if ! vt .ConvertibleTo (ft .In (i )) {
475+ msg := fmt .Sprintf ("Wrong type for argument %d (got %s, expected %s)\n " , i , vt , ft .In (i ))
476+ return ctx .Error (msg , nil )
477+ }
478+ vals [i ] = reflect .ValueOf (args [i ])
479+ }
480+ retvals := fv .Call (vals )
481+ ret := retvals [0 ].Interface ()
482+ err := retvals [1 ].Interface ()
483+ if err != nil {
484+ return ctx .OrigError (err .(error ), nil )
485+ }
486+ return ret
487+ }
488+ }
489+
490+ func pongoContext (containers Context ) pongo2.Context {
491+ context := pongo2.Context {
492+ "containers" : containers ,
493+ "env" : containers .Env ,
494+ "docker" : containers .Docker ,
495+ "closest" : arrayClosest ,
496+ "coalesce" : coalesce ,
497+ "contains" : contains ,
498+ "dict" : pongoWrap (dict ),
499+ "dir" : pongoWrap (dirList ),
500+ "exists" : pongoWrap (exists ),
501+ "first" : arrayFirst ,
502+ "groupBy" : pongoWrap (groupBy ),
503+ "groupByKeys" : pongoWrap (groupByKeys ),
504+ "groupByMulti" : pongoWrap (groupByMulti ),
505+ "groupByLabel" : pongoWrap (groupByLabel ),
506+ "hasPrefix" : hasPrefix ,
507+ "hasSuffix" : hasSuffix ,
508+ "json" : pongoWrap (marshalJson ),
509+ "intersect" : intersect ,
510+ "keys" : pongoWrap (keys ),
511+ "last" : arrayLast ,
512+ "replace" : strings .Replace ,
513+ "parseBool" : strconv .ParseBool ,
514+ "parseJson" : pongoWrap (unmarshalJson ),
515+ "printf" : fmt .Sprintf ,
516+ "queryEscape" : url .QueryEscape ,
517+ "sha1" : hashSha1 ,
518+ "split" : strings .Split ,
519+ "splitN" : strings .SplitN ,
520+ "trimPrefix" : trimPrefix ,
521+ "trimSuffix" : trimSuffix ,
522+ "trim" : trim ,
523+ "when" : pongoWrap (when ),
524+ "where" : pongoWrap (where ),
525+ "whereExist" : pongoWrap (whereExist ),
526+ "whereNotExist" : pongoWrap (whereNotExist ),
527+ "whereAny" : pongoWrap (whereAny ),
528+ "whereAll" : pongoWrap (whereAll ),
529+ "whereLabelExists" : pongoWrap (whereLabelExists ),
530+ "whereLabelDoesNotExist" : pongoWrap (whereLabelDoesNotExist ),
531+ "whereLabelValueMatches" : pongoWrap (whereLabelValueMatches ),
532+ }
533+ return context
534+ }
535+
456536func filterRunning (config Config , containers Context ) Context {
457537 if config .IncludeStopped {
458538 return containers
@@ -486,7 +566,7 @@ func GenerateFile(config Config, containers Context) bool {
486566 filteredContainers = filteredRunningContainers
487567 }
488568
489- contents := executeTemplate (config .Template , filteredContainers )
569+ contents := executeTemplate (config .Template , config . Engine , filteredContainers )
490570
491571 if ! config .KeepBlankLines {
492572 buf := new (bytes.Buffer )
@@ -537,16 +617,29 @@ func GenerateFile(config Config, containers Context) bool {
537617 return true
538618}
539619
540- func executeTemplate (templatePath string , containers Context ) []byte {
541- tmpl , err := newTemplate (filepath .Base (templatePath )).ParseFiles (templatePath )
542- if err != nil {
543- log .Fatalf ("Unable to parse template: %s" , err )
544- }
620+ func executeTemplate (templatePath string , templateEngine string , containers Context ) []byte {
621+ if templateEngine == "pongo2" {
622+ context := pongoContext (containers )
623+ tmpl , err := pongo2 .FromFile (templatePath )
624+ if err != nil {
625+ log .Fatalf ("Unable to parse template: %s" , err )
626+ }
627+ contents , err := tmpl .ExecuteBytes (context )
628+ if err != nil {
629+ log .Fatalf ("Template error: %s\n " , err )
630+ }
631+ return contents
632+ } else {
633+ tmpl , err := newTemplate (filepath .Base (templatePath )).ParseFiles (templatePath )
634+ if err != nil {
635+ log .Fatalf ("Unable to parse template: %s" , err )
636+ }
545637
546- buf := new (bytes.Buffer )
547- err = tmpl .ExecuteTemplate (buf , filepath .Base (templatePath ), & containers )
548- if err != nil {
549- log .Fatalf ("Template error: %s\n " , err )
638+ buf := new (bytes.Buffer )
639+ err = tmpl .ExecuteTemplate (buf , filepath .Base (templatePath ), & containers )
640+ if err != nil {
641+ log .Fatalf ("Template error: %s\n " , err )
642+ }
643+ return buf .Bytes ()
550644 }
551- return buf .Bytes ()
552645}
0 commit comments