add support for yaml templates

This commit is contained in:
Eoin McAfee 2021-08-24 13:20:44 +01:00
parent 6949293c99
commit 12808aadbe
7 changed files with 145 additions and 25 deletions

1
go.mod
View file

@ -18,6 +18,7 @@ require (
github.com/drone/drone-ui v2.0.1+incompatible github.com/drone/drone-ui v2.0.1+incompatible
github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4 github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4
github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629 github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629
github.com/drone/funcmap v0.0.0-20210823160631-9e9dec149056 // indirect
github.com/drone/go-license v1.0.2 github.com/drone/go-license v1.0.2
github.com/drone/go-login v1.0.4-0.20190311170324-2a4df4f242a2 github.com/drone/go-login v1.0.4-0.20190311170324-2a4df4f242a2
github.com/drone/go-scm v1.15.2 github.com/drone/go-scm v1.15.2

4
go.sum
View file

@ -86,6 +86,10 @@ github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4 h1:XsstoCeXC2t8
github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4/go.mod h1:QsqliFK8nG04AHFN9tTn9XJomRBQHD4wcejWW1uz/10= github.com/drone/drone-yaml v1.2.4-0.20200326192514-6f4d6dfb39e4/go.mod h1:QsqliFK8nG04AHFN9tTn9XJomRBQHD4wcejWW1uz/10=
github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629 h1:rIaZZalMGGPb2cU/+ypuggZ8aMlpa17RUlJUtsMv8pw= github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629 h1:rIaZZalMGGPb2cU/+ypuggZ8aMlpa17RUlJUtsMv8pw=
github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g= github.com/drone/envsubst v1.0.3-0.20200709231038-aa43e1c1a629/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
github.com/drone/funcmap v0.0.0-20190918184546-d4ef6e88376d h1:/IO7UVVu191Jc0DajV4cDVoO+91cuppvgxg2MZl+AXI=
github.com/drone/funcmap v0.0.0-20190918184546-d4ef6e88376d/go.mod h1:Hph0/pT6ZxbujnE1Z6/08p5I0XXuOsppqF6NQlGOK0E=
github.com/drone/funcmap v0.0.0-20210823160631-9e9dec149056 h1:SCJwMR0FMA0aKwAntCBh02YmtHEnU40zaDzeeCvIRug=
github.com/drone/funcmap v0.0.0-20210823160631-9e9dec149056/go.mod h1:Hph0/pT6ZxbujnE1Z6/08p5I0XXuOsppqF6NQlGOK0E=
github.com/drone/go-license v1.0.2 h1:7OwndfYk+Lp/cGHkxe4HUn/Ysrrw3WYH2pnd99yrkok= github.com/drone/go-license v1.0.2 h1:7OwndfYk+Lp/cGHkxe4HUn/Ysrrw3WYH2pnd99yrkok=
github.com/drone/go-license v1.0.2/go.mod h1:fGRHf+F1cEaw3YVYiJ6js3G3dVhcxyS617RnNRUMsms= github.com/drone/go-license v1.0.2/go.mod h1:fGRHf+F1cEaw3YVYiJ6js3G3dVhcxyS617RnNRUMsms=
github.com/drone/go-login v1.0.4-0.20190311170324-2a4df4f242a2 h1:RGpgNkowJc5LAVn/ZONx70qmnaTA0z/3hHPzTBdAEO8= github.com/drone/go-login v1.0.4-0.20190311170324-2a4df4f242a2 h1:RGpgNkowJc5LAVn/ZONx70qmnaTA0z/3hHPzTBdAEO8=

View file

@ -17,22 +17,26 @@
package converter package converter
import ( import (
"bytes"
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
"path/filepath"
"regexp" "regexp"
"strings" "strings"
templating "text/template"
"github.com/drone/drone/core" "github.com/drone/drone/core"
"github.com/drone/drone/plugin/converter/jsonnet" "github.com/drone/drone/plugin/converter/jsonnet"
"github.com/drone/drone/plugin/converter/starlark" "github.com/drone/drone/plugin/converter/starlark"
"github.com/drone/funcmap"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
var ( var (
// templateFileRE regex to verifying kind is template. // TemplateFileRE regex to verifying kind is template.
templateFileRE = regexp.MustCompile("^kind:\\s+template+\\n") TemplateFileRE = regexp.MustCompile("^kind:\\s+template+\\n")
ErrTemplateNotFound = errors.New("template converter: template name given not found") ErrTemplateNotFound = errors.New("template converter: template name given not found")
ErrTemplateSyntaxErrors = errors.New("template converter: there is a problem with the yaml file provided") ErrTemplateSyntaxErrors = errors.New("template converter: there is a problem with the yaml file provided")
) )
@ -52,8 +56,9 @@ func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
if strings.HasSuffix(req.Repo.Config, ".yml") == false { if strings.HasSuffix(req.Repo.Config, ".yml") == false {
return nil, nil return nil, nil
} }
// check kind is template // check kind is template
if templateFileRE.MatchString(req.Config.Data) == false { if TemplateFileRE.MatchString(req.Config.Data) == false {
return nil, nil return nil, nil
} }
// map to templateArgs // map to templateArgs
@ -71,29 +76,55 @@ func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
return nil, err return nil, err
} }
// Check if file is of type Starlark switch filepath.Ext(templateArgs.Load) {
if strings.HasSuffix(templateArgs.Load, ".script") || case ".yml", ".yaml":
strings.HasSuffix(templateArgs.Load, ".star") || return parseYaml(req, template, templateArgs)
strings.HasSuffix(templateArgs.Load, ".starlark") { case ".star", ".starlark", ".script":
return parseStarlark(req, template, templateArgs)
file, err := starlark.Parse(req, template, templateArgs.Data) case ".jsonnet":
if err != nil { return parseJsonnet(req, template, templateArgs)
return nil, err default:
}
return &core.Config{
Data: file,
}, nil
}
// Check if the file is of type Jsonnet
if strings.HasSuffix(templateArgs.Load, ".jsonnet") {
file, err := jsonnet.Parse(req, template, templateArgs.Data)
if err != nil {
return nil, err
}
return &core.Config{
Data: file,
}, nil
} }
return nil, nil return nil, nil
} }
func parseYaml(req *core.ConvertArgs, template *core.Template, templateArgs core.TemplateArgs) (*core.Config, error) {
data := map[string]interface{}{
"build": toBuild(req.Build),
"repo": toRepo(req.Repo),
"input": templateArgs.Data,
}
tmpl, err := templating.New(template.Name).Funcs(funcmap.SafeFuncs).Parse(template.Data)
if err != nil {
return nil, err
}
var out bytes.Buffer
err = tmpl.Execute(&out, data)
if err != nil {
return nil, err
}
return &core.Config{
Data: out.String(),
}, nil
}
func parseJsonnet(req *core.ConvertArgs, template *core.Template, templateArgs core.TemplateArgs) (*core.Config, error) {
file, err := jsonnet.Parse(req, template, templateArgs.Data)
if err != nil {
return nil, err
}
return &core.Config{
Data: file,
}, nil
}
func parseStarlark(req *core.ConvertArgs, template *core.Template, templateArgs core.TemplateArgs) (*core.Config, error) {
file, err := starlark.Parse(req, template, templateArgs.Data)
if err != nil {
return nil, err
}
return &core.Config{
Data: file,
}, nil
}

View file

@ -326,3 +326,65 @@ func TestTemplateNestedValuesPluginConvertStarlark(t *testing.T) {
t.Errorf("Want %q got %q", want, got) t.Errorf("Want %q got %q", want, got)
} }
} }
func TestTemplatePluginConvertYaml(t *testing.T) {
templateArgs, err := ioutil.ReadFile("testdata/yaml.template.yml")
if err != nil {
t.Error(err)
return
}
req := &core.ConvertArgs{
Build: &core.Build{
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
},
Repo: &core.Repository{
Slug: "octocat/hello-world",
Config: ".drone.yml",
Namespace: "octocat",
},
Config: &core.Config{
Data: string(templateArgs),
},
}
beforeInput, err := ioutil.ReadFile("testdata/yaml.input.yml")
if err != nil {
t.Error(err)
return
}
after, err := ioutil.ReadFile("testdata/yaml.input.golden")
if err != nil {
t.Error(err)
return
}
template := &core.Template{
Name: "plugin.yaml",
Data: string(beforeInput),
Namespace: "octocat",
}
controller := gomock.NewController(t)
defer controller.Finish()
templates := mock.NewMockTemplateStore(controller)
templates.EXPECT().FindName(gomock.Any(), template.Name, req.Repo.Namespace).Return(template, nil)
plugin := Template(templates)
config, err := plugin.Convert(noContext, req)
if err != nil {
t.Error(err)
return
}
if config == nil {
t.Error("Want non-nil configuration")
return
}
if want, got := config.Data, string(after); want != got {
t.Errorf("Want %q got %q", want, got)
}
}

View file

@ -0,0 +1,8 @@
kind: pipeline
type: docker
name: defaults
steps:
- name: MY_STEP
image: my_image
commands:
- my_command

View file

@ -0,0 +1,8 @@
kind: pipeline
type: docker
name: defaults
steps:
- name: {{ upper .input.stepName }}
image: {{ .input.image }}
commands:
- {{ .input.commands }}

View file

@ -0,0 +1,6 @@
kind: template
load: plugin.yaml
data:
stepName: my_step
image: my_image
commands: my_command