Skip to content

Commit 4133bae

Browse files
committed
copy from terraform
0 parents  commit 4133bae

File tree

112 files changed

+3475
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+3475
-0
lines changed

copy_dir.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package getter
2+
3+
import (
4+
"io"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
)
9+
10+
// copyDir copies the src directory contents into dst. Both directories
11+
// should already exist.
12+
func copyDir(dst, src string) error {
13+
src, err := filepath.EvalSymlinks(src)
14+
if err != nil {
15+
return err
16+
}
17+
18+
walkFn := func(path string, info os.FileInfo, err error) error {
19+
if err != nil {
20+
return err
21+
}
22+
if path == src {
23+
return nil
24+
}
25+
26+
if strings.HasPrefix(filepath.Base(path), ".") {
27+
// Skip any dot files
28+
if info.IsDir() {
29+
return filepath.SkipDir
30+
} else {
31+
return nil
32+
}
33+
}
34+
35+
// The "path" has the src prefixed to it. We need to join our
36+
// destination with the path without the src on it.
37+
dstPath := filepath.Join(dst, path[len(src):])
38+
39+
// If we have a directory, make that subdirectory, then continue
40+
// the walk.
41+
if info.IsDir() {
42+
if path == filepath.Join(src, dst) {
43+
// dst is in src; don't walk it.
44+
return nil
45+
}
46+
47+
if err := os.MkdirAll(dstPath, 0755); err != nil {
48+
return err
49+
}
50+
51+
return nil
52+
}
53+
54+
// If we have a file, copy the contents.
55+
srcF, err := os.Open(path)
56+
if err != nil {
57+
return err
58+
}
59+
defer srcF.Close()
60+
61+
dstF, err := os.Create(dstPath)
62+
if err != nil {
63+
return err
64+
}
65+
defer dstF.Close()
66+
67+
if _, err := io.Copy(dstF, srcF); err != nil {
68+
return err
69+
}
70+
71+
// Chmod it
72+
return os.Chmod(dstPath, info.Mode())
73+
}
74+
75+
return filepath.Walk(src, walkFn)
76+
}

detect.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package getter
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
7+
"github.com/hashicorp/terraform/helper/url"
8+
)
9+
10+
// Detector defines the interface that an invalid URL or a URL with a blank
11+
// scheme is passed through in order to determine if its shorthand for
12+
// something else well-known.
13+
type Detector interface {
14+
// Detect will detect whether the string matches a known pattern to
15+
// turn it into a proper URL.
16+
Detect(string, string) (string, bool, error)
17+
}
18+
19+
// Detectors is the list of detectors that are tried on an invalid URL.
20+
// This is also the order they're tried (index 0 is first).
21+
var Detectors []Detector
22+
23+
func init() {
24+
Detectors = []Detector{
25+
new(GitHubDetector),
26+
new(BitBucketDetector),
27+
new(FileDetector),
28+
}
29+
}
30+
31+
// Detect turns a source string into another source string if it is
32+
// detected to be of a known pattern.
33+
//
34+
// This is safe to be called with an already valid source string: Detect
35+
// will just return it.
36+
func Detect(src string, pwd string) (string, error) {
37+
getForce, getSrc := getForcedGetter(src)
38+
39+
// Separate out the subdir if there is one, we don't pass that to detect
40+
getSrc, subDir := getDirSubdir(getSrc)
41+
42+
u, err := url.Parse(getSrc)
43+
if err == nil && u.Scheme != "" {
44+
// Valid URL
45+
return src, nil
46+
}
47+
48+
for _, d := range Detectors {
49+
result, ok, err := d.Detect(getSrc, pwd)
50+
if err != nil {
51+
return "", err
52+
}
53+
if !ok {
54+
continue
55+
}
56+
57+
var detectForce string
58+
detectForce, result = getForcedGetter(result)
59+
result, detectSubdir := getDirSubdir(result)
60+
61+
// If we have a subdir from the detection, then prepend it to our
62+
// requested subdir.
63+
if detectSubdir != "" {
64+
if subDir != "" {
65+
subDir = filepath.Join(detectSubdir, subDir)
66+
} else {
67+
subDir = detectSubdir
68+
}
69+
}
70+
if subDir != "" {
71+
u, err := url.Parse(result)
72+
if err != nil {
73+
return "", fmt.Errorf("Error parsing URL: %s", err)
74+
}
75+
u.Path += "//" + subDir
76+
result = u.String()
77+
}
78+
79+
// Preserve the forced getter if it exists. We try to use the
80+
// original set force first, followed by any force set by the
81+
// detector.
82+
if getForce != "" {
83+
result = fmt.Sprintf("%s::%s", getForce, result)
84+
} else if detectForce != "" {
85+
result = fmt.Sprintf("%s::%s", detectForce, result)
86+
}
87+
88+
return result, nil
89+
}
90+
91+
return "", fmt.Errorf("invalid source string: %s", src)
92+
}

