Merge pull request #3085 from drone/feature/templates-jsonnet
add jsonnet support for build templates
This commit is contained in:
commit
ef168cd2a4
8 changed files with 265 additions and 35 deletions
|
@ -7,13 +7,11 @@
|
||||||
package converter
|
package converter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/drone/drone/core"
|
"github.com/drone/drone/core"
|
||||||
|
"github.com/drone/drone/plugin/converter/jsonnet"
|
||||||
"github.com/google/go-jsonnet"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(bradrydzewski) handle jsonnet imports
|
// TODO(bradrydzewski) handle jsonnet imports
|
||||||
|
@ -42,32 +40,12 @@ func (p *jsonnetPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*co
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the jsonnet vm
|
file, err := jsonnet.Parse(req, nil, nil)
|
||||||
vm := jsonnet.MakeVM()
|
|
||||||
vm.MaxStack = 500
|
|
||||||
vm.StringOutput = false
|
|
||||||
vm.ErrorFormatter.SetMaxStackTraceSize(20)
|
|
||||||
|
|
||||||
// convert the jsonnet file to yaml
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
docs, err := vm.EvaluateSnippetStream(req.Repo.Config, req.Config.Data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
doc, err2 := vm.EvaluateSnippet(req.Repo.Config, req.Config.Data)
|
return nil, err
|
||||||
if err2 != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
docs = append(docs, doc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the jsonnet vm returns a stream of yaml documents
|
|
||||||
// that need to be combined into a single yaml file.
|
|
||||||
for _, doc := range docs {
|
|
||||||
buf.WriteString("---")
|
|
||||||
buf.WriteString("\n")
|
|
||||||
buf.WriteString(doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &core.Config{
|
return &core.Config{
|
||||||
Data: buf.String(),
|
Data: file,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
56
plugin/converter/jsonnet/jsonnet.go
Normal file
56
plugin/converter/jsonnet/jsonnet.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package jsonnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/drone/drone/core"
|
||||||
|
|
||||||
|
"github.com/google/go-jsonnet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Parse(req *core.ConvertArgs, template *core.Template, templateData map[string]interface{}) (string, error) {
|
||||||
|
// create the jsonnet vm
|
||||||
|
vm := jsonnet.MakeVM()
|
||||||
|
vm.MaxStack = 500
|
||||||
|
vm.StringOutput = false
|
||||||
|
vm.ErrorFormatter.SetMaxStackTraceSize(20)
|
||||||
|
|
||||||
|
var jsonnetFile string
|
||||||
|
var jsonentFileName string
|
||||||
|
if template != nil {
|
||||||
|
jsonnetFile = template.Data
|
||||||
|
jsonentFileName = template.Name
|
||||||
|
} else {
|
||||||
|
jsonnetFile = req.Config.Data
|
||||||
|
jsonentFileName = req.Repo.Config
|
||||||
|
}
|
||||||
|
// map external inputs
|
||||||
|
if len(templateData) != 0 {
|
||||||
|
for k, v := range templateData {
|
||||||
|
key := fmt.Sprintf("input." + k)
|
||||||
|
val := fmt.Sprint(v)
|
||||||
|
vm.ExtVar(key, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// convert the jsonnet file to yaml
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
docs, err := vm.EvaluateSnippetStream(jsonentFileName, jsonnetFile)
|
||||||
|
if err != nil {
|
||||||
|
doc, err2 := vm.EvaluateSnippet(jsonentFileName, jsonnetFile)
|
||||||
|
if err2 != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
docs = append(docs, doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the jsonnet vm returns a stream of yaml documents
|
||||||
|
// that need to be combined into a single yaml file.
|
||||||
|
for _, doc := range docs {
|
||||||
|
buf.WriteString("---")
|
||||||
|
buf.WriteString("\n")
|
||||||
|
buf.WriteString(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
94
plugin/converter/jsonnet/jsonnet_test.go
Normal file
94
plugin/converter/jsonnet/jsonnet_test.go
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
package jsonnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/drone/drone/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
before, err := ioutil.ReadFile("../testdata/input.jsonnet")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
after, err := ioutil.ReadFile("../testdata/input.jsonnet.golden")
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
Config: &core.Config{},
|
||||||
|
}
|
||||||
|
|
||||||
|
template := &core.Template{
|
||||||
|
Name: "my_template.jsonnet",
|
||||||
|
Data: string(before),
|
||||||
|
}
|
||||||
|
|
||||||
|
templateData := map[string]interface{}{
|
||||||
|
"stepName": "my_step",
|
||||||
|
"image": "my_image",
|
||||||
|
"commands": "my_command",
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Config.Data = string(before)
|
||||||
|
|
||||||
|
parsedFile, err := Parse(req, template, templateData)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if want, got := parsedFile, string(after); want != got {
|
||||||
|
t.Errorf("Want %q got %q", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseJsonnetNotTemplateFile(t *testing.T) {
|
||||||
|
before, err := ioutil.ReadFile("../testdata/single.jsonnet")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
after, err := ioutil.ReadFile("../testdata/input.jsonnet.golden")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &core.ConvertArgs{
|
||||||
|
Build: &core.Build{
|
||||||
|
After: "3d21ec53a331a6f037a91c368710b99387d012c1",
|
||||||
|
},
|
||||||
|
Repo: &core.Repository{
|
||||||
|
Slug: "octocat/hello-world",
|
||||||
|
Config: ".drone.jsonnet",
|
||||||
|
},
|
||||||
|
Config: &core.Config{},
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Repo.Config = "plugin.jsonnet"
|
||||||
|
req.Config.Data = string(before)
|
||||||
|
|
||||||
|
parsedFile, err := Parse(req, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if want, got := parsedFile, string(after); want != got {
|
||||||
|
t.Errorf("Want %q got %q", want, got)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,13 +17,13 @@ package converter
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/drone/drone/core"
|
|
||||||
"github.com/drone/drone/plugin/converter/starlark"
|
|
||||||
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/drone/drone/core"
|
||||||
|
"github.com/drone/drone/plugin/converter/jsonnet"
|
||||||
|
"github.com/drone/drone/plugin/converter/starlark"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
|
||||||
if template == nil {
|
if template == nil {
|
||||||
return nil, ErrTemplateNotFound
|
return nil, ErrTemplateNotFound
|
||||||
}
|
}
|
||||||
// Check if file is Starlark
|
// Check if file is of type Starlark
|
||||||
if strings.HasSuffix(templateArgs.Load, ".script") ||
|
if strings.HasSuffix(templateArgs.Load, ".script") ||
|
||||||
strings.HasSuffix(templateArgs.Load, ".star") ||
|
strings.HasSuffix(templateArgs.Load, ".star") ||
|
||||||
strings.HasSuffix(templateArgs.Load, ".starlark") {
|
strings.HasSuffix(templateArgs.Load, ".starlark") {
|
||||||
|
@ -80,5 +80,16 @@ func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
|
||||||
Data: file,
|
Data: file,
|
||||||
}, nil
|
}, 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTemplatePluginConvert(t *testing.T) {
|
func TestTemplatePluginConvertStarlark(t *testing.T) {
|
||||||
templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml")
|
templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -175,3 +175,65 @@ func TestTemplatePluginConvertTemplateNotFound(t *testing.T) {
|
||||||
t.Errorf("template converter: template name given not found")
|
t.Errorf("template converter: template name given not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTemplatePluginConvertJsonnet(t *testing.T) {
|
||||||
|
templateArgs, err := ioutil.ReadFile("testdata/jsonnet.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/input.jsonnet")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
after, err := ioutil.ReadFile("testdata/input.jsonnet.golden")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
template := &core.Template{
|
||||||
|
Name: "plugin.jsonnet",
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
6
plugin/converter/testdata/input.jsonnet
vendored
6
plugin/converter/testdata/input.jsonnet
vendored
|
@ -1,6 +1,6 @@
|
||||||
local stepName = std.extVar("input.my_step");
|
local stepName = std.extVar("input.stepName");
|
||||||
local image = std.extVar("input.my_image");
|
local image = std.extVar("input.image");
|
||||||
local commands = std.extVar("input.my_command");
|
local commands = std.extVar("input.commands");
|
||||||
|
|
||||||
{
|
{
|
||||||
"kind": "pipeline",
|
"kind": "pipeline",
|
||||||
|
|
15
plugin/converter/testdata/input.jsonnet.golden
vendored
Normal file
15
plugin/converter/testdata/input.jsonnet.golden
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"kind": "pipeline",
|
||||||
|
"name": "default",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"commands": [
|
||||||
|
"my_command"
|
||||||
|
],
|
||||||
|
"image": "my_image",
|
||||||
|
"name": "my_step"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "docker"
|
||||||
|
}
|
14
plugin/converter/testdata/single.jsonnet
vendored
Normal file
14
plugin/converter/testdata/single.jsonnet
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"kind": "pipeline",
|
||||||
|
"name": "default",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"commands": [
|
||||||
|
"my_command"
|
||||||
|
],
|
||||||
|
"image": "my_image",
|
||||||
|
"name": "my_step"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "docker"
|
||||||
|
}
|
Loading…
Reference in a new issue