removed yaml functions that were pushed down to the build layer

This commit is contained in:
Brad Rydzewski 2015-09-08 19:18:30 -07:00
parent ba47948fc9
commit cf953f19d3
8 changed files with 62 additions and 1134 deletions

View file

@ -1,74 +1,74 @@
package client package client
import ( // import (
"net" // "net"
"net/http" // "net/http"
"net/rpc" // "net/rpc"
common "github.com/drone/drone/pkg/types" // common "github.com/drone/drone/pkg/types"
) // )
// Client communicates with a Remote plugin using the // // Client communicates with a Remote plugin using the
// net/rpc protocol. // // net/rpc protocol.
type Client struct { // type Client struct {
*rpc.Client // *rpc.Client
} // }
// New returns a new, remote datastore backend that connects // // New returns a new, remote datastore backend that connects
// via tcp and exchanges data using Go's RPC mechanism. // // via tcp and exchanges data using Go's RPC mechanism.
func New(conf *config.Config) (*Client, error) { // func New(conf *config.Config) (*Client, error) {
// conn, err := net.Dial("tcp", conf.Server.Addr) // // conn, err := net.Dial("tcp", conf.Server.Addr)
// if err != nil { // // if err != nil {
// return nil, err // // return nil, err
// } // // }
// client := &Client{ // // client := &Client{
// rpc.NewClient(conn), // // rpc.NewClient(conn),
// } // // }
// return client, nil // // return client, nil
return nil, nil // return nil, nil
} // }
func (c *Client) Login(token, secret string) (*common.User, error) { // func (c *Client) Login(token, secret string) (*common.User, error) {
return nil, nil // return nil, nil
} // }
// Repo fetches the named repository from the remote system. // // Repo fetches the named repository from the remote system.
func (c *Client) Repo(u *common.User, owner, repo string) (*common.Repo, error) { // func (c *Client) Repo(u *common.User, owner, repo string) (*common.Repo, error) {
return nil, nil // return nil, nil
} // }
func (c *Client) Perm(u *common.User, owner, repo string) (*common.Perm, error) { // func (c *Client) Perm(u *common.User, owner, repo string) (*common.Perm, error) {
return nil, nil // return nil, nil
} // }
func (c *Client) Script(u *common.User, r *common.Repo, b *common.Build) ([]byte, error) { // func (c *Client) Script(u *common.User, r *common.Repo, b *common.Build) ([]byte, error) {
return nil, nil // return nil, nil
} // }
func (c *Client) Status(u *common.User, r *common.Repo, b *common.Build, link string) error { // func (c *Client) Status(u *common.User, r *common.Repo, b *common.Build, link string) error {
return nil // return nil
} // }
func (c *Client) Activate(u *common.User, r *common.Repo, k *common.Keypair, link string) error { // func (c *Client) Activate(u *common.User, r *common.Repo, k *common.Keypair, link string) error {
return nil // return nil
} // }
func (c *Client) Deactivate(u *common.User, r *common.Repo, link string) error { // func (c *Client) Deactivate(u *common.User, r *common.Repo, link string) error {
return nil // return nil
} // }
func (c *Client) Hook(r *http.Request) (*common.Hook, error) { // func (c *Client) Hook(r *http.Request) (*common.Hook, error) {
hook := new(common.Hook) // hook := new(common.Hook)
header := make(http.Header) // header := make(http.Header)
copyHeader(r.Header, header) // copyHeader(r.Header, header)
return hook, nil // return hook, nil
} // }
func copyHeader(dst, src http.Header) { // func copyHeader(dst, src http.Header) {
for k, vv := range src { // for k, vv := range src {
for _, v := range vv { // for _, v := range vv {
dst.Add(k, v) // dst.Add(k, v)
} // }
} // }
} // }

View file

@ -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
}

View file

@ -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
`

View file

@ -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
}

View file

@ -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()
})
})
}

View file

@ -1,112 +1,14 @@
package parser package parser
import ( import (
common "github.com/drone/drone/pkg/types" "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/Godeps/_workspace/src/gopkg.in/yaml.v2" "github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2"
) )
// Opts specifies parser options that will permit func ParseCondition(raw string) (*types.Condition, error) {
// 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) {
c := struct { c := struct {
Condition *common.Condition `yaml:"when"` Condition *types.Condition `yaml:"when"`
}{} }{}
err := yaml.Unmarshal([]byte(raw), c) err := yaml.Unmarshal([]byte(raw), c)
return c.Condition, err return c.Condition, err

View file

@ -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)
}

View file

@ -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"))
})
})
}