detect_bitbucket.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package getter
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"net/url"
8+
"strings"
9+
)
10+
11+
// BitBucketDetector implements Detector to detect BitBucket URLs and turn
12+
// them into URLs that the Git or Hg Getter can understand.
13+
type BitBucketDetector struct{}
14+
15+
func (d *BitBucketDetector) Detect(src, _ string) (string, bool, error) {
16+
if len(src) == 0 {
17+
return "", false, nil
18+
}
19+
20+
if strings.HasPrefix(src, "bitbucket.org/") {
21+
return d.detectHTTP(src)
22+
}
23+
24+
return "", false, nil
25+
}
26+
27+
func (d *BitBucketDetector) detectHTTP(src string) (string, bool, error) {
28+
u, err := url.Parse("https://" + src)
29+
if err != nil {
30+
return "", true, fmt.Errorf("error parsing BitBucket URL: %s", err)
31+
}
32+
33+
// We need to get info on this BitBucket repository to determine whether
34+
// it is Git or Hg.
35+
var info struct {
36+
SCM string `json:"scm"`
37+
}
38+
infoUrl := "https://api.bitbucket.org/1.0/repositories" + u.Path
39+
resp, err := http.Get(infoUrl)
40+
if err != nil {
41+
return "", true, fmt.Errorf("error looking up BitBucket URL: %s", err)
42+
}
43+
if resp.StatusCode == 403 {
44+
// A private repo
45+
return "", true, fmt.Errorf(
46+
"shorthand BitBucket URL can't be used for private repos, " +
47+
"please use a full URL")
48+
}
49+
dec := json.NewDecoder(resp.Body)
50+
if err := dec.Decode(&info); err != nil {
51+
return "", true, fmt.Errorf("error looking up BitBucket URL: %s", err)
52+
}
53+
54+
switch info.SCM {
55+
case "git":
56+
if !strings.HasSuffix(u.Path, ".git") {
57+
u.Path += ".git"
58+
}
59+
60+
return "git::" + u.String(), true, nil
61+
case "hg":
62+
return "hg::" + u.String(), true, nil
63+
default:
64+
return "", true, fmt.Errorf("unknown BitBucket SCM type: %s", info.SCM)
65+
}
66+
}

detect_bitbucket_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package getter
2+
3+
import (
4+
"net/http"
5+
"strings"
6+
"testing"
7+
)
8+
9+
const testBBUrl = "https://bitbucket.org/hashicorp/tf-test-git"
10+
11+
func TestBitBucketDetector(t *testing.T) {
12+
t.Parallel()
13+
14+
if _, err := http.Get(testBBUrl); err != nil {
15+
t.Log("internet may not be working, skipping BB tests")
16+
t.Skip()
17+
}
18+
19+
cases := []struct {
20+
Input string
21+
Output string
22+
}{
23+
// HTTP
24+
{
25+
"bitbucket.org/hashicorp/tf-test-git",
26+
"git::https://bitbucket.org/hashicorp/tf-test-git.git",
27+
},
28+
{
29+
"bitbucket.org/hashicorp/tf-test-git.git",
30+
"git::https://bitbucket.org/hashicorp/tf-test-git.git",
31+
},
32+
{
33+
"bitbucket.org/hashicorp/tf-test-hg",
34+
"hg::https://bitbucket.org/hashicorp/tf-test-hg",
35+
},
36+
}
37+
38+
pwd := "/pwd"
39+
f := new(BitBucketDetector)
40+
for i, tc := range cases {
41+
var err error
42+
for i := 0; i < 3; i++ {
43+
var output string
44+
var ok bool
45+
output, ok, err = f.Detect(tc.Input, pwd)
46+
if err != nil {
47+
if strings.Contains(err.Error(), "invalid character") {
48+
continue
49+
}
50+
51+
t.Fatalf("err: %s", err)
52+
}
53+
if !ok {
54+
t.Fatal("not ok")
55+
}
56+
57+
if output != tc.Output {
58+
t.Fatalf("%d: bad: %#v", i, output)
59+
}
60+
61+
break
62+
}
63+
if i >= 3 {
64+
t.Fatalf("failure from bitbucket: %s", err)
65+
}
66+
}
67+
}

detect_file.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package getter
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"runtime"
8+
)
9+
10+
// FileDetector implements Detector to detect file paths.
11+
type FileDetector struct{}
12+
13+
func (d *FileDetector) Detect(src, pwd string) (string, bool, error) {
14+
if len(src) == 0 {
15+
return "", false, nil
16+
}
17+
18+
if !filepath.IsAbs(src) {
19+
if pwd == "" {
20+
return "", true, fmt.Errorf(
21+
"relative paths require a module with a pwd")
22+
}
23+
24+
// Stat the pwd to determine if its a symbolic link. If it is,
25+
// then the pwd becomes the original directory. Otherwise,
26+
// `filepath.Join` below does some weird stuff.
27+
//
28+
// We just ignore if the pwd doesn't exist. That error will be
29+
// caught later when we try to use the URL.
30+
if fi, err := os.Lstat(pwd); !os.IsNotExist(err) {
31+
if err != nil {
32+
return "", true, err
33+
}
34+
if fi.Mode()&os.ModeSymlink != 0 {
35+
pwd, err = os.Readlink(pwd)
36+
if err != nil {
37+
return "", true, err
38+
}
39+
}
40+
}
41+
42+
src = filepath.Join(pwd, src)
43+
}
44+
45+
return fmtFileURL(src), true, nil
46+
}
47+
48+
func fmtFileURL(path string) string {
49+
if runtime.GOOS == "windows" {
50+
// Make sure we're using "/" on Windows. URLs are "/"-based.
51+
path = filepath.ToSlash(path)
52+
return fmt.Sprintf("file://%s", path)
53+
}
54+
55+
// Make sure that we don't start with "/" since we add that below.
56+
if path[0] == '/' {
57+
path = path[1:]
58+
}
59+
return fmt.Sprintf("file:///%s", path)
60+
}

0 commit comments

Comments
 (0)