add jsonnet support for build templates
This commit is contained in:
parent
7278dd61f9
commit
65e5edddc3
8 changed files with 262 additions and 31 deletions
|
@ -7,13 +7,11 @@
|
|||
package converter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
|
||||
"github.com/google/go-jsonnet"
|
||||
"github.com/drone/drone/plugin/converter/jsonnet"
|
||||
)
|
||||
|
||||
// TODO(bradrydzewski) handle jsonnet imports
|
||||
|
@ -42,32 +40,12 @@ func (p *jsonnetPlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*co
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// create the jsonnet vm
|
||||
vm := jsonnet.MakeVM()
|
||||
vm.MaxStack = 500
|
||||
vm.StringOutput = false
|
||||
vm.ErrorFormatter.SetMaxStackTraceSize(20)
|
||||
file, err := jsonnet.Parse(req, nil, nil)
|
||||
|
||||
// convert the jsonnet file to yaml
|
||||
buf := new(bytes.Buffer)
|
||||
docs, err := vm.EvaluateSnippetStream(req.Repo.Config, req.Config.Data)
|
||||
if err != nil {
|
||||
doc, err2 := vm.EvaluateSnippet(req.Repo.Config, req.Config.Data)
|
||||
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{
|
||||
Data: buf.String(),
|
||||
Data: file,
|
||||
}, nil
|
||||
}
|
||||
|
|
57
plugin/converter/jsonnet/jsonnet.go
Normal file
57
plugin/converter/jsonnet/jsonnet.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package jsonnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"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 {
|
||||
if s, ok := v.(string); ok {
|
||||
key := "input." + k
|
||||
vm.ExtVar(key, s)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
93
plugin/converter/jsonnet/jsonnet_test.go
Normal file
93
plugin/converter/jsonnet/jsonnet_test.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package jsonnet
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/core"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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,6 +17,7 @@ package converter
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/drone/drone/plugin/converter/jsonnet"
|
||||
|
||||
"github.com/drone/drone/core"
|
||||
"github.com/drone/drone/plugin/converter/starlark"
|
||||
|
@ -67,7 +68,7 @@ func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
|
|||
if template == nil {
|
||||
return nil, ErrTemplateNotFound
|
||||
}
|
||||
// Check if file is Starlark
|
||||
// Check if file is of type Starlark
|
||||
if strings.HasSuffix(templateArgs.Load, ".script") ||
|
||||
strings.HasSuffix(templateArgs.Load, ".star") ||
|
||||
strings.HasSuffix(templateArgs.Load, ".starlark") {
|
||||
|
@ -80,5 +81,16 @@ func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*c
|
|||
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
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
"github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
func TestTemplatePluginConvert(t *testing.T) {
|
||||
func TestTemplatePluginConvertStarlark(t *testing.T) {
|
||||
templateArgs, err := ioutil.ReadFile("testdata/starlark.template.yml")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
@ -175,3 +175,65 @@ func TestTemplatePluginConvertTemplateNotFound(t *testing.T) {
|
|||
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 image = std.extVar("input.my_image");
|
||||
local commands = std.extVar("input.my_command");
|
||||
local stepName = std.extVar("input.stepName");
|
||||
local image = std.extVar("input.image");
|
||||
local commands = std.extVar("input.commands");
|
||||
|
||||
{
|
||||
"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