1
0
mirror of https://github.com/drone/drone-cli.git synced 2024-11-23 09:21:56 +01:00

refactoring

This commit is contained in:
Brad Rydzewski 2015-03-13 02:10:51 -07:00
parent 1cc8bc3b55
commit c29ddfbec5
17 changed files with 442 additions and 660 deletions

@ -1 +1,77 @@
package compiler
import "github.com/drone/drone-cli/common"
// Flags holds flags for loading a builder.
type Flags struct {
Lint bool
Safe bool
}
// Builder represents a build execution tree.
type Builder struct {
Build Node
Deploy Node
Notify Node
}
// Run runs the build, deploy and notify nodes
// in the build tree.
func (b *Builder) Run() error {
var err error
err = b.RunBuild()
if err != nil {
return err
}
err = b.RunDeploy()
if err != nil {
return err
}
return b.RunNotify()
}
// RunBuild runs only the build node.
func (b *Builder) RunBuild() error {
return nil
}
// RunDeploy runs only the deploy node.
func (b *Builder) RunDeploy() error {
return nil
}
// RunNotify runs on the notify node.
func (b *Builder) RunNotify() error {
return nil
}
// Load loads a build configuration file.
func Load(conf *common.Config, flags *Flags) *Builder {
var (
builds []Node
deploys []Node
notifys []Node
)
for _, step := range conf.Compose {
builds = append(builds, &batchNode{step}) // compose
}
builds = append(builds, &batchNode{}) // setup
builds = append(builds, &batchNode{}) // clone
builds = append(builds, &batchNode{}) // build
for _, step := range conf.Publish {
deploys = append(deploys, &batchNode{step}) // publish
}
for _, step := range conf.Deploy {
deploys = append(deploys, &batchNode{step}) // deploy
}
for _, step := range conf.Notify {
notifys = append(notifys, &batchNode{step}) // notify
}
return &Builder{
serialNode(builds),
serialNode(deploys),
serialNode(notifys),
}
}

@ -1,54 +0,0 @@
package inject
import (
"sort"
"strings"
"github.com/drone/drone-cli/common"
"gopkg.in/yaml.v2"
)
// 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) (*common.Config, error) {
cfg := common.Config{}
err := yaml.Unmarshal([]byte(raw), &cfg)
return &cfg, err
}

