diff --git a/command/apply.go b/command/apply.go index 8001cfe07..0687116a8 100644 --- a/command/apply.go +++ b/command/apply.go @@ -7,8 +7,8 @@ import ( "sort" "strings" + "github.com/hashicorp/go-getter" "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/terraform" ) @@ -76,7 +76,7 @@ func (c *ApplyCommand) Run(args []string) int { if !c.Destroy && maybeInit { // Do a detect to determine if we need to do an init + apply. - if detected, err := module.Detect(configPath, pwd); err != nil { + if detected, err := getter.Detect(configPath, pwd, getter.Detectors); err != nil { c.Ui.Error(fmt.Sprintf( "Invalid path: %s", err)) return 1 diff --git a/command/command_test.go b/command/command_test.go index 2b9f93dd1..954579c3d 100644 --- a/command/command_test.go +++ b/command/command_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/hashicorp/go-getter" "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/terraform" ) @@ -73,7 +74,7 @@ func testModule(t *testing.T, name string) *module.Tree { t.Fatalf("err: %s", err) } - s := &module.FolderStorage{StorageDir: tempDir(t)} + s := &getter.FolderStorage{StorageDir: tempDir(t)} if err := mod.Load(s, module.GetModeGet); err != nil { t.Fatalf("err: %s", err) } diff --git a/command/init.go b/command/init.go index fb842d08d..1b92c0806 100644 --- a/command/init.go +++ b/command/init.go @@ -6,6 +6,7 @@ import ( "os" "strings" + "github.com/hashicorp/go-getter" "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/terraform" @@ -75,7 +76,7 @@ func (c *InitCommand) Run(args []string) int { } // Detect - source, err = module.Detect(source, pwd) + source, err = getter.Detect(source, pwd, getter.Detectors) if err != nil { c.Ui.Error(fmt.Sprintf( "Error with module source: %s", err)) diff --git a/command/meta.go b/command/meta.go index af4a52302..3a12de02f 100644 --- a/command/meta.go +++ b/command/meta.go @@ -9,6 +9,7 @@ import ( "path/filepath" "strconv" + "github.com/hashicorp/go-getter" "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/state" "github.com/hashicorp/terraform/terraform" @@ -330,9 +331,9 @@ func (m *Meta) flagSet(n string) *flag.FlagSet { // moduleStorage returns the module.Storage implementation used to store // modules for commands. -func (m *Meta) moduleStorage(root string) module.Storage { +func (m *Meta) moduleStorage(root string) getter.Storage { return &uiModuleStorage{ - Storage: &module.FolderStorage{ + Storage: &getter.FolderStorage{ StorageDir: filepath.Join(root, "modules"), }, Ui: m.Ui, diff --git a/command/module_storage.go b/command/module_storage.go index e17786a80..5bb832897 100644 --- a/command/module_storage.go +++ b/command/module_storage.go @@ -3,14 +3,14 @@ package command import ( "fmt" - "github.com/hashicorp/terraform/config/module" + "github.com/hashicorp/go-getter" "github.com/mitchellh/cli" ) // uiModuleStorage implements module.Storage and is just a proxy to output // to the UI any Get operations. type uiModuleStorage struct { - Storage module.Storage + Storage getter.Storage Ui cli.Ui } diff --git a/command/module_storage_test.go b/command/module_storage_test.go index b77c2b5f7..97a5ed7ae 100644 --- a/command/module_storage_test.go +++ b/command/module_storage_test.go @@ -3,9 +3,9 @@ package command import ( "testing" - "github.com/hashicorp/terraform/config/module" + "github.com/hashicorp/go-getter" ) func TestUiModuleStorage_impl(t *testing.T) { - var _ module.Storage = new(uiModuleStorage) + var _ getter.Storage = new(uiModuleStorage) } diff --git a/config/lang/y.go b/config/lang/y.go index e7dd185ae..fd0693f15 100644 --- a/config/lang/y.go +++ b/config/lang/y.go @@ -30,7 +30,10 @@ const INTEGER = 57355 const FLOAT = 57356 const STRING = 57357 -var parserToknames = []string{ +var parserToknames = [...]string{ + "$end", + "error", + "$unk", "PROGRAM_BRACKET_LEFT", "PROGRAM_BRACKET_RIGHT", "PROGRAM_STRING_START", @@ -44,7 +47,7 @@ var parserToknames = []string{ "FLOAT", "STRING", } -var parserStatenames = []string{} +var parserStatenames = [...]string{} const parserEofCode = 1 const parserErrCode = 2 @@ -53,7 +56,7 @@ const parserMaxDepth = 200 //line lang.y:165 //line yacctab:1 -var parserExca = []int{ +var parserExca = [...]int{ -1, 1, 1, -1, -2, 0, @@ -67,75 +70,103 @@ var parserStates []string const parserLast = 30 -var parserAct = []int{ +var parserAct = [...]int{ 9, 20, 16, 16, 7, 7, 3, 18, 10, 8, 1, 17, 14, 12, 13, 6, 6, 19, 8, 22, 15, 23, 24, 11, 2, 25, 16, 21, 4, 5, } -var parserPact = []int{ +var parserPact = [...]int{ 1, -1000, 1, -1000, -1000, -1000, -1000, 0, -1000, 15, 0, 1, -1000, -1000, -1, -1000, 0, -8, 0, -1000, -1000, 12, -9, -1000, 0, -9, } -var parserPgo = []int{ +var parserPgo = [...]int{ 0, 0, 29, 28, 23, 6, 27, 10, } -var parserR1 = []int{ +var parserR1 = [...]int{ 0, 7, 7, 4, 4, 5, 5, 2, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 3, } -var parserR2 = []int{ +var parserR2 = [...]int{ 0, 0, 1, 1, 2, 1, 1, 3, 3, 1, 1, 1, 3, 1, 4, 0, 3, 1, 1, } -var parserChk = []int{ +var parserChk = [...]int{ -1000, -7, -4, -5, -3, -2, 15, 4, -5, -1, 8, -4, 13, 14, 12, 5, 11, -1, 8, -1, 9, -6, -1, 9, 10, -1, } -var parserDef = []int{ +var parserDef = [...]int{ 1, -2, 2, 3, 5, 6, 18, 0, 4, 0, 0, 9, 10, 11, 13, 7, 0, 0, 15, 12, 8, 0, 17, 14, 0, 16, } -var parserTok1 = []int{ +var parserTok1 = [...]int{ 1, } -var parserTok2 = []int{ +var parserTok2 = [...]int{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, } -var parserTok3 = []int{ +var parserTok3 = [...]int{ 0, } +var parserErrorMessages = [...]struct { + state int + token int + msg string +}{} + //line yaccpar:1 /* parser for yacc output */ -var parserDebug = 0 +var ( + parserDebug = 0 + parserErrorVerbose = false +) type parserLexer interface { Lex(lval *parserSymType) int Error(s string) } +type parserParser interface { + Parse(parserLexer) int + Lookahead() int +} + +type parserParserImpl struct { + lookahead func() int +} + +func (p *parserParserImpl) Lookahead() int { + return p.lookahead() +} + +func parserNewParser() parserParser { + p := &parserParserImpl{ + lookahead: func() int { return -1 }, + } + return p +} + const parserFlag = -1000 func parserTokname(c int) string { - // 4 is TOKSTART above - if c >= 4 && c-4 < len(parserToknames) { - if parserToknames[c-4] != "" { - return parserToknames[c-4] + if c >= 1 && c-1 < len(parserToknames) { + if parserToknames[c-1] != "" { + return parserToknames[c-1] } } return __yyfmt__.Sprintf("tok-%v", c) @@ -150,51 +181,129 @@ func parserStatname(s int) string { return __yyfmt__.Sprintf("state-%v", s) } -func parserlex1(lex parserLexer, lval *parserSymType) int { - c := 0 - char := lex.Lex(lval) +func parserErrorMessage(state, lookAhead int) string { + const TOKSTART = 4 + + if !parserErrorVerbose { + return "syntax error" + } + + for _, e := range parserErrorMessages { + if e.state == state && e.token == lookAhead { + return "syntax error: " + e.msg + } + } + + res := "syntax error: unexpected " + parserTokname(lookAhead) + + // To match Bison, suggest at most four expected tokens. + expected := make([]int, 0, 4) + + // Look for shiftable tokens. + base := parserPact[state] + for tok := TOKSTART; tok-1 < len(parserToknames); tok++ { + if n := base + tok; n >= 0 && n < parserLast && parserChk[parserAct[n]] == tok { + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + } + + if parserDef[state] == -2 { + i := 0 + for parserExca[i] != -1 || parserExca[i+1] != state { + i += 2 + } + + // Look for tokens that we accept or reduce. + for i += 2; parserExca[i] >= 0; i += 2 { + tok := parserExca[i] + if tok < TOKSTART || parserExca[i+1] == 0 { + continue + } + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + + // If the default action is to accept or reduce, give up. + if parserExca[i+1] != 0 { + return res + } + } + + for i, tok := range expected { + if i == 0 { + res += ", expecting " + } else { + res += " or " + } + res += parserTokname(tok) + } + return res +} + +func parserlex1(lex parserLexer, lval *parserSymType) (char, token int) { + token = 0 + char = lex.Lex(lval) if char <= 0 { - c = parserTok1[0] + token = parserTok1[0] goto out } if char < len(parserTok1) { - c = parserTok1[char] + token = parserTok1[char] goto out } if char >= parserPrivate { if char < parserPrivate+len(parserTok2) { - c = parserTok2[char-parserPrivate] + token = parserTok2[char-parserPrivate] goto out } } for i := 0; i < len(parserTok3); i += 2 { - c = parserTok3[i+0] - if c == char { - c = parserTok3[i+1] + token = parserTok3[i+0] + if token == char { + token = parserTok3[i+1] goto out } } out: - if c == 0 { - c = parserTok2[1] /* unknown char */ + if token == 0 { + token = parserTok2[1] /* unknown char */ } if parserDebug >= 3 { - __yyfmt__.Printf("lex %s(%d)\n", parserTokname(c), uint(char)) + __yyfmt__.Printf("lex %s(%d)\n", parserTokname(token), uint(char)) } - return c + return char, token } func parserParse(parserlex parserLexer) int { + return parserNewParser().Parse(parserlex) +} + +func (parserrcvr *parserParserImpl) Parse(parserlex parserLexer) int { var parsern int var parserlval parserSymType var parserVAL parserSymType + var parserDollar []parserSymType + _ = parserDollar // silence set and not used parserS := make([]parserSymType, parserMaxDepth) Nerrs := 0 /* number of errors */ Errflag := 0 /* error recovery flag */ parserstate := 0 parserchar := -1 + parsertoken := -1 // parserchar translated into internal numbering + parserrcvr.lookahead = func() int { return parserchar } + defer func() { + // Make sure we report no lookahead when not parsing. + parserstate = -1 + parserchar = -1 + parsertoken = -1 + }() parserp := -1 goto parserstack @@ -207,7 +316,7 @@ ret1: parserstack: /* put a state and value onto the stack */ if parserDebug >= 4 { - __yyfmt__.Printf("char %v in %v\n", parserTokname(parserchar), parserStatname(parserstate)) + __yyfmt__.Printf("char %v in %v\n", parserTokname(parsertoken), parserStatname(parserstate)) } parserp++ @@ -225,15 +334,16 @@ parsernewstate: goto parserdefault /* simple state */ } if parserchar < 0 { - parserchar = parserlex1(parserlex, &parserlval) + parserchar, parsertoken = parserlex1(parserlex, &parserlval) } - parsern += parserchar + parsern += parsertoken if parsern < 0 || parsern >= parserLast { goto parserdefault } parsern = parserAct[parsern] - if parserChk[parsern] == parserchar { /* valid shift */ + if parserChk[parsern] == parsertoken { /* valid shift */ parserchar = -1 + parsertoken = -1 parserVAL = parserlval parserstate = parsern if Errflag > 0 { @@ -247,7 +357,7 @@ parserdefault: parsern = parserDef[parserstate] if parsern == -2 { if parserchar < 0 { - parserchar = parserlex1(parserlex, &parserlval) + parserchar, parsertoken = parserlex1(parserlex, &parserlval) } /* look through exception table */ @@ -260,7 +370,7 @@ parserdefault: } for xi += 2; ; xi += 2 { parsern = parserExca[xi+0] - if parsern < 0 || parsern == parserchar { + if parsern < 0 || parsern == parsertoken { break } } @@ -273,11 +383,11 @@ parserdefault: /* error ... attempt to resume parsing */ switch Errflag { case 0: /* brand new error */ - parserlex.Error("syntax error") + parserlex.Error(parserErrorMessage(parserstate, parsertoken)) Nerrs++ if parserDebug >= 1 { __yyfmt__.Printf("%s", parserStatname(parserstate)) - __yyfmt__.Printf(" saw %s\n", parserTokname(parserchar)) + __yyfmt__.Printf(" saw %s\n", parserTokname(parsertoken)) } fallthrough @@ -305,12 +415,13 @@ parserdefault: case 3: /* no shift yet; clobber input char */ if parserDebug >= 2 { - __yyfmt__.Printf("error recovery discards %s\n", parserTokname(parserchar)) + __yyfmt__.Printf("error recovery discards %s\n", parserTokname(parsertoken)) } - if parserchar == parserEofCode { + if parsertoken == parserEofCode { goto ret1 } parserchar = -1 + parsertoken = -1 goto parsernewstate /* try again in the same state */ } } @@ -325,6 +436,13 @@ parserdefault: _ = parserpt // guard against "declared and not used" parserp -= parserR2[parsern] + // parserp is now the index of $0. Perform the default action. Iff the + // reduced production is ε, $1 is possibly out of range. + if parserp+1 >= len(parserS) { + nyys := make([]parserSymType, len(parserS)*2) + copy(nyys, parserS) + parserS = nyys + } parserVAL = parserS[parserp+1] /* consult goto table to find next state */ @@ -344,6 +462,7 @@ parserdefault: switch parsernt { case 1: + parserDollar = parserS[parserpt-0 : parserpt+1] //line lang.y:35 { parserResult = &ast.LiteralNode{ @@ -353,9 +472,10 @@ parserdefault: } } case 2: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:43 { - parserResult = parserS[parserpt-0].node + parserResult = parserDollar[1].node // We want to make sure that the top value is always a Concat // so that the return value is always a string type from an @@ -365,28 +485,30 @@ parserdefault: // because functionally the AST is the same, but we do that because // it makes for an easy literal check later (to check if a string // has any interpolations). - if _, ok := parserS[parserpt-0].node.(*ast.Concat); !ok { - if n, ok := parserS[parserpt-0].node.(*ast.LiteralNode); !ok || n.Typex != ast.TypeString { + if _, ok := parserDollar[1].node.(*ast.Concat); !ok { + if n, ok := parserDollar[1].node.(*ast.LiteralNode); !ok || n.Typex != ast.TypeString { parserResult = &ast.Concat{ - Exprs: []ast.Node{parserS[parserpt-0].node}, - Posx: parserS[parserpt-0].node.Pos(), + Exprs: []ast.Node{parserDollar[1].node}, + Posx: parserDollar[1].node.Pos(), } } } } case 3: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:66 { - parserVAL.node = parserS[parserpt-0].node + parserVAL.node = parserDollar[1].node } case 4: + parserDollar = parserS[parserpt-2 : parserpt+1] //line lang.y:70 { var result []ast.Node - if c, ok := parserS[parserpt-1].node.(*ast.Concat); ok { - result = append(c.Exprs, parserS[parserpt-0].node) + if c, ok := parserDollar[1].node.(*ast.Concat); ok { + result = append(c.Exprs, parserDollar[2].node) } else { - result = []ast.Node{parserS[parserpt-1].node, parserS[parserpt-0].node} + result = []ast.Node{parserDollar[1].node, parserDollar[2].node} } parserVAL.node = &ast.Concat{ @@ -395,89 +517,103 @@ parserdefault: } } case 5: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:86 { - parserVAL.node = parserS[parserpt-0].node + parserVAL.node = parserDollar[1].node } case 6: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:90 { - parserVAL.node = parserS[parserpt-0].node + parserVAL.node = parserDollar[1].node } case 7: + parserDollar = parserS[parserpt-3 : parserpt+1] //line lang.y:96 { - parserVAL.node = parserS[parserpt-1].node + parserVAL.node = parserDollar[2].node } case 8: + parserDollar = parserS[parserpt-3 : parserpt+1] //line lang.y:102 { - parserVAL.node = parserS[parserpt-1].node + parserVAL.node = parserDollar[2].node } case 9: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:106 { - parserVAL.node = parserS[parserpt-0].node + parserVAL.node = parserDollar[1].node } case 10: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:110 { parserVAL.node = &ast.LiteralNode{ - Value: parserS[parserpt-0].token.Value.(int), + Value: parserDollar[1].token.Value.(int), Typex: ast.TypeInt, - Posx: parserS[parserpt-0].token.Pos, + Posx: parserDollar[1].token.Pos, } } case 11: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:118 { parserVAL.node = &ast.LiteralNode{ - Value: parserS[parserpt-0].token.Value.(float64), + Value: parserDollar[1].token.Value.(float64), Typex: ast.TypeFloat, - Posx: parserS[parserpt-0].token.Pos, + Posx: parserDollar[1].token.Pos, } } case 12: + parserDollar = parserS[parserpt-3 : parserpt+1] //line lang.y:126 { parserVAL.node = &ast.Arithmetic{ - Op: parserS[parserpt-1].token.Value.(ast.ArithmeticOp), - Exprs: []ast.Node{parserS[parserpt-2].node, parserS[parserpt-0].node}, - Posx: parserS[parserpt-2].node.Pos(), + Op: parserDollar[2].token.Value.(ast.ArithmeticOp), + Exprs: []ast.Node{parserDollar[1].node, parserDollar[3].node}, + Posx: parserDollar[1].node.Pos(), } } case 13: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:134 { - parserVAL.node = &ast.VariableAccess{Name: parserS[parserpt-0].token.Value.(string), Posx: parserS[parserpt-0].token.Pos} + parserVAL.node = &ast.VariableAccess{Name: parserDollar[1].token.Value.(string), Posx: parserDollar[1].token.Pos} } case 14: + parserDollar = parserS[parserpt-4 : parserpt+1] //line lang.y:138 { - parserVAL.node = &ast.Call{Func: parserS[parserpt-3].token.Value.(string), Args: parserS[parserpt-1].nodeList, Posx: parserS[parserpt-3].token.Pos} + parserVAL.node = &ast.Call{Func: parserDollar[1].token.Value.(string), Args: parserDollar[3].nodeList, Posx: parserDollar[1].token.Pos} } case 15: + parserDollar = parserS[parserpt-0 : parserpt+1] //line lang.y:143 { parserVAL.nodeList = nil } case 16: + parserDollar = parserS[parserpt-3 : parserpt+1] //line lang.y:147 { - parserVAL.nodeList = append(parserS[parserpt-2].nodeList, parserS[parserpt-0].node) + parserVAL.nodeList = append(parserDollar[1].nodeList, parserDollar[3].node) } case 17: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:151 { - parserVAL.nodeList = append(parserVAL.nodeList, parserS[parserpt-0].node) + parserVAL.nodeList = append(parserVAL.nodeList, parserDollar[1].node) } case 18: + parserDollar = parserS[parserpt-1 : parserpt+1] //line lang.y:157 { parserVAL.node = &ast.LiteralNode{ - Value: parserS[parserpt-0].token.Value.(string), + Value: parserDollar[1].token.Value.(string), Typex: ast.TypeString, - Posx: parserS[parserpt-0].token.Pos, + Posx: parserDollar[1].token.Pos, } } } diff --git a/config/module/copy_dir.go b/config/module/copy_dir.go new file mode 100644 index 000000000..f2ae63b77 --- /dev/null +++ b/config/module/copy_dir.go @@ -0,0 +1,76 @@ +package module + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// copyDir copies the src directory contents into dst. Both directories +// should already exist. +func copyDir(dst, src string) error { + src, err := filepath.EvalSymlinks(src) + if err != nil { + return err + } + + walkFn := func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if path == src { + return nil + } + + if strings.HasPrefix(filepath.Base(path), ".") { + // Skip any dot files + if info.IsDir() { + return filepath.SkipDir + } else { + return nil + } + } + + // The "path" has the src prefixed to it. We need to join our + // destination with the path without the src on it. + dstPath := filepath.Join(dst, path[len(src):]) + + // If we have a directory, make that subdirectory, then continue + // the walk. + if info.IsDir() { + if path == filepath.Join(src, dst) { + // dst is in src; don't walk it. + return nil + } + + if err := os.MkdirAll(dstPath, 0755); err != nil { + return err + } + + return nil + } + + // If we have a file, copy the contents. + srcF, err := os.Open(path) + if err != nil { + return err + } + defer srcF.Close() + + dstF, err := os.Create(dstPath) + if err != nil { + return err + } + defer dstF.Close() + + if _, err := io.Copy(dstF, srcF); err != nil { + return err + } + + // Chmod it + return os.Chmod(dstPath, info.Mode()) + } + + return filepath.Walk(src, walkFn) +} diff --git a/config/module/get.go b/config/module/get.go index 3820e65f2..cba15277f 100644 --- a/config/module/get.go +++ b/config/module/get.go @@ -1,6 +1,9 @@ package module import ( + "io/ioutil" + "os" + "github.com/hashicorp/go-getter" ) @@ -23,6 +26,36 @@ const ( GetModeUpdate ) +// GetCopy is the same as Get except that it downloads a copy of the +// module represented by source. +// +// This copy will omit and dot-prefixed files (such as .git/, .hg/) and +// can't be updated on its own. +func GetCopy(dst, src string) error { + // Create the temporary directory to do the real Get to + tmpDir, err := ioutil.TempDir("", "tf") + if err != nil { + return err + } + if err := os.RemoveAll(tmpDir); err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + // Get to that temporary dir + if err := getter.Get(tmpDir, src); err != nil { + return err + } + + // Make sure the destination exists + if err := os.MkdirAll(dst, 0755); err != nil { + return err + } + + // Copy to the final location + return copyDir(dst, tmpDir) +} + func getStorage(s getter.Storage, key string, src string, mode GetMode) (string, bool, error) { // Get the module with the level specified if we were told to. if mode > GetModeNone { diff --git a/helper/resource/testing.go b/helper/resource/testing.go index eaa0cbf71..0b53c3c61 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -11,6 +11,7 @@ import ( "strings" "testing" + "github.com/hashicorp/go-getter" "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/terraform" ) @@ -198,7 +199,7 @@ func testStep( } // Load the modules - modStorage := &module.FolderStorage{ + modStorage := &getter.FolderStorage{ StorageDir: filepath.Join(cfgPath, ".tfmodules"), } err = mod.Load(modStorage, module.GetModeGet)