mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-23 18:31:00 +00:00
removed yaml functions that were pushed down to the build layer
This commit is contained in:
parent
ba47948fc9
commit
cf953f19d3
8 changed files with 62 additions and 1134 deletions
|
@ -1,74 +1,74 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/rpc"
|
||||
// import (
|
||||
// "net"
|
||||
// "net/http"
|
||||
// "net/rpc"
|
||||
|
||||
common "github.com/drone/drone/pkg/types"
|
||||
)
|
||||
// common "github.com/drone/drone/pkg/types"
|
||||
// )
|
||||
|
||||
// Client communicates with a Remote plugin using the
|
||||
// net/rpc protocol.
|
||||
type Client struct {
|
||||
*rpc.Client
|
||||
}
|
||||
// // Client communicates with a Remote plugin using the
|
||||
// // net/rpc protocol.
|
||||
// type Client struct {
|
||||
// *rpc.Client
|
||||
// }
|
||||
|
||||
// New returns a new, remote datastore backend that connects
|
||||
// via tcp and exchanges data using Go's RPC mechanism.
|
||||
func New(conf *config.Config) (*Client, error) {
|
||||
// conn, err := net.Dial("tcp", conf.Server.Addr)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// client := &Client{
|
||||
// rpc.NewClient(conn),
|
||||
// }
|
||||
// return client, nil
|
||||
return nil, nil
|
||||
}
|
||||
// // New returns a new, remote datastore backend that connects
|
||||
// // via tcp and exchanges data using Go's RPC mechanism.
|
||||
// func New(conf *config.Config) (*Client, error) {
|
||||
// // conn, err := net.Dial("tcp", conf.Server.Addr)
|
||||
// // if err != nil {
|
||||
// // return nil, err
|
||||
// // }
|
||||
// // client := &Client{
|
||||
// // rpc.NewClient(conn),
|
||||
// // }
|
||||
// // return client, nil
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
func (c *Client) Login(token, secret string) (*common.User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
// func (c *Client) Login(token, secret string) (*common.User, error) {
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
// Repo fetches the named repository from the remote system.
|
||||
func (c *Client) Repo(u *common.User, owner, repo string) (*common.Repo, error) {
|
||||
return nil, nil
|
||||
}
|
||||
// // Repo fetches the named repository from the remote system.
|
||||
// func (c *Client) Repo(u *common.User, owner, repo string) (*common.Repo, error) {
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
func (c *Client) Perm(u *common.User, owner, repo string) (*common.Perm, error) {
|
||||
return nil, nil
|
||||
}
|
||||
// func (c *Client) Perm(u *common.User, owner, repo string) (*common.Perm, error) {
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
func (c *Client) Script(u *common.User, r *common.Repo, b *common.Build) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
// func (c *Client) Script(u *common.User, r *common.Repo, b *common.Build) ([]byte, error) {
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
func (c *Client) Status(u *common.User, r *common.Repo, b *common.Build, link string) error {
|
||||
return nil
|
||||
}
|
||||
// func (c *Client) Status(u *common.User, r *common.Repo, b *common.Build, link string) error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (c *Client) Activate(u *common.User, r *common.Repo, k *common.Keypair, link string) error {
|
||||
return nil
|
||||
}
|
||||
// func (c *Client) Activate(u *common.User, r *common.Repo, k *common.Keypair, link string) error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (c *Client) Deactivate(u *common.User, r *common.Repo, link string) error {
|
||||
return nil
|
||||
}
|
||||
// func (c *Client) Deactivate(u *common.User, r *common.Repo, link string) error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (c *Client) Hook(r *http.Request) (*common.Hook, error) {
|
||||
hook := new(common.Hook)
|
||||
header := make(http.Header)
|
||||
copyHeader(r.Header, header)
|
||||
// func (c *Client) Hook(r *http.Request) (*common.Hook, error) {
|
||||
// hook := new(common.Hook)
|
||||
// header := make(http.Header)
|
||||
// copyHeader(r.Header, header)
|
||||
|
||||
return hook, nil
|
||||
}
|
||||
// return hook, nil
|
||||
// }
|
||||
|
||||
func copyHeader(dst, src http.Header) {
|
||||
for k, vv := range src {
|
||||
for _, v := range vv {
|
||||
dst.Add(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
// func copyHeader(dst, src http.Header) {
|
||||
// for k, vv := range src {
|
||||
// for _, v := range vv {
|
||||
// dst.Add(k, v)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
package inject
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2"
|
||||
"github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
// Inject injects a map of parameters into a raw string and returns
|
||||
// the resulting string.
|
||||
//
|
||||
// Parameters are represented in the string using $$ notation, similar
|
||||
// to how environment variables are defined in Makefiles.
|
||||
func Inject(raw string, params map[string]string) string {
|
||||
if params == nil {
|
||||
return raw
|
||||
}
|
||||
keys := []string{}
|
||||
for k := range params {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(keys)))
|
||||
injected := raw
|
||||
for _, k := range keys {
|
||||
v := params[k]
|
||||
injected = strings.Replace(injected, "$$"+k, v, -1)
|
||||
}
|
||||
return injected
|
||||
}
|
||||
|
||||
// InjectSafe attempts to safely inject parameters without leaking
|
||||
// parameters in the Build or Compose section of the yaml file.
|
||||
//
|
||||
// The intended use case for this function are public pull requests.
|
||||
// We want to avoid a malicious pull request that allows someone
|
||||
// to inject and print private variables.
|
||||
func InjectSafe(raw string, params map[string]string) string {
|
||||
before, _ := parse(raw)
|
||||
after, _ := parse(Inject(raw, params))
|
||||
before.Notify = after.Notify
|
||||
before.Publish = after.Publish
|
||||
before.Deploy = after.Deploy
|
||||
result, _ := yaml.Marshal(before)
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// helper funtion to parse a yaml configuration file.
|
||||
func parse(raw string) (*types.Config, error) {
|
||||
cfg := types.Config{}
|
||||
err := yaml.Unmarshal([]byte(raw), &cfg)
|
||||
return &cfg, err
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package inject
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
|
||||
)
|
||||
|
||||
func Test_Inject(t *testing.T) {
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Inject params", func() {
|
||||
|
||||
g.It("Should replace vars with $$", func() {
|
||||
s := "echo $$FOO $BAR"
|
||||
m := map[string]string{}
|
||||
m["FOO"] = "BAZ"
|
||||
g.Assert("echo BAZ $BAR").Equal(Inject(s, m))
|
||||
})
|
||||
|
||||
g.It("Should not replace vars with single $", func() {
|
||||
s := "echo $FOO $BAR"
|
||||
m := map[string]string{}
|
||||
m["FOO"] = "BAZ"
|
||||
g.Assert(s).Equal(Inject(s, m))
|
||||
})
|
||||
|
||||
g.It("Should not replace vars in nil map", func() {
|
||||
s := "echo $$FOO $BAR"
|
||||
g.Assert(s).Equal(Inject(s, nil))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_InjectSafe(t *testing.T) {
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Safely Inject params", func() {
|
||||
|
||||
m := map[string]string{}
|
||||
m["TOKEN"] = "FOO"
|
||||
m["SECRET"] = "BAR"
|
||||
c, _ := parse(InjectSafe(yml, m))
|
||||
|
||||
g.It("Should replace vars in notify section", func() {
|
||||
g.Assert(c.Deploy["digital_ocean"].Config["token"]).Equal("FOO")
|
||||
g.Assert(c.Deploy["digital_ocean"].Config["secret"]).Equal("BAR")
|
||||
})
|
||||
|
||||
g.It("Should not replace vars in script section", func() {
|
||||
g.Assert(c.Build.Config["commands"].([]interface{})[0]).Equal("echo $$TOKEN")
|
||||
g.Assert(c.Build.Config["commands"].([]interface{})[1]).Equal("echo $$SECRET")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
var yml = `
|
||||
build:
|
||||
image: foo
|
||||
commands:
|
||||
- echo $$TOKEN
|
||||
- echo $$SECRET
|
||||
deploy:
|
||||
digital_ocean:
|
||||
token: $$TOKEN
|
||||
secret: $$SECRET
|
||||
`
|
142
pkg/yaml/lint.go
142
pkg/yaml/lint.go
|
@ -1,142 +0,0 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
common "github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
// lintRule defines a function that runs lint
|
||||
// checks against a Yaml Config file. If the rule
|
||||
// fails it should return an error message.
|
||||
type lintRule func(*common.Config) error
|
||||
|
||||
var lintRules = []lintRule{
|
||||
expectBuild,
|
||||
expectImage,
|
||||
expectCommand,
|
||||
expectCloneInWorkspace,
|
||||
expectCacheInWorkspace,
|
||||
}
|
||||
|
||||
// Lint runs all lint rules against the Yaml Config.
|
||||
func Lint(c *common.Config) error {
|
||||
for _, rule := range lintRules {
|
||||
err := rule(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// lint rule that fails when no build is defined
|
||||
func expectBuild(c *common.Config) error {
|
||||
if c.Build == nil {
|
||||
return fmt.Errorf("Yaml must define a build section")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// lint rule that fails when no build image is defined
|
||||
func expectImage(c *common.Config) error {
|
||||
if len(c.Build.Image) == 0 {
|
||||
return fmt.Errorf("Yaml must define a build image")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// lint rule that fails when no build commands are defined
|
||||
func expectCommand(c *common.Config) error {
|
||||
if c.Setup.Config == nil || c.Setup.Config["commands"] == nil {
|
||||
return fmt.Errorf("Yaml must define build / setup commands")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// lint rule that fails if the clone directory is not contained
|
||||
// in the root workspace.
|
||||
func expectCloneInWorkspace(c *common.Config) error {
|
||||
pathv, ok := c.Clone.Config["path"]
|
||||
var path string
|
||||
|
||||
if ok {
|
||||
path, _ = pathv.(string)
|
||||
}
|
||||
if len(path) == 0 {
|
||||
// This should only happen if the transformer was not run
|
||||
return fmt.Errorf("No workspace specified")
|
||||
}
|
||||
|
||||
relative, relOk := filepath.Rel("/drone/src", path)
|
||||
if relOk != nil {
|
||||
return fmt.Errorf("Path is not relative to root")
|
||||
}
|
||||
|
||||
cleaned := filepath.Clean(relative)
|
||||
if strings.Index(cleaned, "../") != -1 {
|
||||
return fmt.Errorf("Cannot clone above the root")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// lint rule that fails if the cache directories are not contained
|
||||
// in the workspace.
|
||||
func expectCacheInWorkspace(c *common.Config) error {
|
||||
for _, step := range c.Build.Cache {
|
||||
if strings.Index(step, ":") != -1 {
|
||||
return fmt.Errorf("Cache cannot contain : in the path")
|
||||
}
|
||||
|
||||
cleaned := filepath.Clean(step)
|
||||
|
||||
if strings.Index(cleaned, "../") != -1 {
|
||||
return fmt.Errorf("Cache must point to a path in the workspace")
|
||||
} else if cleaned == "." {
|
||||
return fmt.Errorf("Cannot cache the workspace")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LintPlugins(c *common.Config, opts *Opts) error {
|
||||
if len(opts.Whitelist) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var images []string
|
||||
images = append(images, c.Setup.Image)
|
||||
images = append(images, c.Clone.Image)
|
||||
for _, step := range c.Publish {
|
||||
images = append(images, step.Image)
|
||||
}
|
||||
for _, step := range c.Deploy {
|
||||
images = append(images, step.Image)
|
||||
}
|
||||
for _, step := range c.Notify {
|
||||
images = append(images, step.Image)
|
||||
}
|
||||
|
||||
for _, image := range images {
|
||||
match := false
|
||||
for _, pattern := range opts.Whitelist {
|
||||
if pattern == image {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
ok, err := filepath.Match(pattern, image)
|
||||
if ok && err == nil {
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
return fmt.Errorf("Cannot use un-trusted image %s", image)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,177 +0,0 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
|
||||
common "github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
func Test_Linter(t *testing.T) {
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Linter", func() {
|
||||
|
||||
g.It("Should fail when nil build", func() {
|
||||
c := &common.Config{}
|
||||
g.Assert(expectBuild(c) != nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should fail when no image", func() {
|
||||
c := &common.Config{
|
||||
Build: &common.Step{},
|
||||
}
|
||||
g.Assert(expectImage(c) != nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should fail when no commands", func() {
|
||||
c := &common.Config{
|
||||
Setup: &common.Step{},
|
||||
}
|
||||
g.Assert(expectCommand(c) != nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should pass when proper Build provided", func() {
|
||||
c := &common.Config{
|
||||
Build: &common.Step{
|
||||
Config: map[string]interface{}{
|
||||
"commands": []string{"echo hi"},
|
||||
},
|
||||
},
|
||||
}
|
||||
g.Assert(expectImage(c) != nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should pass linter when build properly setup", func() {
|
||||
c := &common.Config{}
|
||||
c.Build = &common.Step{}
|
||||
c.Build.Image = "golang"
|
||||
c.Setup = &common.Step{}
|
||||
c.Setup.Config = map[string]interface{}{}
|
||||
c.Setup.Config["commands"] = []string{"go build", "go test"}
|
||||
c.Clone = &common.Step{}
|
||||
c.Clone.Config = map[string]interface{}{}
|
||||
c.Clone.Config["path"] = "/drone/src/foo/bar"
|
||||
c.Publish = map[string]*common.Step{}
|
||||
c.Publish["docker"] = &common.Step{Image: "docker"}
|
||||
c.Deploy = map[string]*common.Step{}
|
||||
c.Deploy["kubernetes"] = &common.Step{Image: "kubernetes"}
|
||||
c.Notify = map[string]*common.Step{}
|
||||
c.Notify["email"] = &common.Step{Image: "email"}
|
||||
g.Assert(Lint(c) == nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should pass with clone path inside workspace", func() {
|
||||
c := &common.Config{
|
||||
Clone: &common.Step{
|
||||
Config: map[string]interface{}{
|
||||
"path": "/drone/src/foo/bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
g.Assert(expectCloneInWorkspace(c) == nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should fail with clone path outside workspace", func() {
|
||||
c := &common.Config{
|
||||
Clone: &common.Step{
|
||||
Config: map[string]interface{}{
|
||||
"path": "/foo/bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
g.Assert(expectCloneInWorkspace(c) != nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should pass with cache path inside workspace", func() {
|
||||
c := &common.Config{
|
||||
Build: &common.Step{
|
||||
Cache: []string{".git", "/.git", "/.git/../.git/../.git"},
|
||||
},
|
||||
}
|
||||
g.Assert(expectCacheInWorkspace(c) == nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should fail with cache path outside workspace", func() {
|
||||
c := &common.Config{
|
||||
Build: &common.Step{
|
||||
Cache: []string{".git", "/.git", "../../.git"},
|
||||
},
|
||||
}
|
||||
g.Assert(expectCacheInWorkspace(c) != nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should fail when caching workspace directory", func() {
|
||||
c := &common.Config{
|
||||
Build: &common.Step{
|
||||
Cache: []string{".git", ".git/../"},
|
||||
},
|
||||
}
|
||||
g.Assert(expectCacheInWorkspace(c) != nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should fail when : is in the cache path", func() {
|
||||
c := &common.Config{
|
||||
Build: &common.Step{
|
||||
Cache: []string{".git", ".git:/../"},
|
||||
},
|
||||
}
|
||||
g.Assert(expectCacheInWorkspace(c) != nil).IsTrue()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_LintPlugins(t *testing.T) {
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Plugin Linter", func() {
|
||||
|
||||
g.It("Should fail un-trusted plugin", func() {
|
||||
c := &common.Config{
|
||||
Setup: &common.Step{Image: "foo/baz"},
|
||||
Clone: &common.Step{Image: "foo/bar"},
|
||||
Notify: map[string]*common.Step{},
|
||||
Deploy: map[string]*common.Step{},
|
||||
Publish: map[string]*common.Step{},
|
||||
}
|
||||
o := &Opts{Whitelist: []string{"plugins/*"}}
|
||||
g.Assert(LintPlugins(c, o) != nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should pass when empty whitelist", func() {
|
||||
c := &common.Config{
|
||||
Setup: &common.Step{Image: "foo/baz"},
|
||||
Clone: &common.Step{Image: "foo/bar"},
|
||||
Notify: map[string]*common.Step{},
|
||||
Deploy: map[string]*common.Step{},
|
||||
Publish: map[string]*common.Step{},
|
||||
}
|
||||
o := &Opts{Whitelist: []string{}}
|
||||
g.Assert(LintPlugins(c, o) == nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should pass wildcard", func() {
|
||||
c := &common.Config{
|
||||
Setup: &common.Step{Image: "plugins/drone-setup"},
|
||||
Clone: &common.Step{Image: "plugins/drone-build"},
|
||||
Notify: map[string]*common.Step{},
|
||||
Deploy: map[string]*common.Step{},
|
||||
Publish: map[string]*common.Step{},
|
||||
}
|
||||
o := &Opts{Whitelist: []string{"plugins/*"}}
|
||||
g.Assert(LintPlugins(c, o) == nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should pass itemized", func() {
|
||||
c := &common.Config{
|
||||
Setup: &common.Step{Image: "plugins/drone-setup"},
|
||||
Clone: &common.Step{Image: "plugins/drone-build"},
|
||||
Notify: map[string]*common.Step{},
|
||||
Deploy: map[string]*common.Step{},
|
||||
Publish: map[string]*common.Step{},
|
||||
}
|
||||
o := &Opts{Whitelist: []string{"plugins/drone-setup", "plugins/drone-build"}}
|
||||
g.Assert(LintPlugins(c, o) == nil).IsTrue()
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,112 +1,14 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
common "github.com/drone/drone/pkg/types"
|
||||
"github.com/drone/drone/pkg/yaml/inject"
|
||||
"github.com/drone/drone/pkg/yaml/matrix"
|
||||
"github.com/drone/drone/pkg/yaml/transform"
|
||||
"github.com/drone/drone/pkg/types"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Opts specifies parser options that will permit
|
||||
// or deny certain Yaml settings.
|
||||
type Opts struct {
|
||||
Volumes bool
|
||||
Network bool
|
||||
Privileged bool
|
||||
Caching bool
|
||||
Whitelist []string
|
||||
}
|
||||
|
||||
var DefaultOpts = &Opts{
|
||||
Volumes: false,
|
||||
Network: false,
|
||||
Privileged: false,
|
||||
Caching: true,
|
||||
Whitelist: []string{"plugins/*"},
|
||||
}
|
||||
|
||||
// Parse parses a build matrix and returns
|
||||
// a list of build configurations for each axis
|
||||
// using the default parsing options.
|
||||
func Parse(raw string, r *common.Repo) ([]*common.Config, error) {
|
||||
return ParseOpts(raw, DefaultOpts, r)
|
||||
}
|
||||
|
||||
// ParseOpts parses a build matrix and returns
|
||||
// a list of build configurations for each axis
|
||||
// using the provided parsing options.
|
||||
func ParseOpts(raw string, opts *Opts, r *common.Repo) ([]*common.Config, error) {
|
||||
axis, err := matrix.Parse(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
confs := []*common.Config{}
|
||||
|
||||
// when no matrix values exist we should return
|
||||
// a single config value with an empty label.
|
||||
if len(axis) == 0 {
|
||||
conf, err := ParseSingle(raw, opts, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
confs = append(confs, conf)
|
||||
}
|
||||
|
||||
for _, ax := range axis {
|
||||
// inject the matrix values into the raw script
|
||||
injected := inject.Inject(raw, ax)
|
||||
conf, err := ParseSingle(injected, opts, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf.Axis = common.Axis(ax)
|
||||
confs = append(confs, conf)
|
||||
}
|
||||
|
||||
return confs, nil
|
||||
}
|
||||
|
||||
// helper funtion to parse a yaml configuration file.
|
||||
func ParseSingle(raw string, opts *Opts, r *common.Repo) (*common.Config, error) {
|
||||
conf := &common.Config{}
|
||||
err := yaml.Unmarshal([]byte(raw), conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// apply rules / transforms
|
||||
transform.Defaults(conf)
|
||||
if !opts.Network {
|
||||
transform.RemoveNetwork(conf)
|
||||
}
|
||||
if !opts.Volumes {
|
||||
transform.RemoveVolumes(conf)
|
||||
}
|
||||
if !opts.Privileged {
|
||||
transform.RemovePrivileged(conf)
|
||||
}
|
||||
transform.Repo(conf, r)
|
||||
if !opts.Caching {
|
||||
transform.RemoveVolumes(conf)
|
||||
}
|
||||
|
||||
// lint the yaml file
|
||||
err = Lint(conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = LintPlugins(conf, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conf, err
|
||||
}
|
||||
|
||||
func ParseCondition(raw string) (*common.Condition, error) {
|
||||
func ParseCondition(raw string) (*types.Condition, error) {
|
||||
c := struct {
|
||||
Condition *common.Condition `yaml:"when"`
|
||||
Condition *types.Condition `yaml:"when"`
|
||||
}{}
|
||||
err := yaml.Unmarshal([]byte(raw), c)
|
||||
return c.Condition, err
|
||||
|
|
|
@ -1,314 +0,0 @@
|
|||
package transform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
common "github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
// buildRoot is the root build directory.
|
||||
//
|
||||
// If this changes then the matching value in lint.go needs
|
||||
// to be modified as well.
|
||||
const buildRoot = "/drone/src"
|
||||
|
||||
// transformRule applies a check or transformation rule
|
||||
// to the build configuration.
|
||||
type transformRule func(*common.Config)
|
||||
|
||||
var transformRules = []transformRule{
|
||||
transformSetup,
|
||||
transformClone,
|
||||
transformBuild,
|
||||
transformImages,
|
||||
transformDockerPlugin,
|
||||
}
|
||||
|
||||
var rmPrivilegedRules = []transformRule{
|
||||
rmPrivileged,
|
||||
rmVolumes,
|
||||
rmNetwork,
|
||||
}
|
||||
|
||||
// Default executes the default transformers that
|
||||
// ensure the minimal Yaml configuration is in place
|
||||
// and correctly configured.
|
||||
func Defaults(c *common.Config) {
|
||||
for _, rule := range transformRules {
|
||||
rule(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Safe executes all transformers that remove privileged
|
||||
// options from the Yaml.
|
||||
func Safe(c *common.Config) {
|
||||
for _, rule := range rmPrivilegedRules {
|
||||
rule(c)
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveNetwork executes all transformers that remove
|
||||
// network options from the Yaml.
|
||||
func RemoveNetwork(c *common.Config) {
|
||||
rmNetwork(c)
|
||||
}
|
||||
|
||||
// TransformRemoveVolumes executes all transformers that
|
||||
// remove volume options from the Yaml.
|
||||
func RemoveVolumes(c *common.Config) {
|
||||
rmVolumes(c)
|
||||
}
|
||||
|
||||
// RemovePrivileged executes all transformers that remove
|
||||
// privileged options from the Yaml.
|
||||
func RemovePrivileged(c *common.Config) {
|
||||
rmPrivileged(c)
|
||||
}
|
||||
|
||||
// Repo executes all transformers that rely on repository
|
||||
// information.
|
||||
func Repo(c *common.Config, r *common.Repo) {
|
||||
transformWorkspace(c, r)
|
||||
transformCache(c, r)
|
||||
}
|
||||
|
||||
// transformSetup is a transformer that adds a default
|
||||
// setup step if none exists.
|
||||
func transformSetup(c *common.Config) {
|
||||
c.Setup = &common.Step{}
|
||||
c.Setup.Image = "plugins/drone-build"
|
||||
|
||||
// TODO move below code to separate transform
|
||||
if c.Build == nil {
|
||||
c.Build = &common.Step{}
|
||||
}
|
||||
if c.Build.Config == nil {
|
||||
c.Build.Config = map[string]interface{}{}
|
||||
}
|
||||
////
|
||||
|
||||
c.Setup.Config = c.Build.Config
|
||||
}
|
||||
|
||||
// transformClone is a transformer that adds a default
|
||||
// clone step if none exists.
|
||||
func transformClone(c *common.Config) {
|
||||
if c.Clone == nil {
|
||||
c.Clone = &common.Step{}
|
||||
}
|
||||
if len(c.Clone.Image) == 0 {
|
||||
c.Clone.Image = "plugins/drone-git"
|
||||
c.Clone.Volumes = nil
|
||||
c.Clone.NetworkMode = ""
|
||||
}
|
||||
if c.Clone.Config == nil {
|
||||
c.Clone.Config = map[string]interface{}{}
|
||||
c.Clone.Config["depth"] = 50
|
||||
c.Clone.Config["recursive"] = true
|
||||
}
|
||||
}
|
||||
|
||||
// transformBuild is a transformer that removes the
|
||||
// build configuration vargs. They should have
|
||||
// already been transferred to the Setup step.
|
||||
func transformBuild(c *common.Config) {
|
||||
c.Build.Config = nil
|
||||
c.Build.Entrypoint = []string{"/bin/bash", "-e"}
|
||||
c.Build.Command = []string{"/drone/bin/build.sh"}
|
||||
}
|
||||
|
||||
// transformImages is a transformer that ensures every
|
||||
// step has an image and uses a fully-qualified
|
||||
// image name.
|
||||
func transformImages(c *common.Config) {
|
||||
c.Setup.Image = imageName(c.Setup.Image)
|
||||
c.Clone.Image = imageName(c.Clone.Image)
|
||||
for name, step := range c.Publish {
|
||||
step.Image = imageNameDefault(step.Image, name)
|
||||
}
|
||||
for name, step := range c.Deploy {
|
||||
step.Image = imageNameDefault(step.Image, name)
|
||||
}
|
||||
for name, step := range c.Notify {
|
||||
step.Image = imageNameDefault(step.Image, name)
|
||||
}
|
||||
}
|
||||
|
||||
// transformDockerPlugin is a transformer that ensures the
|
||||
// official Docker plugin can run in privileged mode. It
|
||||
// will disable volumes and network mode for added protection.
|
||||
func transformDockerPlugin(c *common.Config) {
|
||||
for _, step := range c.Publish {
|
||||
if step.Image == "plugins/drone-docker" {
|
||||
step.Privileged = true
|
||||
step.Volumes = nil
|
||||
step.NetworkMode = ""
|
||||
step.Entrypoint = []string{}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rmPrivileged is a transformer that ensures every
|
||||
// step is executed in non-privileged mode.
|
||||
func rmPrivileged(c *common.Config) {
|
||||
c.Setup.Privileged = false
|
||||
c.Clone.Privileged = false
|
||||
c.Build.Privileged = false
|
||||
for _, step := range c.Publish {
|
||||
if step.Image == "plugins/drone-docker" {
|
||||
continue // the official docker plugin is the only exception here
|
||||
}
|
||||
step.Privileged = false
|
||||
}
|
||||
for _, step := range c.Deploy {
|
||||
step.Privileged = false
|
||||
}
|
||||
for _, step := range c.Notify {
|
||||
step.Privileged = false
|
||||
}
|
||||
for _, step := range c.Compose {
|
||||
step.Privileged = false
|
||||
}
|
||||
}
|
||||
|
||||
// rmVolumes is a transformer that ensures every
|
||||
// step is executed without volumes.
|
||||
func rmVolumes(c *common.Config) {
|
||||
c.Setup.Volumes = nil
|
||||
c.Clone.Volumes = nil
|
||||
c.Build.Volumes = nil
|
||||
for _, step := range c.Publish {
|
||||
step.Volumes = nil
|
||||
}
|
||||
for _, step := range c.Deploy {
|
||||
step.Volumes = nil
|
||||
}
|
||||
for _, step := range c.Notify {
|
||||
step.Volumes = nil
|
||||
}
|
||||
for _, step := range c.Compose {
|
||||
step.Volumes = nil
|
||||
}
|
||||
}
|
||||
|
||||
// rmNetwork is a transformer that ensures every
|
||||
// step is executed with default bridge networking.
|
||||
func rmNetwork(c *common.Config) {
|
||||
c.Setup.NetworkMode = ""
|
||||
c.Clone.NetworkMode = ""
|
||||
c.Build.NetworkMode = ""
|
||||
for _, step := range c.Publish {
|
||||
step.NetworkMode = ""
|
||||
}
|
||||
for _, step := range c.Deploy {
|
||||
step.NetworkMode = ""
|
||||
}
|
||||
for _, step := range c.Notify {
|
||||
step.NetworkMode = ""
|
||||
}
|
||||
for _, step := range c.Compose {
|
||||
step.NetworkMode = ""
|
||||
}
|
||||
}
|
||||
|
||||
// transformWorkspace is a transformer that adds the workspace
|
||||
// directory to the configuration based on the repository
|
||||
// information.
|
||||
func transformWorkspace(c *common.Config, r *common.Repo) {
|
||||
pathv, ok := c.Clone.Config["path"]
|
||||
var path string
|
||||
|
||||
if ok {
|
||||
path, _ = pathv.(string)
|
||||
}
|
||||
if len(path) == 0 {
|
||||
path = repoPath(r)
|
||||
}
|
||||
|
||||
c.Clone.Config["path"] = filepath.Join(buildRoot, path)
|
||||
}
|
||||
|
||||
// transformCache is a transformer that adds volumes
|
||||
// to the configuration based on the cache.
|
||||
func transformCache(c *common.Config, r *common.Repo) {
|
||||
cacheCount := len(c.Build.Cache)
|
||||
|
||||
if cacheCount == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
volumes := make([]string, cacheCount)
|
||||
|
||||
cache := cacheRoot(r)
|
||||
workspace := c.Clone.Config["path"].(string)
|
||||
|
||||
for i, dir := range c.Build.Cache {
|
||||
cacheDir := filepath.Join(cache, dir)
|
||||
workspaceDir := filepath.Join(workspace, dir)
|
||||
|
||||
volumes[i] = fmt.Sprintf("%s:%s", cacheDir, workspaceDir)
|
||||
}
|
||||
|
||||
c.Setup.Volumes = append(c.Setup.Volumes, volumes...)
|
||||
c.Clone.Volumes = append(c.Clone.Volumes, volumes...)
|
||||
c.Build.Volumes = append(c.Build.Volumes, volumes...)
|
||||
|
||||
for _, step := range c.Publish {
|
||||
step.Volumes = append(step.Volumes, volumes...)
|
||||
}
|
||||
for _, step := range c.Deploy {
|
||||
step.Volumes = append(step.Volumes, volumes...)
|
||||
}
|
||||
for _, step := range c.Notify {
|
||||
step.Volumes = append(step.Volumes, volumes...)
|
||||
}
|
||||
for _, step := range c.Compose {
|
||||
step.Volumes = append(step.Volumes, volumes...)
|
||||
}
|
||||
}
|
||||
|
||||
// imageName is a helper function that resolves the
|
||||
// image name. When using official drone plugins it
|
||||
// is possible to use an alias name. This converts to
|
||||
// the fully qualified name.
|
||||
func imageName(name string) string {
|
||||
if strings.Contains(name, "/") {
|
||||
return name
|
||||
}
|
||||
name = strings.Replace(name, "_", "-", -1)
|
||||
name = "plugins/drone-" + name
|
||||
return name
|
||||
}
|
||||
|
||||
// imageNameDefault is a helper function that resolves
|
||||
// the image name. If the image name is blank the
|
||||
// default name is used instead.
|
||||
func imageNameDefault(name, defaultName string) string {
|
||||
if len(name) == 0 {
|
||||
name = defaultName
|
||||
}
|
||||
return imageName(name)
|
||||
}
|
||||
|
||||
// workspaceRoot is a helper function that determines the
|
||||
// default workspace the build runs in.
|
||||
func workspaceRoot(r *common.Repo) string {
|
||||
return filepath.Join(buildRoot, repoPath(r))
|
||||
}
|
||||
|
||||
// cacheRoot is a helper function that deteremines the
|
||||
// default caching root.
|
||||
func cacheRoot(r *common.Repo) string {
|
||||
return filepath.Join("/tmp/drone/cache", repoPath(r))
|
||||
}
|
||||
|
||||
// repoPath is a helper function that creates a path based
|
||||
// on the host and repository name.
|
||||
func repoPath(r *common.Repo) string {
|
||||
parsed, _ := url.Parse(r.Link)
|
||||
return filepath.Join(parsed.Host, r.FullName)
|
||||
}
|
|
@ -1,220 +0,0 @@
|
|||
package transform
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
|
||||
common "github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
func Test_Transform(t *testing.T) {
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Transform", func() {
|
||||
|
||||
g.It("Should transform setup step", func() {
|
||||
c := &common.Config{}
|
||||
c.Build = &common.Step{}
|
||||
c.Build.Config = map[string]interface{}{}
|
||||
transformSetup(c)
|
||||
g.Assert(c.Setup != nil).IsTrue()
|
||||
g.Assert(c.Setup.Image).Equal("plugins/drone-build")
|
||||
g.Assert(c.Setup.Config).Equal(c.Build.Config)
|
||||
})
|
||||
|
||||
g.It("Should transform clone step", func() {
|
||||
c := &common.Config{}
|
||||
transformClone(c)
|
||||
g.Assert(c.Clone != nil).IsTrue()
|
||||
g.Assert(c.Clone.Image).Equal("plugins/drone-git")
|
||||
})
|
||||
|
||||
g.It("Should transform build", func() {
|
||||
c := &common.Config{}
|
||||
c.Build = &common.Step{}
|
||||
c.Build.Config = map[string]interface{}{}
|
||||
c.Build.Config["commands"] = []string{"echo hello"}
|
||||
transformBuild(c)
|
||||
g.Assert(len(c.Build.Config)).Equal(0)
|
||||
g.Assert(c.Build.Entrypoint[0]).Equal("/bin/bash")
|
||||
g.Assert(c.Build.Command[0]).Equal("/drone/bin/build.sh")
|
||||
})
|
||||
|
||||
g.It("Should transform images", func() {
|
||||
c := &common.Config{}
|
||||
c.Setup = &common.Step{Image: "foo"}
|
||||
c.Clone = &common.Step{Image: "foo/bar"}
|
||||
c.Build = &common.Step{Image: "golang"}
|
||||
c.Publish = map[string]*common.Step{"google_compute": &common.Step{}}
|
||||
c.Deploy = map[string]*common.Step{"amazon": &common.Step{}}
|
||||
c.Notify = map[string]*common.Step{"slack": &common.Step{}}
|
||||
transformImages(c)
|
||||
|
||||
g.Assert(c.Setup.Image).Equal("plugins/drone-foo")
|
||||
g.Assert(c.Clone.Image).Equal("foo/bar")
|
||||
g.Assert(c.Build.Image).Equal("golang")
|
||||
g.Assert(c.Publish["google_compute"].Image).Equal("plugins/drone-google-compute")
|
||||
g.Assert(c.Deploy["amazon"].Image).Equal("plugins/drone-amazon")
|
||||
g.Assert(c.Notify["slack"].Image).Equal("plugins/drone-slack")
|
||||
})
|
||||
|
||||
g.It("Should transform docker plugin", func() {
|
||||
c := &common.Config{}
|
||||
c.Publish = map[string]*common.Step{}
|
||||
c.Publish["docker"] = &common.Step{Image: "plugins/drone-docker"}
|
||||
transformDockerPlugin(c)
|
||||
g.Assert(c.Publish["docker"].Privileged).Equal(true)
|
||||
})
|
||||
|
||||
g.It("Should remove privileged flag", func() {
|
||||
c := &common.Config{}
|
||||
c.Setup = &common.Step{Privileged: true}
|
||||
c.Clone = &common.Step{Privileged: true}
|
||||
c.Build = &common.Step{Privileged: true}
|
||||
c.Compose = map[string]*common.Step{"postgres": &common.Step{Privileged: true}}
|
||||
c.Publish = map[string]*common.Step{"google": &common.Step{Privileged: true}}
|
||||
c.Deploy = map[string]*common.Step{"amazon": &common.Step{Privileged: true}}
|
||||
c.Notify = map[string]*common.Step{"slack": &common.Step{Privileged: true}}
|
||||
rmPrivileged(c)
|
||||
|
||||
g.Assert(c.Setup.Privileged).Equal(false)
|
||||
g.Assert(c.Clone.Privileged).Equal(false)
|
||||
g.Assert(c.Build.Privileged).Equal(false)
|
||||
g.Assert(c.Compose["postgres"].Privileged).Equal(false)
|
||||
g.Assert(c.Publish["google"].Privileged).Equal(false)
|
||||
g.Assert(c.Deploy["amazon"].Privileged).Equal(false)
|
||||
g.Assert(c.Notify["slack"].Privileged).Equal(false)
|
||||
})
|
||||
|
||||
g.It("Should not remove docker plugin privileged flag", func() {
|
||||
c := &common.Config{}
|
||||
c.Setup = &common.Step{}
|
||||
c.Clone = &common.Step{}
|
||||
c.Build = &common.Step{}
|
||||
c.Publish = map[string]*common.Step{}
|
||||
c.Publish["docker"] = &common.Step{Image: "plugins/drone-docker"}
|
||||
transformDockerPlugin(c)
|
||||
g.Assert(c.Publish["docker"].Privileged).Equal(true)
|
||||
})
|
||||
|
||||
g.It("Should remove volumes", func() {
|
||||
c := &common.Config{}
|
||||
c.Setup = &common.Step{Volumes: []string{"/:/tmp"}}
|
||||
c.Clone = &common.Step{Volumes: []string{"/:/tmp"}}
|
||||
c.Build = &common.Step{Volumes: []string{"/:/tmp"}}
|
||||
c.Compose = map[string]*common.Step{"postgres": &common.Step{Volumes: []string{"/:/tmp"}}}
|
||||
c.Publish = map[string]*common.Step{"google": &common.Step{Volumes: []string{"/:/tmp"}}}
|
||||
c.Deploy = map[string]*common.Step{"amazon": &common.Step{Volumes: []string{"/:/tmp"}}}
|
||||
c.Notify = map[string]*common.Step{"slack": &common.Step{Volumes: []string{"/:/tmp"}}}
|
||||
rmVolumes(c)
|
||||
|
||||
g.Assert(len(c.Setup.Volumes)).Equal(0)
|
||||
g.Assert(len(c.Clone.Volumes)).Equal(0)
|
||||
g.Assert(len(c.Build.Volumes)).Equal(0)
|
||||
g.Assert(len(c.Compose["postgres"].Volumes)).Equal(0)
|
||||
g.Assert(len(c.Publish["google"].Volumes)).Equal(0)
|
||||
g.Assert(len(c.Deploy["amazon"].Volumes)).Equal(0)
|
||||
g.Assert(len(c.Notify["slack"].Volumes)).Equal(0)
|
||||
})
|
||||
|
||||
g.It("Should remove network", func() {
|
||||
c := &common.Config{}
|
||||
c.Setup = &common.Step{NetworkMode: "host"}
|
||||
c.Clone = &common.Step{NetworkMode: "host"}
|
||||
c.Build = &common.Step{NetworkMode: "host"}
|
||||
c.Compose = map[string]*common.Step{"postgres": &common.Step{NetworkMode: "host"}}
|
||||
c.Publish = map[string]*common.Step{"google": &common.Step{NetworkMode: "host"}}
|
||||
c.Deploy = map[string]*common.Step{"amazon": &common.Step{NetworkMode: "host"}}
|
||||
c.Notify = map[string]*common.Step{"slack": &common.Step{NetworkMode: "host"}}
|
||||
rmNetwork(c)
|
||||
|
||||
g.Assert(c.Setup.NetworkMode).Equal("")
|
||||
g.Assert(c.Clone.NetworkMode).Equal("")
|
||||
g.Assert(c.Build.NetworkMode).Equal("")
|
||||
g.Assert(c.Compose["postgres"].NetworkMode).Equal("")
|
||||
g.Assert(c.Publish["google"].NetworkMode).Equal("")
|
||||
g.Assert(c.Deploy["amazon"].NetworkMode).Equal("")
|
||||
g.Assert(c.Notify["slack"].NetworkMode).Equal("")
|
||||
})
|
||||
|
||||
g.It("Should return full qualified image name", func() {
|
||||
g.Assert(imageName("microsoft/azure")).Equal("microsoft/azure")
|
||||
g.Assert(imageName("azure")).Equal("plugins/drone-azure")
|
||||
g.Assert(imageName("azure_storage")).Equal("plugins/drone-azure-storage")
|
||||
})
|
||||
|
||||
g.It("Should have cached volumes", func() {
|
||||
c := &common.Config{
|
||||
Setup: &common.Step{},
|
||||
Clone: &common.Step{
|
||||
Config: map[string]interface{}{},
|
||||
},
|
||||
Build: &common.Step{
|
||||
Cache: []string{".git", "foo", "bar"},
|
||||
},
|
||||
Notify: map[string]*common.Step{},
|
||||
Deploy: map[string]*common.Step{},
|
||||
Publish: map[string]*common.Step{},
|
||||
}
|
||||
r := &common.Repo{
|
||||
Link: "https://github.com/drone/drone",
|
||||
FullName: "drone/drone",
|
||||
}
|
||||
transformWorkspace(c, r)
|
||||
transformCache(c, r)
|
||||
|
||||
cacheCount := len(c.Build.Cache)
|
||||
|
||||
test := func(s *common.Step) {
|
||||
g.Assert(len(s.Volumes)).Equal(cacheCount)
|
||||
}
|
||||
|
||||
testRange := func(s map[string]*common.Step) {
|
||||
for _, step := range s {
|
||||
test(step)
|
||||
}
|
||||
}
|
||||
|
||||
test(c.Setup)
|
||||
test(c.Clone)
|
||||
test(c.Build)
|
||||
testRange(c.Publish)
|
||||
testRange(c.Deploy)
|
||||
testRange(c.Notify)
|
||||
testRange(c.Compose)
|
||||
})
|
||||
|
||||
g.It("Should have default workspace directory", func() {
|
||||
c := &common.Config{
|
||||
Clone: &common.Step{
|
||||
Config: map[string]interface{}{},
|
||||
},
|
||||
}
|
||||
r := &common.Repo{
|
||||
Link: "https://github.com/drone/drone",
|
||||
FullName: "drone/drone",
|
||||
}
|
||||
transformWorkspace(c, r)
|
||||
|
||||
g.Assert(c.Clone.Config["path"]).Equal(workspaceRoot(r))
|
||||
})
|
||||
|
||||
g.It("Should use path for working directory", func() {
|
||||
c := &common.Config{
|
||||
Clone: &common.Step{
|
||||
Config: map[string]interface{}{
|
||||
"path": "foo/bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
r := &common.Repo{
|
||||
Link: "https://github.com/drone/drone",
|
||||
FullName: "drone/drone",
|
||||
}
|
||||
transformWorkspace(c, r)
|
||||
|
||||
g.Assert(c.Clone.Config["path"]).Equal(filepath.Join(buildRoot, "foo/bar"))
|
||||
})
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue