harness-drone/plugin/converter/template.go

132 lines
3.7 KiB
Go

// Copyright 2019 Drone IO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !oss
package converter
import (
"bytes"
"context"
"database/sql"
"errors"
"path/filepath"
"regexp"
"strings"
templating "text/template"
"github.com/drone/drone/core"
"github.com/drone/drone/plugin/converter/jsonnet"
"github.com/drone/drone/plugin/converter/starlark"
"github.com/drone/funcmap"
"gopkg.in/yaml.v2"
)
var (
// templateFileRE regex to verifying kind is template.
templateFileRE = regexp.MustCompilePOSIX("^kind:[[:space:]]+template[[:space:]]?+$")
errTemplateNotFound = errors.New("template converter: template name given not found")
errTemplateSyntaxErrors = errors.New("template converter: there is a problem with the yaml file provided")
errTemplateExtensionInvalid = errors.New("template extension invalid. must be yaml, starlark or jsonnet")
)
func Template(templateStore core.TemplateStore, stepLimit uint64) core.ConvertService {
return &templatePlugin{
templateStore: templateStore,
stepLimit: stepLimit,
}
}
type templatePlugin struct {
templateStore core.TemplateStore
stepLimit uint64
}
func (p *templatePlugin) Convert(ctx context.Context, req *core.ConvertArgs) (*core.Config, error) {
// check type is yaml
if strings.HasSuffix(req.Repo.Config, ".yml") == false {
return nil, nil
}
// check kind is template
if templateFileRE.MatchString(req.Config.Data) == false {
return nil, nil
}
// map to templateArgs
var templateArgs core.TemplateArgs
err := yaml.Unmarshal([]byte(req.Config.Data), &templateArgs)
if err != nil {
return nil, errTemplateSyntaxErrors
}
// get template from db
template, err := p.templateStore.FindName(ctx, templateArgs.Load, req.Repo.Namespace)
if err == sql.ErrNoRows {
return nil, errTemplateNotFound
}
if err != nil {
return nil, err
}
switch filepath.Ext(templateArgs.Load) {
case ".yml", ".yaml":
return parseYaml(req, template, templateArgs)
case ".star", ".starlark", ".script":
return parseStarlark(req, template, templateArgs, p.stepLimit)
case ".jsonnet":
return parseJsonnet(req, template, templateArgs)
default:
return nil, errTemplateExtensionInvalid
}
}
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, nil, 0, 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, stepLimit uint64) (*core.Config, error) {
file, err := starlark.Parse(req, template, templateArgs.Data, stepLimit)
if err != nil {
return nil, err
}
return &core.Config{
Data: file,
}, nil
}