@ -1,67 +0,0 @@
package inject
import (
"testing"
"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
`

@ -1,105 +0,0 @@
package linter
import (
"fmt"
"strings"
"github.com/drone/drone-cli/common"
)
// 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,
expectTrustedSetup,
expectTrustedClone,
expectTrustedPublish,
expectTrustedDeploy,
expectTrustedNotify,
}
// 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.Build.Config == nil || c.Build.Config["commands"] == nil {
return fmt.Errorf("Yaml must define build commands")
}
return nil
}
// lint rule that fails when a non-trusted clone plugin is used.
func expectTrustedClone(c *common.Config) error {
if c.Clone != nil && strings.Contains(c.Clone.Image, "/") {
return fmt.Errorf("Yaml must use trusted clone plugins")
}
return nil
}
// lint rule that fails when a non-trusted clone plugin is used.
func expectTrustedSetup(c *common.Config) error {
if c.Setup != nil && strings.Contains(c.Setup.Image, "/") {
return fmt.Errorf("Yaml must use trusted setup plugins")
}
return nil
}
// lint rule that fails when a non-trusted publish plugin is used.
func expectTrustedPublish(c *common.Config) error {
for _, step := range c.Publish {
if strings.Contains(step.Image, "/") {
return fmt.Errorf("Yaml must use trusted publish plugins")
}
}
return nil
}
// lint rule that fails when a non-trusted deploy plugin is used.
func expectTrustedDeploy(c *common.Config) error {
for _, step := range c.Deploy {
if strings.Contains(step.Image, "/") {
return fmt.Errorf("Yaml must use trusted deploy plugins")
}
}
return nil
}
// lint rule that fails when a non-trusted notify plugin is used.
func expectTrustedNotify(c *common.Config) error {
for _, step := range c.Notify {
if strings.Contains(step.Image, "/") {
return fmt.Errorf("Yaml must use trusted notify plugins")
}
}
return nil
}

@ -1,92 +0,0 @@
package linter
import (
"testing"
"github.com/drone/drone-cli/common"
"github.com/franela/goblin"
)
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{
Build: &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 fail when untrusted setup image", func() {
c := &common.Config{Setup: &common.Step{Image: "foo/bar"}}
g.Assert(expectTrustedSetup(c) != nil).IsTrue()
})
g.It("Should fail when untrusted clone image", func() {
c := &common.Config{Clone: &common.Step{Image: "foo/bar"}}
g.Assert(expectTrustedClone(c) != nil).IsTrue()
})
g.It("Should fail when untrusted publish image", func() {
c := &common.Config{}
c.Publish = map[string]*common.Step{}
c.Publish["docker"] = &common.Step{Image: "foo/bar"}
g.Assert(expectTrustedPublish(c) != nil).IsTrue()
})
g.It("Should fail when untrusted deploy image", func() {
c := &common.Config{}
c.Deploy = map[string]*common.Step{}
c.Deploy["amazon"] = &common.Step{Image: "foo/bar"}
g.Assert(expectTrustedDeploy(c) != nil).IsTrue()
})
g.It("Should fail when untrusted notify image", func() {
c := &common.Config{}
c.Notify = map[string]*common.Step{}
c.Notify["hipchat"] = &common.Step{Image: "foo/bar"}
g.Assert(expectTrustedNotify(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.Build.Config = map[string]interface{}{}
c.Build.Config["commands"] = []string{"go build", "go test"}
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()
})
})
}

@ -1,109 +0,0 @@
package parser
import (
"strings"
"gopkg.in/yaml.v2"
)
const (
limitTags = 10
limitAxis = 25
)
// Matrix represents the build matrix.
type Matrix map[string][]string
// Axis represents a single permutation of entries
// from the build matrix.
type Axis map[string]string
// String returns a string representation of an Axis as
// a comma-separated list of environment variables.
func (a Axis) String() string {
var envs []string
for k, v := range a {
envs = append(envs, k+"="+v)
}
return strings.Join(envs, ",")
}
// Parse parses the Matrix section of the yaml file and
// returns a list of axis.
func Parse(raw string) ([]Axis, error) {
matrix, err := parseMatrix(raw)
if err != nil {
return nil, err
}
// if not a matrix build return an array
// with just the single axis.
if len(matrix) == 0 {
return nil, nil
}
return Calc(matrix), nil
}
// Calc calculates the permutations for th build matrix.
//
// Note that this method will cap the number of permutations
// to 25 to prevent an overly expensive calculation.
func Calc(matrix Matrix) []Axis {
// calculate number of permutations and
// extract the list of tags
// (ie go_version, redis_version, etc)
var perm int
var tags []string
for k, v := range matrix {
perm *= len(v)
if perm == 0 {
perm = len(v)
}
tags = append(tags, k)
}
// structure to hold the transformed
// result set
axisList := []Axis{}
// for each axis calculate the uniqe
// set of values that should be used.
for p := 0; p < perm; p++ {
axis := map[string]string{}
decr := perm
for i, tag := range tags {
elems := matrix[tag]
decr = decr / len(elems)
elem := p / decr % len(elems)
axis[tag] = elems[elem]
// enforce a maximum number of tags
// in the build matrix.
if i > limitTags {
break
}
}
// append to the list of axis.
axisList = append(axisList, axis)
// enforce a maximum number of axis
// that should be calculated.
if p > limitAxis {
break
}
}
return axisList
}
// helper function to parse the Matrix data from
// the raw yaml file.
func parseMatrix(raw string) (Matrix, error) {
data := struct {
Matrix map[string][]string
}{}
err := yaml.Unmarshal([]byte(raw), &data)
return data.Matrix, err
}

@ -1,33 +0,0 @@
package parser
import (
"testing"
"github.com/franela/goblin"
)
func Test_Matrix(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("Calculate matrix", func() {
m := map[string][]string{}
m["go_version"] = []string{"go1", "go1.2"}
m["python_version"] = []string{"3.2", "3.3"}
m["django_version"] = []string{"1.7", "1.7.1", "1.7.2"}
m["redis_version"] = []string{"2.6", "2.8"}
axis := Calc(m)
g.It("Should calculate permutations", func() {
g.Assert(len(axis)).Equal(24)
})
g.It("Should not duplicate permutations", func() {
set := map[string]bool{}
for _, perm := range axis {
set[perm.String()] = true
}
g.Assert(len(set)).Equal(24)
})
})
}

110
compiler/node.go Normal file

@ -0,0 +1,110 @@
package compiler
import (
"sync"
"github.com/drone/drone-cli/common"
"github.com/samalba/dockerclient"
)
// Node is an element in the build execution tree.
type Node interface {
Run(*State, *ResultWriter) error
}
// parallelNode runs a set of build nodes in parallel.
type parallelNode []Node
func (n parallelNode) Run(s *State, rw *ResultWriter) error {
var wg sync.WaitGroup
for _, node := range n {
wg.Add(1)
go func(node Node) {
defer wg.Done()
node.Run(s, rw)
}(node)
}
wg.Wait()
return nil
}
// serialNode runs a set of build nodes in sequential order.
type serialNode []Node
func (n serialNode) Run(s *State, rw *ResultWriter) error {
for _, node := range n {
err := node.Run(s, rw)
if err != nil {
return err
}
if rw.ExitCode() != 0 {
return nil
}
}
return nil
}
// batchNode runs a container and blocks until complete.
type batchNode struct {
step *common.Step
}
func (n *batchNode) Run(s *State, rw *ResultWriter) error {
host := toHostConfig(n.step)
conf := toContainerConfig(n.step)
if n.step.Config != nil {
conf.Cmd = toCommand(s, n.step)
conf.Entrypoint = []string{}
}
return nil
}
// serviceNode runs a container, blocking, writes output, uses config section
type serviceNode struct {
step *common.Step
}
func (n *serviceNode) Run(s *State, rw *ResultWriter) error {
host := toHostConfig(n.step)
conf := toContainerConfig(n.step)
return nil
}
// cloneNode runs a clone container, blocking, uses build section
type cloneNode struct {
step *common.Step
}
func (n *cloneNode) Run(s *State, rw *ResultWriter) error {
return nil
}
// buildNode runs a build container, discards the step.config section
type buildNode struct {
host *dockerclient.HostConfig
conf *dockerclient.ContainerConfig
}
func newBuildNode(step *common.Step) *buildNode {
host := toHostConfig(step)
conf := toContainerConfig(step)
conf.Entrypoint = []string{"/bin/bash"}
conf.Cmd = []string{"/drone/bin/build.sh"}
return &buildNode{host, conf}
}
func (n *buildNode) Run(s *State, rw *ResultWriter) error {
return nil
}
// setupNode container, discards the step.config section
type setupNode struct {
step *common.Step
}
func (n *setupNode) Run(s *State, rw *ResultWriter) error {
return nil
}

99
compiler/parse/node.go Normal file

@ -0,0 +1,99 @@
package parser
import (
"sync"
"github.com/drone/drone-cli/common"
)
// parallelNode runs a set of build nodes in parallel.
type parallelNode struct {
nodes []Node
}
func (n *parallelNode) Run(s *State, rw *ResultWriter) error {
for _, node := range n.nodes {
err := node.Run(s, rw)
if err != nil {
return err
}
if rw.ExitCode() != 0 {
return nil
}
}
return nil
}
// serialNode runs a set of build nodes in sequential order.
type serialNode struct {
nodes []Node
}
func (n *serialNode) Run(s *State, rw *ResultWriter) error {
var wg sync.WaitGroup
for _, task := range n.nodes {
wg.Add(1)
go func(task Node) {
defer wg.Done()
task.Run(s, rw)
}(task)
}
wg.Wait()
return nil
}
// batchNode runs a container and blocks until complete.
type batchNode struct {
step *common.Step
}
func (n *batchNode) Run(s *State, rw *ResultWriter) error {
host := toHostConfig(n.step)
conf := toContainerConfig(n.step)
if n.step.Config != nil {
conf.Cmd = toCommand(s, n.step)
conf.Entrypoint = []string{}
}
return nil
}
// serviceNode runs a container, blocking, writes output, uses config section
type serviceNode struct {
step *common.Step
}
func (n *serviceNode) Run(s *State, rw *ResultWriter) error {
host := toHostConfig(n.step)
conf := toContainerConfig(n.step)
return nil
}
// cloneNode runs a clone container, blocking, uses build section
type cloneNode struct {
step *common.Step
}
func (n *cloneNode) Run(s *State, rw *ResultWriter) error {
return nil
}
// buildNode runs a build container, discards the step.config section
type buildNode struct {
step *common.Step
}
func (n *buildNode) Run(s *State, rw *ResultWriter) error {
return nil
}
// setupNode container, discards the step.config section
type setupNode struct {
step *common.Step
}
func (n *setupNode) Run(s *State, rw *ResultWriter) error {
return nil
}

@ -3,8 +3,6 @@ package parser
import (
"github.com/drone/drone-cli/common"
"github.com/drone/drone-cli/common/matrix"
"github.com/drone/drone-cli/parser/inject"
"gopkg.in/yaml.v2"
)
@ -29,7 +27,7 @@ func Parse(raw string) ([]*common.Config, error) {
for _, ax := range axis {
// inject the matrix values into the raw script
injected := inject.Inject(raw, ax)
injected := Inject(raw, ax)
conf, err := parse(injected)
if err != nil {
return nil, err

14
compiler/parse/result.go Normal file

@ -0,0 +1,14 @@
package parser
// ResultWriter represents the result from a build request.
type ResultWriter interface {
// Write writes the build stdout and stderr to the result.
Write([]byte) (int, error)
// WriteExitCode writes the build exit status to the result.
WriteExitCode(int)
// ExitCode returns the build exit status.
ExitCode() int
}

8
compiler/parse/tree.go Normal file

@ -0,0 +1,8 @@
package parser
type Tree struct {
Build Node
Publish Node
Deploy Node
Notify Node
}

31
compiler/result.go Normal file

@ -0,0 +1,31 @@
package compiler
import "io"
// ResultWriter represents the result from a build request.
type ResultWriter struct {
writer io.Writer
exitCode int
}
// NewResultWriter returns a new ResultWriter.
func NewResultWriter(w io.Writer) *ResultWriter {
return &ResultWriter{writer: w}
}
// Write writes the build stdout and stderr to the result.
func (r *ResultWriter) Write(p []byte) (n int, err error) {
return r.writer.Write(p)
}
// WriteExitCode writes the build exit status to the result.
func (r *ResultWriter) WriteExitCode(code int) {
if code != 0 {
r.exitCode = code
}
}
// ExitCode returns the build exit status.
func (r *ResultWriter) ExitCode() int {
return r.exitCode
}

35
compiler/state.go Normal file

@ -0,0 +1,35 @@
package compiler
import (
"io"
"github.com/drone/drone-cli/common"
"github.com/samalba/dockerclient"
)
// State represents the execution state of the
// running build.
type State struct {
Repo *common.Repo
Commit *common.Repo
Config *common.Config
Clone *common.Clone
Client dockerclient.Client
}
func (*State) Run() (string, error) {
return "", nil
}
func (*State) Remove(string) error {
return nil
}
func (*State) RemoveAll() error {
return nil
}
func (*State) Logs(string) (io.ReadCloser, error) {
return nil, nil
}

68
compiler/util.go Normal file

@ -0,0 +1,68 @@
package compiler
import (
"encoding/json"
"github.com/drone/drone-cli/common"
"github.com/samalba/dockerclient"
)
// helper function to encode the build step to
// a json string. Primarily used for plugins, which
// expect a json encoded string in stdin or arg[1].
func toCommand(state *State, step *common.Step) []string {
p := payload{
state.Repo,
state.Commit,
state.Clone,
step.Config,
}
return []string{p.Encode()}
}
// helper function that converts a build step to
// a hostConfig for use with the dockerclient
func toHostConfig(step *common.Step) *dockerclient.HostConfig {
return &dockerclient.HostConfig{
Privileged: step.Privileged,
NetworkMode: step.NetworkMode,
}
}
// helper function that converts the build step to
// a containerConfig for use with the dockerclient
func toContainerConfig(step *common.Step) *dockerclient.ContainerConfig {
config := &dockerclient.ContainerConfig{
Image: step.Image,
Env: step.Environment,
Cmd: step.Command,
Entrypoint: step.Entrypoint,
WorkingDir: step.WorkingDir,
}
if len(step.Volumes) != 0 {
config.Volumes = map[string]struct{}{}
for _, path := range step.Volumes {
config.Volumes[path] = struct{}{}
}
}
return config
}
// payload represents the payload of a plugin
// that is serialized and sent to the plugin in JSON
// format via stdin or arg[1].
type payload struct {
Repo *common.Repo `json:"repo"`
Commit *common.Repo `json:"commit"`
Clone *common.Clone `json:"clone"`
Config map[string]interface{} `json:"vargs"`
}
// Encode encodes the payload in JSON format.
func (p *payload) Encode() string {
out, _ := json.Marshal(p)
return string(out)
}

@ -1,105 +0,0 @@
package linter
import (
"fmt"
"strings"
"github.com/drone/drone-cli/common"
)
// 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,
expectTrustedSetup,
expectTrustedClone,
expectTrustedPublish,
expectTrustedDeploy,
expectTrustedNotify,
}
// 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.Build.Config == nil || c.Build.Config["commands"] == nil {
return fmt.Errorf("Yaml must define build commands")
}
return nil
}
// lint rule that fails when a non-trusted clone plugin is used.
func expectTrustedClone(c *common.Config) error {
if c.Clone != nil && strings.Contains(c.Clone.Image, "/") {
return fmt.Errorf("Yaml must use trusted clone plugins")
}
return nil
}
// lint rule that fails when a non-trusted clone plugin is used.
func expectTrustedSetup(c *common.Config) error {
if c.Setup != nil && strings.Contains(c.Setup.Image, "/") {
return fmt.Errorf("Yaml must use trusted setup plugins")
}
return nil
}
// lint rule that fails when a non-trusted publish plugin is used.
func expectTrustedPublish(c *common.Config) error {
for _, step := range c.Publish {
if strings.Contains(step.Image, "/") {
return fmt.Errorf("Yaml must use trusted publish plugins")
}
}
return nil
}
// lint rule that fails when a non-trusted deploy plugin is used.
func expectTrustedDeploy(c *common.Config) error {
for _, step := range c.Deploy {
if strings.Contains(step.Image, "/") {
return fmt.Errorf("Yaml must use trusted deploy plugins")
}
}
return nil
}
// lint rule that fails when a non-trusted notify plugin is used.
func expectTrustedNotify(c *common.Config) error {
for _, step := range c.Notify {
if strings.Contains(step.Image, "/") {
return fmt.Errorf("Yaml must use trusted notify plugins")
}
}
return nil
}

@ -1,92 +0,0 @@
package linter
import (
"testing"
"github.com/drone/drone-cli/common"
"github.com/franela/goblin"
)
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{
Build: &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 fail when untrusted setup image", func() {
c := &common.Config{Setup: &common.Step{Image: "foo/bar"}}
g.Assert(expectTrustedSetup(c) != nil).IsTrue()
})
g.It("Should fail when untrusted clone image", func() {
c := &common.Config{Clone: &common.Step{Image: "foo/bar"}}
g.Assert(expectTrustedClone(c) != nil).IsTrue()
})
g.It("Should fail when untrusted publish image", func() {
c := &common.Config{}
c.Publish = map[string]*common.Step{}
c.Publish["docker"] = &common.Step{Image: "foo/bar"}
g.Assert(expectTrustedPublish(c) != nil).IsTrue()
})
g.It("Should fail when untrusted deploy image", func() {
c := &common.Config{}
c.Deploy = map[string]*common.Step{}
c.Deploy["amazon"] = &common.Step{Image: "foo/bar"}
g.Assert(expectTrustedDeploy(c) != nil).IsTrue()
})
g.It("Should fail when untrusted notify image", func() {
c := &common.Config{}
c.Notify = map[string]*common.Step{}
c.Notify["hipchat"] = &common.Step{Image: "foo/bar"}
g.Assert(expectTrustedNotify(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.Build.Config = map[string]interface{}{}
c.Build.Config["commands"] = []string{"go build", "go test"}
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()
})
})
}