diff --git a/plugin/converter/jsonnet.go b/plugin/converter/jsonnet.go index 35020938..40d5b69d 100644 --- a/plugin/converter/jsonnet.go +++ b/plugin/converter/jsonnet.go @@ -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) + return nil, err } - - // 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 } diff --git a/plugin/converter/jsonnet/jsonnet.go b/plugin/converter/jsonnet/jsonnet.go new file mode 100644 index 00000000..57dc478c --- /dev/null +++ b/plugin/converter/jsonnet/jsonnet.go @@ -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 +} diff --git a/plugin/converter/jsonnet/jsonnet_test.go b/plugin/converter/jsonnet/jsonnet_test.go new file mode 100644 index 00000000..3d944abc --- /dev/null +++ b/plugin/converter/jsonnet/jsonnet_test.go @@ -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) + } +} diff --git a/plugin/converter/template.go b/plugin/converter/template.go index d84fee54..1f0cbc16 100644 --- a/plugin/converter/template.go +++ b/plugin/converter/template.go @@ -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 } diff --git a/plugin/converter/template_test.go b/plugin/converter/template_test.go index aa2abe79..1b742166 100644 --- a/plugin/converter/template_test.go +++ b/plugin/converter/template_test.go @@ -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) + } +} diff --git a/plugin/converter/testdata/input.jsonnet b/plugin/converter/testdata/input.jsonnet index 0fd935a6..25f58635 100644 --- a/plugin/converter/testdata/input.jsonnet +++ b/plugin/converter/testdata/input.jsonnet @@ -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", diff --git a/plugin/converter/testdata/input.jsonnet.golden b/plugin/converter/testdata/input.jsonnet.golden new file mode 100644 index 00000000..ba8143b7 --- /dev/null +++ b/plugin/converter/testdata/input.jsonnet.golden @@ -0,0 +1,15 @@ +--- +{ + "kind": "pipeline", + "name": "default", + "steps": [ + { + "commands": [ + "my_command" + ], + "image": "my_image", + "name": "my_step" + } + ], + "type": "docker" +} diff --git a/plugin/converter/testdata/single.jsonnet b/plugin/converter/testdata/single.jsonnet new file mode 100644 index 00000000..8640885f --- /dev/null +++ b/plugin/converter/testdata/single.jsonnet @@ -0,0 +1,14 @@ +{ + "kind": "pipeline", + "name": "default", + "steps": [ + { + "commands": [ + "my_command" + ], + "image": "my_image", + "name": "my_step" + } + ], + "type": "docker" +}