From 8ab151197f8f237af9bae0051ea8dae683e873b0 Mon Sep 17 00:00:00 2001 From: Jacob McCann Date: Tue, 30 May 2017 09:47:23 -0500 Subject: [PATCH 1/6] Vendor goblin --- vendor/github.com/franela/goblin/LICENSE | 19 ++ vendor/github.com/franela/goblin/Makefile | 3 + vendor/github.com/franela/goblin/README.md | 141 ++++++++ .../github.com/franela/goblin/assertions.go | 59 ++++ vendor/github.com/franela/goblin/go.snippets | 36 +++ vendor/github.com/franela/goblin/goblin.go | 302 ++++++++++++++++++ .../github.com/franela/goblin/goblin_logo.jpg | Bin 0 -> 36893 bytes .../franela/goblin/goblin_output.png | Bin 0 -> 18985 bytes .../franela/goblin/mono_reporter.go | 26 ++ vendor/github.com/franela/goblin/reporting.go | 137 ++++++++ vendor/github.com/franela/goblin/resolver.go | 21 ++ vendor/vendor.json | 8 +- 12 files changed, 751 insertions(+), 1 deletion(-) create mode 100644 vendor/github.com/franela/goblin/LICENSE create mode 100644 vendor/github.com/franela/goblin/Makefile create mode 100644 vendor/github.com/franela/goblin/README.md create mode 100644 vendor/github.com/franela/goblin/assertions.go create mode 100644 vendor/github.com/franela/goblin/go.snippets create mode 100644 vendor/github.com/franela/goblin/goblin.go create mode 100644 vendor/github.com/franela/goblin/goblin_logo.jpg create mode 100644 vendor/github.com/franela/goblin/goblin_output.png create mode 100644 vendor/github.com/franela/goblin/mono_reporter.go create mode 100644 vendor/github.com/franela/goblin/reporting.go create mode 100644 vendor/github.com/franela/goblin/resolver.go diff --git a/vendor/github.com/franela/goblin/LICENSE b/vendor/github.com/franela/goblin/LICENSE new file mode 100644 index 0000000..b2d6522 --- /dev/null +++ b/vendor/github.com/franela/goblin/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013 Marcos Lilljedahl and Jonathan Leibiusky + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/franela/goblin/Makefile b/vendor/github.com/franela/goblin/Makefile new file mode 100644 index 0000000..66763dc --- /dev/null +++ b/vendor/github.com/franela/goblin/Makefile @@ -0,0 +1,3 @@ +export GOPATH=$(shell pwd) +test: + go test -v diff --git a/vendor/github.com/franela/goblin/README.md b/vendor/github.com/franela/goblin/README.md new file mode 100644 index 0000000..a4b6eab --- /dev/null +++ b/vendor/github.com/franela/goblin/README.md @@ -0,0 +1,141 @@ +[![Build Status](https://travis-ci.org/franela/goblin.png?branch=master)](https://travis-ci.org/franela/goblin) +Goblin +====== + +![](https://github.com/marcosnils/goblin/blob/master/goblin_logo.jpg?raw=true) + +A [Mocha](http://mochajs.org/) like BDD testing framework for Go + +No extensive documentation nor complicated steps to get it running + +Run tests as usual with `go test` + +Colorful reports and beautiful syntax + + +Why Goblin? +----------- + +Inspired by the flexibility and simplicity of Node BDD and frustrated by the +rigorousness of Go way of testing, we wanted to bring a new tool to +write self-describing and comprehensive code. + + + +What do I get with it? +---------------------- + +- Preserve the exact same syntax and behaviour as Node's Mocha +- Nest as many `Describe` and `It` blocks as you want +- Use `Before`, `BeforeEach`, `After` and `AfterEach` for setup and teardown your tests +- No need to remember confusing parameters in `Describe` and `It` blocks +- Use a declarative and expressive language to write your tests +- Plug different assertion libraries ([Gomega](https://github.com/onsi/gomega) supported so far) +- Skip your tests the same way as you would do in Mocha +- Automatic terminal support for colored outputs +- Two line setup is all you need to get up running + + + +How do I use it? +---------------- + +Since ```go test``` is not currently extensive, you will have to hook Goblin to it. You do that by +adding a single test method in your test file. All your goblin tests will be implemented inside this function. + +```go +package foobar + +import ( + "testing" + . "github.com/franela/goblin" +) + +func Test(t *testing.T) { + g := Goblin(t) + g.Describe("Numbers", func() { + g.It("Should add two numbers ", func() { + g.Assert(1+1).Equal(2) + }) + g.It("Should match equal numbers", func() { + g.Assert(2).Equal(4) + }) + g.It("Should substract two numbers") + }) +} +``` + +Ouput will be something like: + +![](https://github.com/marcosnils/goblin/blob/master/goblin_output.png?raw=true) + +Nice and easy, right? + +Can I do asynchronous tests? +---------------------------- + +Yes! Goblin will help you to test asynchronous things, like goroutines, etc. You just need to add a ```done``` parameter to the handler function of your ```It```. This handler function should be called when your test passes. + +```go + ... + g.Describe("Numbers", func() { + g.It("Should add two numbers asynchronously", func(done Done) { + go func() { + g.Assert(1+1).Equal(2) + done() + }() + }) + }) + ... +``` + +Goblin will wait for the ```done``` call, a ```Fail``` call or any false assertion. + +How do I use it with Gomega? +---------------------------- + +Gomega is a nice assertion framework. But it doesn't provide a nice way to hook it to testing frameworks. It should just panic instead of requiring a fail function. There is an issue about that [here](https://github.com/onsi/gomega/issues/5). +While this is being discussed and hopefully fixed, the way to use Gomega with Goblin is: + +```go +package foobar + +import ( + "testing" + . "github.com/franela/goblin" + . "github.com/onsi/gomega" +) + +func Test(t *testing.T) { + g := Goblin(t) + + //special hook for gomega + RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) + + g.Describe("lala", func() { + g.It("lslslslsls", func() { + Expect(1).To(Equal(10)) + }) + }) +} +``` + + +FAQ: +---- + +### How do I run specific tests? + +If `-goblin.run=$REGES` is supplied to the `go test` command then only tests that match the supplied regex will run + + +TODO: +----- + +We do have a couple of [issues](https://github.com/franela/goblin/issues) pending we'll be addressing soon. But feel free to +contribute and send us PRs (with tests please :smile:). + +Contributions: +------------ + +Special thanks to [Leandro Reox](https://github.com/leandroreox) (Leitan) for the goblin logo. diff --git a/vendor/github.com/franela/goblin/assertions.go b/vendor/github.com/franela/goblin/assertions.go new file mode 100644 index 0000000..5ccae7d --- /dev/null +++ b/vendor/github.com/franela/goblin/assertions.go @@ -0,0 +1,59 @@ +package goblin + +import ( + "fmt" + "reflect" + "strings" +) + +type Assertion struct { + src interface{} + fail func(interface{}) +} + +func objectsAreEqual(a, b interface{}) bool { + if reflect.TypeOf(a) != reflect.TypeOf(b) { + return false + } + + if reflect.DeepEqual(a, b) { + return true + } + + if fmt.Sprintf("%#v", a) == fmt.Sprintf("%#v", b) { + return true + } + + return false +} + +func formatMessages(messages ...string) string { + if len(messages) > 0 { + return ", " + strings.Join(messages, " ") + } + return "" +} + +func (a *Assertion) Eql(dst interface{}) { + a.Equal(dst) +} + +func (a *Assertion) Equal(dst interface{}) { + if !objectsAreEqual(a.src, dst) { + a.fail(fmt.Sprintf("%#v %s %#v", a.src, "does not equal", dst)) + } +} + +func (a *Assertion) IsTrue(messages ...string) { + if !objectsAreEqual(a.src, true) { + message := fmt.Sprintf("%v %s%s", a.src, "expected false to be truthy", formatMessages(messages...)) + a.fail(message) + } +} + +func (a *Assertion) IsFalse(messages ...string) { + if !objectsAreEqual(a.src, false) { + message := fmt.Sprintf("%v %s%s", a.src, "expected true to be falsey", formatMessages(messages...)) + a.fail(message) + } +} diff --git a/vendor/github.com/franela/goblin/go.snippets b/vendor/github.com/franela/goblin/go.snippets new file mode 100644 index 0000000..5610a4b --- /dev/null +++ b/vendor/github.com/franela/goblin/go.snippets @@ -0,0 +1,36 @@ +snippet gd + g.Describe("${1:name}", func() { + ${2} + }) + ${0} +snippet git + g.It("${1:name}", func() { + ${2} + }) + ${0} +snippet gait + g.It("${1:name}", func(done Done) { + done() + ${2} + }) + ${0} +snippet gb + g.Before(func() { + ${1} + }) + ${0} +snippet gbe + g.BeforeEach(func() { + ${1} + }) + ${0} +snippet ga + g.After(func() { + ${1} + }) + ${0} +snippet gae + g.AfterEach(func() { + ${1} + }) + ${0} diff --git a/vendor/github.com/franela/goblin/goblin.go b/vendor/github.com/franela/goblin/goblin.go new file mode 100644 index 0000000..d237d81 --- /dev/null +++ b/vendor/github.com/franela/goblin/goblin.go @@ -0,0 +1,302 @@ +package goblin + +import ( + "flag" + "fmt" + "regexp" + "runtime" + "sync" + "testing" + "time" +) + +type Done func(error ...interface{}) + +type Runnable interface { + run(*G) bool +} + +func (g *G) Describe(name string, h func()) { + d := &Describe{name: name, h: h, parent: g.parent} + + if d.parent != nil { + d.parent.children = append(d.parent.children, Runnable(d)) + } + + g.parent = d + + h() + + g.parent = d.parent + + if g.parent == nil && d.hasTests { + g.reporter.begin() + if d.run(g) { + g.t.Fail() + } + g.reporter.end() + } +} +func (g *G) Timeout(time time.Duration) { + g.timeout = time + g.timer.Reset(time) +} + +type Describe struct { + name string + h func() + children []Runnable + befores []func() + afters []func() + afterEach []func() + beforeEach []func() + hasTests bool + parent *Describe +} + +func (d *Describe) runBeforeEach() { + if d.parent != nil { + d.parent.runBeforeEach() + } + + for _, b := range d.beforeEach { + b() + } +} + +func (d *Describe) runAfterEach() { + + if d.parent != nil { + d.parent.runAfterEach() + } + + for _, a := range d.afterEach { + a() + } +} + +func (d *Describe) run(g *G) bool { + failed := false + if d.hasTests { + g.reporter.beginDescribe(d.name) + + for _, b := range d.befores { + b() + } + + for _, r := range d.children { + if r.run(g) { + failed = true + } + } + + for _, a := range d.afters { + a() + } + + g.reporter.endDescribe() + } + + return failed +} + +type Failure struct { + stack []string + testName string + message string +} + +type It struct { + h interface{} + name string + parent *Describe + failure *Failure + reporter Reporter + isAsync bool +} + +func (it *It) run(g *G) bool { + g.currentIt = it + + if it.h == nil { + g.reporter.itIsPending(it.name) + return false + } + //TODO: should handle errors for beforeEach + it.parent.runBeforeEach() + + runIt(g, it.h) + + it.parent.runAfterEach() + + failed := false + if it.failure != nil { + failed = true + } + + if failed { + g.reporter.itFailed(it.name) + g.reporter.failure(it.failure) + } else { + g.reporter.itPassed(it.name) + } + return failed +} + +func (it *It) failed(msg string, stack []string) { + it.failure = &Failure{stack: stack, message: msg, testName: it.parent.name + " " + it.name} +} + +func parseFlags() { + //Flag parsing + flag.Parse() + if *regexParam != "" { + runRegex = regexp.MustCompile(*regexParam) + } else { + runRegex = nil + } +} + +var timeout = flag.Duration("goblin.timeout", 5*time.Second, "Sets default timeouts for all tests") +var isTty = flag.Bool("goblin.tty", true, "Sets the default output format (color / monochrome)") +var regexParam = flag.String("goblin.run", "", "Runs only tests which match the supplied regex") +var runRegex *regexp.Regexp + +func init() { + parseFlags() +} + +func Goblin(t *testing.T, arguments ...string) *G { + g := &G{t: t, timeout: *timeout} + var fancy TextFancier + if *isTty { + fancy = &TerminalFancier{} + } else { + fancy = &Monochrome{} + } + + g.reporter = Reporter(&DetailedReporter{fancy: fancy}) + return g +} + +func runIt(g *G, h interface{}) { + defer timeTrack(time.Now(), g) + g.mutex.Lock() + g.timedOut = false + g.mutex.Unlock() + g.timer = time.NewTimer(g.timeout) + g.shouldContinue = make(chan bool) + if call, ok := h.(func()); ok { + // the test is synchronous + go func(c chan bool) { call(); c <- true }(g.shouldContinue) + } else if call, ok := h.(func(Done)); ok { + doneCalled := 0 + go func(c chan bool) { + call(func(msg ...interface{}) { + if len(msg) > 0 { + g.Fail(msg) + } else { + doneCalled++ + if doneCalled > 1 { + g.Fail("Done called multiple times") + } + c <- true + } + }) + }(g.shouldContinue) + } else { + panic("Not implemented.") + } + select { + case <-g.shouldContinue: + case <-g.timer.C: + //Set to nil as it shouldn't continue + g.shouldContinue = nil + g.timedOut = true + g.Fail("Test exceeded " + fmt.Sprintf("%s", g.timeout)) + } + // Reset timeout value + g.timeout = *timeout +} + +type G struct { + t *testing.T + parent *Describe + currentIt *It + timeout time.Duration + reporter Reporter + timedOut bool + shouldContinue chan bool + mutex sync.Mutex + timer *time.Timer +} + +func (g *G) SetReporter(r Reporter) { + g.reporter = r +} + +func (g *G) It(name string, h ...interface{}) { + if matchesRegex(name) { + it := &It{name: name, parent: g.parent, reporter: g.reporter} + notifyParents(g.parent) + if len(h) > 0 { + it.h = h[0] + } + g.parent.children = append(g.parent.children, Runnable(it)) + } +} + +func matchesRegex(value string) bool { + if runRegex != nil { + return runRegex.MatchString(value) + } + return true +} + +func notifyParents(d *Describe) { + d.hasTests = true + if d.parent != nil { + notifyParents(d.parent) + } +} + +func (g *G) Before(h func()) { + g.parent.befores = append(g.parent.befores, h) +} + +func (g *G) BeforeEach(h func()) { + g.parent.beforeEach = append(g.parent.beforeEach, h) +} + +func (g *G) After(h func()) { + g.parent.afters = append(g.parent.afters, h) +} + +func (g *G) AfterEach(h func()) { + g.parent.afterEach = append(g.parent.afterEach, h) +} + +func (g *G) Assert(src interface{}) *Assertion { + return &Assertion{src: src, fail: g.Fail} +} + +func timeTrack(start time.Time, g *G) { + g.reporter.itTook(time.Since(start)) +} + +func (g *G) Fail(error interface{}) { + //Skips 7 stacks due to the functions between the stack and the test + stack := ResolveStack(7) + message := fmt.Sprintf("%v", error) + g.currentIt.failed(message, stack) + if g.shouldContinue != nil { + g.shouldContinue <- true + } + g.mutex.Lock() + defer g.mutex.Unlock() + if !g.timedOut { + //Stop test function execution + runtime.Goexit() + } + +} diff --git a/vendor/github.com/franela/goblin/goblin_logo.jpg b/vendor/github.com/franela/goblin/goblin_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..44534f297b14af552cc040a32e29a85892d364a2 GIT binary patch literal 36893 zcmd421z23m(kQxy!QF#HaCdiiffH-L>eN`?>p709{^MP8t9~3Im7%0Pfdt?WDY{EdW4C z37`i603Lt?VFF+X1cLklAOZm9Cky~4Aj03U4T$zH8Yl=28vwZjNRZ0~#Qqb;hQR*e z)&TThyor$SDF`K`#h+4te#^)ysgSWV^D?so06Qxi2R|DtKN}AjD=R-c7e6Z>0Kmip z033t^nw6c6HS2Gfjg>VE^iL17p#E+JWRL~^8wM^Qd;kCo0{=w``=>4-@_%#z(f_Fn z6zFdWAa@$S9d<$jBKfh=^!;=oneqI5{~V z!oedTB0&i1&;ckA7z_mshJk^Gm=E+EQV&35z+gUR6NAN4HHIU1#%6z!kPS~EUe$@C zHhE0RVd4^mfQb7D51)XFnueB+o|B84hnJ6ELQ+avMpjNBqushPQjrIodf ztDC!rr4|-uh^n!wh21CO= z=mmoEgj`?@Xqd-ru$W@1aK_G9EM~ zPqCo?E6si@_K#lk05TW^F&-EL5CJZ)QLt5@v?-o%=-9VN358a~I6t?v<5qTd*n6rF zovgvTQYW7joasZb>kiixr`YUEsonhaSy$ym8|uXKn#i?VK5J8!FndS6%R6})k3+Iy zo;Mtf;XiT|esCiK2b>w6inEWcId5v;WcMVTR^p$V3l?8TPF9c~YD=B;Wq5!VI0*4G znwW^9n#uc%dlIUn?*X3=J^+V8+R0b6y-b=E2zC_Wr}Bh9o`Pwd_TbM2Ctz#%a~^phW|<{A0TsFdB)g|$ zGxJpCUz(f>ZIR%+rNbxlP0;-dp;^Su6=E{%%@}6ruNrN&76H366Ar*M_K{uNA&SK8n zl;uJ(sg2R734!Uu3@b(C&)(tsQGL7>ZJ!q=sUdh$Y_f6a<3iT`Vtk0-EUwvyM>auAaQ7n?E{bvt=3+I}H$_#XF&`8GJg9_mlcCu@$7S%UDi^I1{c`&0bdG z`U<6y6&z>$UI4LNV?-d63 zTIqw1qNQCGD+8s{&W^YGMUi3mJzz1Crg<@)*PYWc%BggqI=Jy;k<_Q}!(?B}sSU2N z+dG`6#9?0Fk)7i!?*Sol<4=NdCMbZ1MB0|0^ZLwjUn;VPO8HEU-i3pcHIDZ7KhoSvn_8ZhZ?+|1+sP)~ES$uI2cc!-Sa$p4wly?0pBP3_$8H;@z#jvhRUP0CU?z?FekIPV`M7bBNXR0>d=E0&HO!d0Oi2Z zZ1bF*<*V`ddPVy&J>dAVjAlxZV@uN66R{S4si!Lbq+xxs^2en7k+T8=RP?pkrej?|}mm-O*#oUBo>f-c{XasabG$UZXeDw^97} zK!+8Wp^IIA?wMD&T#cmfu#iqgPUzSqrAtZUvu|T~;JNjt43FV%f4UQom_q9rPXhl! zUl%>?hWTT*AhxCy$(!<~_h8qHiiTd5#VgC639qrCWw}FS5%cNQm*&x;Tjt;<#kV(F z?P2cv0=qrV73s^!KEguVJ%%E|}NJN?LfvgEw%eY$!xX?rUuZ0a~_6Np^yG91! z0Yn+RYcTWk_h>fjZN#@$Gq=j?vIkuDB-qtWNkfreW_XaWLoiEv5>Z8kU+oIBO&U$t zZrMgjctaZ}t}pqY$G`7}xd*J!D}+7~??38TK@}8O%l(m^yRy8rFtThX$GDcG)kzo> zC~Sm)FBl1YEhV^sX^*HKPF}0PRnURE3{)piI;Ri#nyUvL$o>Szsd+o*oR~r_TzlFv zg;*HItDH4I?TdQML7=-P*x@Lo>QOBy9YgxJXGHvz7;+2PaEJOLr0zOpcbKGb0yXajF*~nYX!@eB(jBSe6ItZ) zqnkl=%B^q<6W7=d&@3lt#H$+bmTmoqNFS@tDEm;1@pIx@z0G*m5lPV05hwsdu4Iot zzv0EL*t(DjmL+G3LrT3=wUIcyO8?D457s%j&OMVn%)3Rz_nY*BJCXELw7jS@mOU6J zVX~RElf&__Tb_bpR@&S z%hyhkIWG2+4~5^o83K_8qKbTt%yQ1X`Zn6JfHRlaCvh<{Njg5Ns(NKWS<^JdiQvE0 z^6_)1(7N3)+FDgcz$)igY%y2&wVk6?HOKNE&YC%1nd(SJNp@udhaS{en;$6cZIv#(rG)K%)H`WJTnp*%u{N z+0{|E!i{sepp3CGDC4lQscZc=E3S6F_6A94%hl>t{2`;-AaMnHLPTf#Y zn0p-~zStvEHt~)PZmKY?j=Q1KT322_s#ScMg_CNjn!`LJjQxcSjev~Utwm$hu!N^+ zp)JYd8Fv~5dlLnHC6viXUU=rnfk%X*RDAnk`+7XW6qz+cib6ChIbaMm%8Ef^!o8d>>J~{Km|dLJ;amRZ(UDtIs06|pp-t|)tP&sYdEy= z$~_+k8h(1c6OY37{iZ+8XfIk%0G3x+w^owkCqWUM!~yafiPTMVClEBF`RXLHgK-tX?@)eadzInN|RP4f3=zP5FPI}qH zj&ej=Fy=~EvwkUA5MLs6ynKC^#c4wjynSk)3&+KFx&5RRfUPP8;0$^p-3hs`+@Kl+ z?6}X&QmwLFUChc9Ge{;c!hy-&o#28=j9TLbbOQj|hxMIWu;P2iG zA6|?JXa)2L6x(y^p;ax6ItTp72J3bs2I(g>yxL<$WI)^>b$N$w+7{KlTe=(7b|#nM zh{-{6ya&>ThC=v`9|u=6W!rGtHfFVk(!7sT37|PbsebDsL&e+Q?(<Vz~f zhc5`a5KbNy(Wss_wzhLH&A_Rz|1|VYoIm)$hRINYAP>D{?Auu$JeGV}uv?z)E4R(=T|vQaDG=>F|HONY|4fwNqT@=7 z_z7K${5Q{;T>@Ke%1&og?Tqq_jv)TaQO~+guYf1bNj)S?wgM|u?X?457IC9tQdXvd z71C%l@iJ{*9dL&EE5p7-#mH3ZLLoNbLt|*~vxrZcOfqId8-1k=lf&02 z+5DzCIG<$%$^^>g|F;SFpbcFMx>%taNiAq|w(5w4@i--J?Yrd?e``y+t2elOCY<$~=YzMQ0(ZC>AMSyY z+fl&tb1oNT=j-a2Wp2|K=^!`=Ip+}|BTXmpS-O>;O>k`mw20{|TIp``5|BF$dP{O!T%@;l{)q5` z6o0=VZ6X9;7hi9%E*e$tbQXB=H7)-*Ymhs4U0~*geRiuCGt=R;_{(hib*_%xQy7sL zz{mbSgtNcHTWUJ1EU1=uauJVbYlj1~Wc^f&di2^yiNZ;*c5X{&7zVbLjJ0m9(!5%y zYQN$P^LKo$wM(QHh&T}T0WL}y#w@O0JQwM1)i#LiEw3ZA?X8O){O(MBHI+@0mvT$b zcgOfS#-K}kuH^MSNA|NuTg8v%issX=6%IAxB9T*o&|BNLd$P9-ggt5X#btZi^O8O;%u$8UWT@_PP5~Tr8E$M(2YBIBoTlT`87o0TTW6Pi z2ghZFU9mmnvS;8c3{hgG|C99|@Xq{g<49g(OfI623FTNfaN`o#srI^sGUHc&rKNy#eVg=sR!j;K-w+<_?KBK}J}>6Z zrfSWqu3aHnp+=YMS5Fb4rTLK{$J4EhG+f?Y+G{e?pgK~MU{4`K*!(tVKv;de&V5P+ z%6`<9zv0DstMsfk?(R2%C8dotg~mP|^Eg-@Qt_;tvweMG=cg={0oE%Dg;rP#%7QeH zc%Z+=E=-`qFK*v7;q5(#hhr$Rn$Vq_yA}Clp5|RSx4759_&I>a!^~5-nlu@$GT(=t zyf5-4R7oH}9O|Nr`u6V8)V98~y0>t#@J0!1x4=Ds{Dhwt9NJIZDCBefZ#Psvz1w$pZLvrv5)q9Vw`6(>ioq)6<4Oxby$8O<$k zpAugiO*bC%KQI1z{=-(YYAUZk)uqM}GoF>FuiN+*sHlhpHvQU3cK1WlC8wu6Uzydq zes_F!rxD&m!VwpKtmyFEWLhO|TFU)g1&)mCD}k(2CQ_)-)^t4DBnmGnsR63C=qJz- zfnEV=7`nEe6ySWg8Wp2r+T;JYeJb~>JuZ`xh~^X7>#A}-KMr$SYqW1nEd#X2%ke1}7>7wwq9<9euiLT_b$4gAPM?wI@1}a>Q4#Ax;*0Me>=ITCAG)#SLP5I1dM1&Cqz4*QC zo$SrsjLE$0?HpYBy@Y6f3g?Hw4`>z|2$74K1;3i4^j{>9Cn1`@`r_&7$?VC&?C4_2 z!p6tP$HL0a!p_bFp_}O^*IXRhFIrv#w|KR>b`8TtYqnWjZ_rGE0`SRM@U$Kd@Z18Qdadpw=oUF?2Z#>|w(+|JzI+`-Ki z;wx+qCWVZtnJK@8ql>+|Ef7{}B^Z#Pb-_<{z^;ZXe zm# zZb^1ANj3>d4n8(cUNOmE#(@z26!Ftif64f>EC05|KUDhv(FmxUJO4i(fxl$@S0nI0 zacszpR#N)+&h_^iXm9_QYyYI>mveA+Glp1LPEr_xV79h4<7egJWo2XI;bi4uXXWN) zW8;-z;}n+^m*(W)<>BPul>RyOm6ZNueej%L@|0byAq%Uq-QTwSORm|2T+aWV++F6OR|?k=Y0LNpJf@!#n+4H?UWC;We;zO41bRP_Eej~+VwwV^tMtZ-F{1md@7^Aju;>G9X76AIp?T=DlbOBQ1Kb0FUwXKkLtwBX1P=4CHur?UQxKTa z&fVS`0$)L3Y@&n$@+|=sffEofLyJ)CMKwv@0Av&_eR`_I4!+y1IQQ~hZ(5bnr7xPLhR!8zst059aU9UT7;&LkB88bSd8Z~h+~ zWflOSg#bYP_#gV91NZ<5KmpJK zOaME;0|)@3fD|APC<7XR4qyP70#<-M-~xC8e!vSL42S~afmc8p@D9iWih&BC8fXAo zfKH$f7zQSQX(0M^cl1U+6A40uA#tC$WYiA{wEKs~q zVo(ZD8c_OBmQc=6K2RZ0u~2WIa-hnf>Y+NJhM}gRR-yKxE+B~}NMKy>V=xn#7c2o* z2J3R@_dreU^VzQMx6;=cHB-`oc!TX24d$cEC=;ZopnZvQO~g7~q89l;Mow+~C6CQsK(r+TlLJZNXi^ zBf*oxv%^cn>%u$02f?Snm%z8de}vzLzd=ApphDnBP)0CA@J5J3$U|sG7)RJbxIx4~ zq(Kxy)IhXBe1Z4|u>!FdaS`zh2?>cDi4RE?$r>pT=?&6*qyeNgr0>X>$n?k($j^}7 zkz{}AK&M8RKsQGBM^8s@LZ3yyz`(*_!%)Vs$B4!#!5GBY!Gy!4 z#+1S|!wkX9#q7r1zyf1YU`b$^Vg+O6VfA8dVZ&n6V9R0KU`Jt>V~=4U<6z=&;%MP` z;iTiV;;iC=aj9|TaP4s8aI0}=aIYT`KN5Rn{wU&6#iNgpF7XKQMDfh=BJnEmrtz-v z$?&D{ZSfQE8}OG1pb6*+R0%u@G6{MK4hXRc1qe+EBMGYs=ZT<*=!w*be2H?2Mu;wm zNr+{LorqJ3yNM4-a7jc-Y)Fzx+DLXuu}FnUtx1zf+e!DxaLB~S?8x4b^^hGyQhj6} zyFbo;Jofm8oQ7PJJczuKe2D^)f|tUABAKF#;+T?@Qi;-!vXpY33W183%8KeWRX^1w zH4U{kbvShc^$raljU0^+O)1SHEefp&tut*7?Gzm>9WR|NT?X9*Jrq3`y%l{r{Wt>@ z12=;;Lk7bnBMc)SqXT0O<17<0lNgf+Qz_FLGcL0ta}aX_^AQUri!Mt7OFzpUD;KLB zYaZ(Y8z!4PjyUv zw+nX#_bv}5j}gyXo>^XOUKQSG-T^)sK5@PPz81dk{M`I*{MGzl1(*bE1xf^V1Zf1# z1@iG_7Y^#afrzlG=&dt4~>;K7Tr_ zL!e`=)2IultEF3@d!Z+#_eyW;8P~J0XEXXV`d<2j1_TCn2Car@hQ@|9MleR&MkPkK z#>&Py#up}XCT~rSO(jfUo9>&5nkAdPqHa=>)3b?9*x9_b!eo?4zYUKm~uUL)T0-Vxs0KGHtr9|CEn??`Cu*JNIxsSDo9gE|R%Z!JO zcZi=!5J@OX#7Oi>+(=SNs!t|Mj!Zs(W%z0^g)`;dYsA;?uUFqFzG+OQOpQ;yO|wdy zP8UzFd`t8;;_YRIS;ogq(aef>#P6csU1wQm&1K7E*XK~@q~yZpdgOk|)5`15=gluJ zASj3`_)+LkxLTxE)Kkn;TwFp_5?2aU>R!5A_N;88T(Z2ef~g|A61OtyJ@DTB{a%$( z)l9WQbyp2k?) z4mAvO57&=yjns{Djn<8EkJXR!j5kj3O*BsmPPTm%`PlhM@>AcG+|=;&lj+GB&6&B` zXS1twW^+5A?LME(d(7V~1TMlXMlPW*r7ROI=d94JRIYNbwysI8jjXG$FKn1@>~Fem z-fo3_LHUxhO}bsY!@kq9E4@3hr?0{&fo=0*O)3k6B&gZf?h-v#cw9`YhDKn7qCBqi|gyhsvANCOcD5&jUW z{4DSYAUG&6EDSUvB)<^n&zfKLp!>hl^U%TQkcgr~{X_;h^AFT(;h!iw zs17zpE%0P>#h$@|;Uc(HDNrXKxcOgTMea~07+aRYZHnk5!PYahT!-a~q`MxcAAIaB ze>X|D1s~!=KJ=u!C>v|(k%LDdgbCEKt|`Yj-YiqyrNkK^By%oruZENMHi7JmS0XA- zPk^*cx=_TxNx^3_44Ztc6c@46ttYDxK7iF7=e@jZ1O}2IXB8F-rO(+37UfcS6RRIP z8==)F=tdFiflidsnE)=Db2B^gqT$y9q7Q7ul;}M8d+_KwP3Q6{l%Fx8d>Fj)&#nO) zR`O!{NM(`C;A=l7Rt&S`P}L0RLGlPINS@NqfDaX}lYZpA>uMVEI~b;vk6O+SQsl`b z0U9u&spQ%8@~QN!cZqV8&g9mNqiVL|%t?}vl%1Z z*J@Gn%&zj_Ij!FhzI5!c&K<_&#TgQ>zDCaIc;Eni)c#B!SEBvd7VabcW-V6Z7paXi zqBvO1Y?=J5M|01ysK>0#}aPl%; zNKkwQw+nR8x72RX=qx_a0(kuLUDODRlBa`1txQA%KxcvMTdz+mgifytS&{MS7i%R_ zgX4cv8tpNZCHFfU_P&1=sVfQqk~JEHJ@NgJw?{3IWgi3qDWDl!azrfFX-QE*XAelA z4BIG}dV)Y;1|Z`E>74wM12}VLKcUPKUMoY((sRBE{K=^K-SUg*PY4v0A15tOUKK+m zA2c-j@IDHOy5A!S@%@A#ar)=1Vdls9%xKFpi9fvt889mi>Kb=*`9%N+E<2=k`)+nC z!eSu!b>b-t( zODwD+fTH4)G#dySSeK}=Oke#)0vPSGt48d`P3bzH#Rk zgxlJ);mn=0?Hm9Mt4QK@Lz5(eFClaO^sfPhSQQ7JQ}Wa&q2-vmm-_pbvZAfJJ;80X zbQE_nKjID*oqZwM-{RK&#fy+Y^tEp&j|C!SX;KN%TI^i-aBtn??JC3OjY4iyV88RL{(9m7`w-|s(-kmy|5p4p3K;zV2tCPz!`&~ysdu*1^ke6Xu z$8H9OMH>>uQo!wyB$pwC|4oAKH3yf(J%}^T>`od@9KJ6wM!Ch^i7-wgr|uj0Hx)Q5 zuHZ`eMG}Ay3$UXNc_po+K2(7rt04P|ihraZCK`fLi*g(_NW$+5%*IwZ zwn{JD!_LKDH}!F#u-uK2W?T}<45gL;F{O;S_@yVIe)xc8ma&nwbILaK>Iu@-w{NFA zhzSIB-#3<|cxa!{&jliD?m#i~*9anNVa4nEsl7NmQQK30t(BaYqqXrKlcbs}mn7-V z=De=|OtLHc-2ojDf!>_H`^ginzD(IzL|xI`14~uW4(F`h4N|IIS0c|qcG!}wqwUM5 zZJkX*ee;pBJG;hA5k!{dhJ8Ooi{1n-@g@flcb6elawnEap!UNzHWSCpoJfV8v{o{T zu{WlJ=&%u0nmVy;_~>n@UT2!Xze=Gdo~`&?KnlD@1*PsL5Rb2U%+>b5P-Jq;btj7J zmd9Oq+@i8tYmgZewW8S@wPNniZKV2E-P8=#<>r>HmlG4|#eaB=u97F1KucDF+3*%G z&J1z8Q?XUgSr>+a2&P&VOtF@zI=L7?h$tO{{IxjKyZGvdZRNOuoq&8OK%q?cNQiw& z1G)xDzJ;9e$a*Rw<$x4S%meOMtrj)zDN^KWfkvD%b zHZhE!st|3{l6HsSvicDmu};Fv=)P-t>}$8p>uIM1?`KoZq8?82pYeOsqQ|wa-WA^6 zzS>BH9veL&=tW+K*39+oWapd}+W!!l26IL{^|C%N+Q_e|jKXjsyud&aW1}ikwYk0q zw=)N2G>TtdhqhpusE=3aHJW%*7GJFw^U>s!aK5`e>+Tn|z3XiBWVLJQCd%Ke`gn=5 z;%S05Lj{P=W2jBCq#7*fz~)X}JVGu`QN^?`xem3&#K?Sjr)0uMmEpCkN^zKECVP94 zN2@)44WAk$0vZ1~27 zAm0@kfh_k|dS}(qxtcFr4D1HF`KwyA)Jd3s5a&)KytSuFN z4c*Z-SJIrwIRk1G_h1s{b&6PEduiUx31_*E=O{s1@AdNBC5#kKi^3x*i0vtnyH9J2+-9D??%}!U|N10x>%q^@=lK?rDYRGk>?w)y%w>94)7v!XX9|0 zQi3+5b(U3Vc}bJZsfRa~T$Oi?pU5eVSq&R5)(pj-Upd?*%D|dTA)@qRMFD_I35Y{ZYr zg%Jkou}U%H@T-@}#aAYx+=MA?-I6B_0%z^NKB&lPqEd4tlL^(H+x`!q-Ia7u@Km?6 ze}E>@)kZ=If|}YZy}CttA=u1i(Z0UXUZe@%!pDy&-Za9*h)yUsel&;!oaJ1fMbNzL z6fQjv@2!-1V}yTO?E`{G54-q2O$Tr7P!K3+S*vbKF{W)sh2g+`#AsAfRPXXgM^SxB zDD7Q&0+aXkEauro1=W_tT;khNVTnfG2CK(z8ZL`o8!%II2yZJEg1CJor5}}?WVP96 zyEU1>Q@5_8;kFbiW#0q&?|nLUeM_@cTvu>KRrZ!GX7KcQ7)s(>y>HY*zdu|`nxVTR z1cOLM>%8P-5?WPa9#0yyQYI`SN0~yMrUjKYJa%hl?r60%KO8fDUu3zq(j`8tw!zDN zP9QB9^Qz{gE3wtS@e%u!7h6ICG5<>3}` zNR*Qa(fM69=cy&^NW}?7(Rb{+3dE;ovRTEGZcVHW5tV>P)d7PhbsW6{_S*|YWySE3_YQ=SZ(h*#p8(7h_ z437p~1yuHR)%0Ud8G7+pi@V{~R)ccm9}i-La7xS2ykhN)p8L|?>FCcg6M@r{!V%oL zeuI?IC{<&J%K7c!gxYpOFy<{e5Z`actTt@zREyieMlS@V0589U!y)19(2U5-2KH9Z z;`q2gp@WjBg!?^$L;t;teab@Y3bUHEHZmT~8~o#f-q$2{0azbC*dkefObt`S4YR`` zH8~xN)Qru=A|)iI7>>gAQ;5b}<5r(Ny5fmzu!z_6HDD&I;c^m5b@ z`>~=H-ZoH2PY}76%Q|LotA85!g z$)I4N;oy;wksf|{1_k*69R?-2sQ20BEAvPNq9C%_Zsz%yymROEF*JI)tADLr z+@LD$=`bKR9O=TN?K`Rc(o^Kf7BvM|9L^tQer~zH2Dw+2wT%&Dl>t`;ik!UCqd`-0 zHk4aEQD>gV_3c0ie`JfmYkiOb>RYkh+fjc%nd2m7^H{+&<-RX+KAp5HIlNUkHy_*d z+@|5jhA$J;1BZv26h;ULHsCGni;R7ql)YfUIN+smGw$V;ou0<2G+0Cxw5#UQ_()G- zyh%Yt$0INpL2kkgep$L8ovNuF;4CdR-^Szymm%*QqEr2QD7^ID(%- zZa_ll(+RIOU8G`7RKmZXJ^RceX=xfD4JOLhJUw`e7<^nw-g_25F*DyTSj;%n>)sRo z?&G8b$%j=+Q{pbKFmuPEwwcnUxG%)$X9r@%C6-sBbGy##_ki2dW-#E6fOwlHO44?@o_BT3fb@LTxRIbtni&;d#7t zU3S7U>8`~S_h>ZJeDkw!#A0n-p*t6p&EXOYGL@z;enDEUOeJRSsu#57lD>S5S_+13 z-hxh7MBR2eN3Rf-Z1i>@vCvNWw=305zAot-3EE7ahHrI;Qp&j~FIcyzG?9mQaH9Pq zCmDmJ-&)l9olEhYI}N-y8Azopt#w`xNko>qVimW|+*;+<(MU0#KzzB@6OWVRApP(9dw=Ys*PrS9 zD~OBr+i@CwY^L07iN}%ss~e_0RbFWOe3(BZ>|%Is(S)J;P<(T8ZYKRhH8rH`a5S~6 z$!Xj*4JjqpKP2PvEjGqz%nT16DYC|$an>h)BB@nTZWPOHk9Bb& zRrlky0o!n~h21djGS_#;L*Ww7HH)Fac@%ZYAOy3>4mMv@UV_X+uQE3=N_oFn#S)~n z8NNX+iKjhmWCO=GZ6s_6WQb_0<$H9KkCys$hEp9pqqdecO*Qf1n_H(`hxQSJ{5;%- zHt*tc*{Z{|p)?pBmL*+LJmRH@$PXxpFAe%Vr7l_UDg|kycqsZRKl<6-MTgMI!UpY` zNk6qM*_(ZdqLsST^W2lZ>POBd?`U9QN8;4t%c4Z%bY%m|ox4SAaf6{*|EXz#A2`$4 z@gneNBhjN#ai%7Ff_Z8Jg&UO7D9(Zca-YjD_@gSKK6{}$jw!&5aj55-QQ75-Z@f8e zQi%yh#Pt7`T=jvxB7=BvukLvR7WUQ!+M0(LlAU!CR(%at?+rOu!(|Z`G&PoQ9&N5* zzT`KZ5*A_w2b1nX-%r|NJyl+Gcu2SDmYd9VsgDV-eWfaQ3SSfMduPdoJlR1`!Y)Wh zkIYo^-4jCKdim8So0r&-hjobGTWx^%Rg;}6;x=kCn%g!}G$)12^@yI-g#LoewCYTlP|UQUp~I!GkTRTb%O zUpwO2%aS9`Yo@gZ)2JCtL<26ntpi7cm6(*;62aO8Z4{deW*+i9bJ)ff1qtjOG0QX7 zw|d-Ff(b3crqttGN9X}pTnT}l+xXa0M=9*ebonGqhExI}N`wV1woOO4AG~DJ@giIq zl*`{Gv`I?{i|>_G3Z}aZyRNLnjPzzr%iS@12p9H$c@dzCf^Y`x-`#T zqsX$$T!LPy#Pvc`1-v%wc7PlG(H8xY!MAa)bwT~x6`19^u|ManDeqIeNa%R{a+8XU1<0;zo`R<<3GS?SKes#y z9pU#?BxNj$dVEGVjw>c6EUC(;p_%X1XOyOcp9}4#RMPBD67eA_)9;I-jK0}c!Xw<5 z=i0GpFP}K^bZg%*QRq#W>_nE?zP{>RfA{*T_EoTVQnh5dUW%tb+skfO7;6XU$-!Nn zdKWrLL6OU_Br-8i(QvE!qIQ5;|c0d;daTa68I^D66Nx%etd0?!Ir z`;FcE#W_zVpr-YT~Ue&#o9r?jVEE>`CiN*?+<+y(A ze3fGIkxzD->FI4|$?G(6Zj(l81WNXFga6GrGW!Md_Lm*
  • +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 W|I)^Hm^PjO0000>>" + text +} + +func (self *Monochrome) Green(text string) string { + return text +} diff --git a/vendor/github.com/franela/goblin/reporting.go b/vendor/github.com/franela/goblin/reporting.go new file mode 100644 index 0000000..1d67d66 --- /dev/null +++ b/vendor/github.com/franela/goblin/reporting.go @@ -0,0 +1,137 @@ +package goblin + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +type Reporter interface { + beginDescribe(string) + endDescribe() + begin() + end() + failure(*Failure) + itTook(time.Duration) + itFailed(string) + itPassed(string) + itIsPending(string) +} + +type TextFancier interface { + Red(text string) string + Gray(text string) string + Cyan(text string) string + Green(text string) string + WithCheck(text string) string +} + +type DetailedReporter struct { + level, failed, passed, pending int + failures []*Failure + executionTime, totalExecutionTime time.Duration + fancy TextFancier +} + +func (r *DetailedReporter) SetTextFancier(f TextFancier) { + r.fancy = f +} + +type TerminalFancier struct { +} + +func (self *TerminalFancier) Red(text string) string { + return "\033[31m" + text + "\033[0m" +} + +func (self *TerminalFancier) Gray(text string) string { + return "\033[90m" + text + "\033[0m" +} + +func (self *TerminalFancier) Cyan(text string) string { + return "\033[36m" + text + "\033[0m" +} + +func (self *TerminalFancier) Green(text string) string { + return "\033[32m" + text + "\033[0m" +} + +func (self *TerminalFancier) WithCheck(text string) string { + return "\033[32m\u2713\033[0m " + text +} + +func (r *DetailedReporter) getSpace() string { + return strings.Repeat(" ", (r.level+1)*2) +} + +func (r *DetailedReporter) failure(failure *Failure) { + r.failures = append(r.failures, failure) +} + +func (r *DetailedReporter) print(text string) { + fmt.Printf("%v%v\n", r.getSpace(), text) +} + +func (r *DetailedReporter) printWithCheck(text string) { + fmt.Printf("%v%v\n", r.getSpace(), r.fancy.WithCheck(text)) +} + +func (r *DetailedReporter) beginDescribe(name string) { + fmt.Println("") + r.print(name) + r.level++ +} + +func (r *DetailedReporter) endDescribe() { + r.level-- +} + +func (r *DetailedReporter) itTook(duration time.Duration) { + r.executionTime = duration + r.totalExecutionTime += duration +} + +func (r *DetailedReporter) itFailed(name string) { + r.failed++ + r.print(r.fancy.Red(strconv.Itoa(r.failed) + ") " + name)) +} + +func (r *DetailedReporter) itPassed(name string) { + r.passed++ + r.printWithCheck(r.fancy.Gray(name)) +} + +func (r *DetailedReporter) itIsPending(name string) { + r.pending++ + r.print(r.fancy.Cyan("- " + name)) +} + +func (r *DetailedReporter) begin() { +} + +func (r *DetailedReporter) end() { + comp := fmt.Sprintf("%d tests complete", r.passed) + t := fmt.Sprintf("(%d ms)", r.totalExecutionTime/time.Millisecond) + + //fmt.Printf("\n\n \033[32m%d tests complete\033[0m \033[90m(%d ms)\033[0m\n", r.passed, r.totalExecutionTime/time.Millisecond) + fmt.Printf("\n\n %v %v\n", r.fancy.Green(comp), r.fancy.Gray(t)) + + if r.pending > 0 { + pend := fmt.Sprintf("%d test(s) pending", r.pending) + fmt.Printf(" %v\n\n", r.fancy.Cyan(pend)) + } + + if len(r.failures) > 0 { + fmt.Printf("%s \n\n", r.fancy.Red(fmt.Sprintf(" %d tests failed:", len(r.failures)))) + + } + + for i, failure := range r.failures { + fmt.Printf(" %d) %s:\n\n", i+1, failure.testName) + fmt.Printf(" %s\n", r.fancy.Red(failure.message)) + for _, stackItem := range failure.stack { + fmt.Printf(" %s\n", r.fancy.Gray(stackItem)) + } + } +} diff --git a/vendor/github.com/franela/goblin/resolver.go b/vendor/github.com/franela/goblin/resolver.go new file mode 100644 index 0000000..125fcec --- /dev/null +++ b/vendor/github.com/franela/goblin/resolver.go @@ -0,0 +1,21 @@ +package goblin + +import ( + "runtime/debug" + "strings" +) + +func ResolveStack(skip int) []string { + return cleanStack(debug.Stack(), skip) +} + +func cleanStack(stack []byte, skip int) []string { + arrayStack := strings.Split(string(stack), "\n") + var finalStack []string + for i := skip; i < len(arrayStack); i++ { + if strings.Contains(arrayStack[i], ".go") { + finalStack = append(finalStack, arrayStack[i]) + } + } + return finalStack +} diff --git a/vendor/vendor.json b/vendor/vendor.json index b753746..3c62460 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -144,6 +144,12 @@ "revision": "c0d7d3282e4c14991a4814de7eae4774e388de61", "revisionTime": "2016-10-07T22:43:33Z" }, + { + "checksumSHA1": "ip3Xz/dILw2RMM6Z0MzAj5TUZ/c=", + "path": "github.com/franela/goblin", + "revision": "2fa789fd0c6b7975acbfb89a04830b4081a7b0e9", + "revisionTime": "2017-01-11T05:10:28Z" + }, { "checksumSHA1": "cVyhKIRI2gQrgpn5qrBeAqErmWM=", "path": "github.com/go-ini/ini", @@ -175,5 +181,5 @@ "revisionTime": "2016-10-06T02:47:49Z" } ], - "rootPath": "github.com/drone-plugins/drone-terraform" + "rootPath": "github.com/jmccann/drone-terraform" } From 551bdf8b83f4ef486eb706c8ca9408f69257b952 Mon Sep 17 00:00:00 2001 From: Jacob McCann Date: Tue, 30 May 2017 09:47:32 -0500 Subject: [PATCH 2/6] Minor update --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 81dc427..214ff78 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Docker image for the Drone Terraform plugin # -# docker build --rm=true -t jmccann/drone-terraform:latest . +# docker build -t jmccann/drone-terraform:latest . FROM golang:1.8-alpine AS builder COPY ./*.go ./src/ COPY ./vendor/ ./src/ From 69e77144a1eaf09290cb69ca2ca7a238c0ec86ea Mon Sep 17 00:00:00 2001 From: Jacob McCann Date: Tue, 30 May 2017 09:48:34 -0500 Subject: [PATCH 3/6] Remove secrets handling and add copying TF_VAR_ vars to lowercase names --- plugin.go | 20 ++++++++++---------- plugin_test.go | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/plugin.go b/plugin.go index 4cd9f55..f84a406 100644 --- a/plugin.go +++ b/plugin.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "os/exec" + "regexp" "strings" "time" @@ -53,9 +54,7 @@ func (p Plugin) Exec() error { var commands []*exec.Cmd - if len(p.Config.Secrets) != 0 { - exportSecrets(p.Config.Secrets) - } + CopyTfEnv() if p.Config.Cacert != "" { commands = append(commands, installCaCert(p.Config.Cacert)) @@ -108,9 +107,14 @@ func installCaCert(cacert string) *exec.Cmd { ) } -func exportSecrets(secrets map[string]string) { - for k, v := range secrets { - os.Setenv(fmt.Sprintf("%s", k), fmt.Sprintf("%s", os.Getenv(v))) +func CopyTfEnv() { + tfVar := regexp.MustCompile(`^TF_VAR_.*$`) + for _, e := range os.Environ() { + pair := strings.Split(e, "=") + if tfVar.MatchString(pair[0]) { + name := strings.Split(pair[0], "TF_VAR_") + os.Setenv(fmt.Sprintf("TF_VAR_%s", strings.ToLower(name[1])), pair[1]) + } } } @@ -187,10 +191,6 @@ func planCommand(config Config) *exec.Cmd { args = append(args, "-var") args = append(args, fmt.Sprintf("%s=%s", k, v)) } - for k, v := range config.Secrets { - args = append(args, "-var") - args = append(args, fmt.Sprintf("%s=%s", k, os.Getenv(v))) - } if config.Parallelism > 0 { args = append(args, fmt.Sprintf("-parallelism=%d", config.Parallelism)) } diff --git a/plugin_test.go b/plugin_test.go index d7cc9da..b951d8b 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -1,9 +1,12 @@ package main import ( + "os" "os/exec" "reflect" "testing" + + . "github.com/franela/goblin" ) func Test_destroyCommand(t *testing.T) { @@ -131,3 +134,21 @@ func Test_planCommand(t *testing.T) { }) } } + +func TestPlugin(t *testing.T) { + g := Goblin(t) + + g.Describe("CopyTfEnv", func() { + g.It("Should create copies of TF_VAR_ to lowercase", func() { + // Set some initial TF_VAR_ that are uppercase + os.Setenv("TF_VAR_SOMETHING", "some value") + os.Setenv("TF_VAR_SOMETHING_ELSE", "some other value") + + CopyTfEnv() + + // Make sure new env vars exist with proper values + g.Assert(os.Getenv("TF_VAR_something")).Equal("some value") + g.Assert(os.Getenv("TF_VAR_something_else")).Equal("some other value") + }) + }) +} From 9749b0b8f948bbe142a91487e33ee902f25b3911 Mon Sep 17 00:00:00 2001 From: Jacob McCann Date: Wed, 6 Sep 2017 09:29:04 -0500 Subject: [PATCH 4/6] Fix parsing values containing = from os environment --- plugin.go | 2 +- plugin_test.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugin.go b/plugin.go index f84a406..093e321 100644 --- a/plugin.go +++ b/plugin.go @@ -110,7 +110,7 @@ func installCaCert(cacert string) *exec.Cmd { func CopyTfEnv() { tfVar := regexp.MustCompile(`^TF_VAR_.*$`) for _, e := range os.Environ() { - pair := strings.Split(e, "=") + pair := strings.SplitN(e, "=", 2) if tfVar.MatchString(pair[0]) { name := strings.Split(pair[0], "TF_VAR_") os.Setenv(fmt.Sprintf("TF_VAR_%s", strings.ToLower(name[1])), pair[1]) diff --git a/plugin_test.go b/plugin_test.go index b951d8b..6073d9c 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -143,12 +143,14 @@ func TestPlugin(t *testing.T) { // Set some initial TF_VAR_ that are uppercase os.Setenv("TF_VAR_SOMETHING", "some value") os.Setenv("TF_VAR_SOMETHING_ELSE", "some other value") + os.Setenv("TF_VAR_BASE64", "dGVzdA==") CopyTfEnv() // Make sure new env vars exist with proper values g.Assert(os.Getenv("TF_VAR_something")).Equal("some value") g.Assert(os.Getenv("TF_VAR_something_else")).Equal("some other value") + g.Assert(os.Getenv("TF_VAR_base64")).Equal("dGVzdA==") }) }) } From 00912b6d7558844787161b4645d169b0c421469f Mon Sep 17 00:00:00 2001 From: Jacob McCann Date: Wed, 6 Sep 2017 10:24:24 -0500 Subject: [PATCH 5/6] Update docs about secrets --- DOCS.md | 59 +++++++++++++++++++-------------------------------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/DOCS.md b/DOCS.md index 63d97f5..8b024c1 100644 --- a/DOCS.md +++ b/DOCS.md @@ -29,8 +29,11 @@ pipeline: + app_version: 1.0.0 ``` -Example configuration passing secrets to terraform via `vars`. The following -example will call `terraform apply -var my_secret=${TERRAFORM_SECRET}`: +Example configuration passing secrets to terraform. Please read +https://www.terraform.io/docs/configuration/variables.html#environment-variables +for more details. + +**Drone 0.6+**: ```diff pipeline: @@ -38,7 +41,19 @@ pipeline: image: jmccann/drone-terraform:1 plan: false + secrets: -+ my_secret: TERRAFORM_SECRET ++ - source: terraform_secret ++ target: tf_var_my_secret +``` + +**Drone 0.5**: + +```diff +pipeline: + terraform: + image: jmccann/drone-terraform:1 + plan: false ++ environment: ++ TF_VAR_MY_SECRET: ${TERRAFORM_SECRET} ``` You may be passing sensitive vars to your terraform commands. If you do not want @@ -138,36 +153,6 @@ pipeline: + parallelism: 2 ``` -If you need to set different ENV secrets for multiple `terraform` steps you can utilize `secrets`. -The following example shows using different remotes secrets each step. - -```yaml -pipeline: - dev_terraform: - image: jmccann/drone-terraform:1 - plan: false - init_options: - backend_config: - - "bucket=my-terraform-config-bucket" - - "key=tf-states/my-project" - - "region=us-east-1" -+ secrets: -+ AWS_ACCESS_KEY_ID: DEV_AWS_ACCESS_KEY_ID -+ AWS_SECRET_ACCESS_KEY: DEV_AWS_SECRET_ACCESS_KEY - - prod_terraform: - image: jmccann/drone-terraform:1 - plan: false - init_options: - backend_config: - - "bucket=my-terraform-config-bucket" - - "key=tf-states/my-project" - - "region=us-east-1" -+ secrets: -+ AWS_ACCESS_KEY_ID: PROD_AWS_ACCESS_KEY_ID -+ AWS_SECRET_ACCESS_KEY: PROD_AWS_SECRET_ACCESS_KEY -``` - Destroying the service can be done using the boolean `destory` option. Keep in mind that Fastly won't allow a service with active version be destoryed. Use `force_destroy` option in the service definition for terraform to handle it. ```yaml @@ -205,12 +190,6 @@ var_files : a list of variable files to pass to the Terraform `plan` and `apply` commands. Each value is passed as a `-var-file ` option. -secrets -: a map of variables to pass to the Terraform `plan` and `apply` commands as well as setting envvars. -The `key` is the var and ENV to set. The `value` is the ENV to read the value from. -* Each entry generate a terraform var as follows: `-var =$` -* Additionally each entry generate sets and envvar as follows: `key=$value` - ca_cert : ca cert to add to your environment to allow terraform to use internal/private resources @@ -227,4 +206,4 @@ parallelism : The number of concurrent operations as Terraform walks its graph. destroy (boolean) -: Destroys the service (still requires [`force_destroy`](https://www.terraform.io/docs/providers/fastly/r/service_v1.html#force_destroy) option to be set in the service definition) \ No newline at end of file +: Destroys the service (still requires [`force_destroy`](https://www.terraform.io/docs/providers/fastly/r/service_v1.html#force_destroy) option to be set in the service definition) From e59d29cb5bcda88bfb760a94ac9bd0a6c056f980 Mon Sep 17 00:00:00 2001 From: Jacob McCann Date: Wed, 6 Sep 2017 10:28:25 -0500 Subject: [PATCH 6/6] Document additional way to pass secrets with Drone 0.5 --- DOCS.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/DOCS.md b/DOCS.md index 8b024c1..adf12d8 100644 --- a/DOCS.md +++ b/DOCS.md @@ -49,11 +49,18 @@ pipeline: ```diff pipeline: - terraform: + terraform_1: image: jmccann/drone-terraform:1 plan: false + environment: + TF_VAR_MY_SECRET: ${TERRAFORM_SECRET} + + terraform_2: + image: jmccann/drone-terraform:1 + plan: false ++ sensitive: true ++ vars: ++ my_secret: ${TERRAFORM_SECRET} ``` You may be passing sensitive vars to your terraform commands. If you do not want