diff options
author | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
---|---|---|
committer | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
commit | 107c1cdb09c575aa2f61d97f48d8587eb6bada4c (patch) | |
tree | ca7d008643efc555c388baeaf1d986e0b6b3e28c /vendor/github.com/hashicorp/terraform/configs/configload | |
parent | 844b5a68d8af4791755b8f0ad293cc99f5959183 (diff) | |
download | terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.gz terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.zst terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.zip |
Upgrade to 0.12
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/configs/configload')
12 files changed, 1244 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/copy_dir.go b/vendor/github.com/hashicorp/terraform/configs/configload/copy_dir.go new file mode 100644 index 0000000..ebbeb3b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/copy_dir.go | |||
@@ -0,0 +1,125 @@ | |||
1 | package configload | ||
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 | |||
23 | if path == src { | ||
24 | return nil | ||
25 | } | ||
26 | |||
27 | if strings.HasPrefix(filepath.Base(path), ".") { | ||
28 | // Skip any dot files | ||
29 | if info.IsDir() { | ||
30 | return filepath.SkipDir | ||
31 | } else { | ||
32 | return nil | ||
33 | } | ||
34 | } | ||
35 | |||
36 | // The "path" has the src prefixed to it. We need to join our | ||
37 | // destination with the path without the src on it. | ||
38 | dstPath := filepath.Join(dst, path[len(src):]) | ||
39 | |||
40 | // we don't want to try and copy the same file over itself. | ||
41 | if eq, err := sameFile(path, dstPath); eq { | ||
42 | return nil | ||
43 | } else if err != nil { | ||
44 | return err | ||
45 | } | ||
46 | |||
47 | // If we have a directory, make that subdirectory, then continue | ||
48 | // the walk. | ||
49 | if info.IsDir() { | ||
50 | if path == filepath.Join(src, dst) { | ||
51 | // dst is in src; don't walk it. | ||
52 | return nil | ||
53 | } | ||
54 | |||
55 | if err := os.MkdirAll(dstPath, 0755); err != nil { | ||
56 | return err | ||
57 | } | ||
58 | |||
59 | return nil | ||
60 | } | ||
61 | |||
62 | // If the current path is a symlink, recreate the symlink relative to | ||
63 | // the dst directory | ||
64 | if info.Mode()&os.ModeSymlink == os.ModeSymlink { | ||
65 | target, err := os.Readlink(path) | ||
66 | if err != nil { | ||
67 | return err | ||
68 | } | ||
69 | |||
70 | return os.Symlink(target, dstPath) | ||
71 | } | ||
72 | |||
73 | // If we have a file, copy the contents. | ||
74 | srcF, err := os.Open(path) | ||
75 | if err != nil { | ||
76 | return err | ||
77 | } | ||
78 | defer srcF.Close() | ||
79 | |||
80 | dstF, err := os.Create(dstPath) | ||
81 | if err != nil { | ||
82 | return err | ||
83 | } | ||
84 | defer dstF.Close() | ||
85 | |||
86 | if _, err := io.Copy(dstF, srcF); err != nil { | ||
87 | return err | ||
88 | } | ||
89 | |||
90 | // Chmod it | ||
91 | return os.Chmod(dstPath, info.Mode()) | ||
92 | } | ||
93 | |||
94 | return filepath.Walk(src, walkFn) | ||
95 | } | ||
96 | |||
97 | // sameFile tried to determine if to paths are the same file. | ||
98 | // If the paths don't match, we lookup the inode on supported systems. | ||
99 | func sameFile(a, b string) (bool, error) { | ||
100 | if a == b { | ||
101 | return true, nil | ||
102 | } | ||
103 | |||
104 | aIno, err := inode(a) | ||
105 | if err != nil { | ||
106 | if os.IsNotExist(err) { | ||
107 | return false, nil | ||
108 | } | ||
109 | return false, err | ||
110 | } | ||
111 | |||
112 | bIno, err := inode(b) | ||
113 | if err != nil { | ||
114 | if os.IsNotExist(err) { | ||
115 | return false, nil | ||
116 | } | ||
117 | return false, err | ||
118 | } | ||
119 | |||
120 | if aIno > 0 && aIno == bIno { | ||
121 | return true, nil | ||
122 | } | ||
123 | |||
124 | return false, nil | ||
125 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/doc.go b/vendor/github.com/hashicorp/terraform/configs/configload/doc.go new file mode 100644 index 0000000..8b615f9 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/doc.go | |||
@@ -0,0 +1,4 @@ | |||
1 | // Package configload knows how to install modules into the .terraform/modules | ||
2 | // directory and to load modules from those installed locations. It is used | ||
3 | // in conjunction with the LoadConfig function in the parent package. | ||
4 | package configload | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/getter.go b/vendor/github.com/hashicorp/terraform/configs/configload/getter.go new file mode 100644 index 0000000..4a3dace --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/getter.go | |||
@@ -0,0 +1,150 @@ | |||
1 | package configload | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "log" | ||
6 | "os" | ||
7 | "path/filepath" | ||
8 | |||
9 | cleanhttp "github.com/hashicorp/go-cleanhttp" | ||
10 | getter "github.com/hashicorp/go-getter" | ||
11 | ) | ||
12 | |||
13 | // We configure our own go-getter detector and getter sets here, because | ||
14 | // the set of sources we support is part of Terraform's documentation and | ||
15 | // so we don't want any new sources introduced in go-getter to sneak in here | ||
16 | // and work even though they aren't documented. This also insulates us from | ||
17 | // any meddling that might be done by other go-getter callers linked into our | ||
18 | // executable. | ||
19 | |||
20 | var goGetterDetectors = []getter.Detector{ | ||
21 | new(getter.GitHubDetector), | ||
22 | new(getter.BitBucketDetector), | ||
23 | new(getter.S3Detector), | ||
24 | new(getter.FileDetector), | ||
25 | } | ||
26 | |||
27 | var goGetterNoDetectors = []getter.Detector{} | ||
28 | |||
29 | var goGetterDecompressors = map[string]getter.Decompressor{ | ||
30 | "bz2": new(getter.Bzip2Decompressor), | ||
31 | "gz": new(getter.GzipDecompressor), | ||
32 | "xz": new(getter.XzDecompressor), | ||
33 | "zip": new(getter.ZipDecompressor), | ||
34 | |||
35 | "tar.bz2": new(getter.TarBzip2Decompressor), | ||
36 | "tar.tbz2": new(getter.TarBzip2Decompressor), | ||
37 | |||
38 | "tar.gz": new(getter.TarGzipDecompressor), | ||
39 | "tgz": new(getter.TarGzipDecompressor), | ||
40 | |||
41 | "tar.xz": new(getter.TarXzDecompressor), | ||
42 | "txz": new(getter.TarXzDecompressor), | ||
43 | } | ||
44 | |||
45 | var goGetterGetters = map[string]getter.Getter{ | ||
46 | "file": new(getter.FileGetter), | ||
47 | "git": new(getter.GitGetter), | ||
48 | "hg": new(getter.HgGetter), | ||
49 | "s3": new(getter.S3Getter), | ||
50 | "http": getterHTTPGetter, | ||
51 | "https": getterHTTPGetter, | ||
52 | } | ||
53 | |||
54 | var getterHTTPClient = cleanhttp.DefaultClient() | ||
55 | |||
56 | var getterHTTPGetter = &getter.HttpGetter{ | ||
57 | Client: getterHTTPClient, | ||
58 | Netrc: true, | ||
59 | } | ||
60 | |||
61 | // A reusingGetter is a helper for the module installer that remembers | ||
62 | // the final resolved addresses of all of the sources it has already been | ||
63 | // asked to install, and will copy from a prior installation directory if | ||
64 | // it has the same resolved source address. | ||
65 | // | ||
66 | // The keys in a reusingGetter are resolved and trimmed source addresses | ||
67 | // (with a scheme always present, and without any "subdir" component), | ||
68 | // and the values are the paths where each source was previously installed. | ||
69 | type reusingGetter map[string]string | ||
70 | |||
71 | // getWithGoGetter retrieves the package referenced in the given address | ||
72 | // into the installation path and then returns the full path to any subdir | ||
73 | // indicated in the address. | ||
74 | // | ||
75 | // The errors returned by this function are those surfaced by the underlying | ||
76 | // go-getter library, which have very inconsistent quality as | ||
77 | // end-user-actionable error messages. At this time we do not have any | ||
78 | // reasonable way to improve these error messages at this layer because | ||
79 | // the underlying errors are not separatelyr recognizable. | ||
80 | func (g reusingGetter) getWithGoGetter(instPath, addr string) (string, error) { | ||
81 | packageAddr, subDir := splitAddrSubdir(addr) | ||
82 | |||
83 | log.Printf("[DEBUG] will download %q to %s", packageAddr, instPath) | ||
84 | |||
85 | realAddr, err := getter.Detect(packageAddr, instPath, getter.Detectors) | ||
86 | if err != nil { | ||
87 | return "", err | ||
88 | } | ||
89 | |||
90 | var realSubDir string | ||
91 | realAddr, realSubDir = splitAddrSubdir(realAddr) | ||
92 | if realSubDir != "" { | ||
93 | subDir = filepath.Join(realSubDir, subDir) | ||
94 | } | ||
95 | |||
96 | if realAddr != packageAddr { | ||
97 | log.Printf("[TRACE] go-getter detectors rewrote %q to %q", packageAddr, realAddr) | ||
98 | } | ||
99 | |||
100 | if prevDir, exists := g[realAddr]; exists { | ||
101 | log.Printf("[TRACE] copying previous install %s to %s", prevDir, instPath) | ||
102 | err := os.Mkdir(instPath, os.ModePerm) | ||
103 | if err != nil { | ||
104 | return "", fmt.Errorf("failed to create directory %s: %s", instPath, err) | ||
105 | } | ||
106 | err = copyDir(instPath, prevDir) | ||
107 | if err != nil { | ||
108 | return "", fmt.Errorf("failed to copy from %s to %s: %s", prevDir, instPath, err) | ||
109 | } | ||
110 | } else { | ||
111 | log.Printf("[TRACE] fetching %q to %q", realAddr, instPath) | ||
112 | client := getter.Client{ | ||
113 | Src: realAddr, | ||
114 | Dst: instPath, | ||
115 | Pwd: instPath, | ||
116 | |||
117 | Mode: getter.ClientModeDir, | ||
118 | |||
119 | Detectors: goGetterNoDetectors, // we already did detection above | ||
120 | Decompressors: goGetterDecompressors, | ||
121 | Getters: goGetterGetters, | ||
122 | } | ||
123 | err = client.Get() | ||
124 | if err != nil { | ||
125 | return "", err | ||
126 | } | ||
127 | // Remember where we installed this so we might reuse this directory | ||
128 | // on subsequent calls to avoid re-downloading. | ||
129 | g[realAddr] = instPath | ||
130 | } | ||
131 | |||
132 | // Our subDir string can contain wildcards until this point, so that | ||
133 | // e.g. a subDir of * can expand to one top-level directory in a .tar.gz | ||
134 | // archive. Now that we've expanded the archive successfully we must | ||
135 | // resolve that into a concrete path. | ||
136 | var finalDir string | ||
137 | if subDir != "" { | ||
138 | finalDir, err = getter.SubdirGlob(instPath, subDir) | ||
139 | log.Printf("[TRACE] expanded %q to %q", subDir, finalDir) | ||
140 | if err != nil { | ||
141 | return "", err | ||
142 | } | ||
143 | } else { | ||
144 | finalDir = instPath | ||
145 | } | ||
146 | |||
147 | // If we got this far then we have apparently succeeded in downloading | ||
148 | // the requested object! | ||
149 | return filepath.Clean(finalDir), nil | ||
150 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/inode.go b/vendor/github.com/hashicorp/terraform/configs/configload/inode.go new file mode 100644 index 0000000..57df041 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/inode.go | |||
@@ -0,0 +1,21 @@ | |||
1 | // +build linux darwin openbsd netbsd solaris dragonfly | ||
2 | |||
3 | package configload | ||
4 | |||
5 | import ( | ||
6 | "fmt" | ||
7 | "os" | ||
8 | "syscall" | ||
9 | ) | ||
10 | |||
11 | // lookup the inode of a file on posix systems | ||
12 | func inode(path string) (uint64, error) { | ||
13 | stat, err := os.Stat(path) | ||
14 | if err != nil { | ||
15 | return 0, err | ||
16 | } | ||
17 | if st, ok := stat.Sys().(*syscall.Stat_t); ok { | ||
18 | return st.Ino, nil | ||
19 | } | ||
20 | return 0, fmt.Errorf("could not determine file inode") | ||
21 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/inode_freebsd.go b/vendor/github.com/hashicorp/terraform/configs/configload/inode_freebsd.go new file mode 100644 index 0000000..4dc28ea --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/inode_freebsd.go | |||
@@ -0,0 +1,21 @@ | |||
1 | // +build freebsd | ||
2 | |||
3 | package configload | ||
4 | |||
5 | import ( | ||
6 | "fmt" | ||
7 | "os" | ||
8 | "syscall" | ||
9 | ) | ||
10 | |||
11 | // lookup the inode of a file on posix systems | ||
12 | func inode(path string) (uint64, error) { | ||
13 | stat, err := os.Stat(path) | ||
14 | if err != nil { | ||
15 | return 0, err | ||
16 | } | ||
17 | if st, ok := stat.Sys().(*syscall.Stat_t); ok { | ||
18 | return uint64(st.Ino), nil | ||
19 | } | ||
20 | return 0, fmt.Errorf("could not determine file inode") | ||
21 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/inode_windows.go b/vendor/github.com/hashicorp/terraform/configs/configload/inode_windows.go new file mode 100644 index 0000000..0d22e67 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/inode_windows.go | |||
@@ -0,0 +1,8 @@ | |||
1 | // +build windows | ||
2 | |||
3 | package configload | ||
4 | |||
5 | // no syscall.Stat_t on windows, return 0 for inodes | ||
6 | func inode(path string) (uint64, error) { | ||
7 | return 0, nil | ||
8 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/loader.go b/vendor/github.com/hashicorp/terraform/configs/configload/loader.go new file mode 100644 index 0000000..416b48f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/loader.go | |||
@@ -0,0 +1,150 @@ | |||
1 | package configload | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "path/filepath" | ||
6 | |||
7 | "github.com/hashicorp/terraform/configs" | ||
8 | "github.com/hashicorp/terraform/registry" | ||
9 | "github.com/hashicorp/terraform/svchost/disco" | ||
10 | "github.com/spf13/afero" | ||
11 | ) | ||
12 | |||
13 | // A Loader instance is the main entry-point for loading configurations via | ||
14 | // this package. | ||
15 | // | ||
16 | // It extends the general config-loading functionality in the parent package | ||
17 | // "configs" to support installation of modules from remote sources and | ||
18 | // loading full configurations using modules that were previously installed. | ||
19 | type Loader struct { | ||
20 | // parser is used to read configuration | ||
21 | parser *configs.Parser | ||
22 | |||
23 | // modules is used to install and locate descendent modules that are | ||
24 | // referenced (directly or indirectly) from the root module. | ||
25 | modules moduleMgr | ||
26 | } | ||
27 | |||
28 | // Config is used with NewLoader to specify configuration arguments for the | ||
29 | // loader. | ||
30 | type Config struct { | ||
31 | // ModulesDir is a path to a directory where descendent modules are | ||
32 | // (or should be) installed. (This is usually the | ||
33 | // .terraform/modules directory, in the common case where this package | ||
34 | // is being loaded from the main Terraform CLI package.) | ||
35 | ModulesDir string | ||
36 | |||
37 | // Services is the service discovery client to use when locating remote | ||
38 | // module registry endpoints. If this is nil then registry sources are | ||
39 | // not supported, which should be true only in specialized circumstances | ||
40 | // such as in tests. | ||
41 | Services *disco.Disco | ||
42 | } | ||
43 | |||
44 | // NewLoader creates and returns a loader that reads configuration from the | ||
45 | // real OS filesystem. | ||
46 | // | ||
47 | // The loader has some internal state about the modules that are currently | ||
48 | // installed, which is read from disk as part of this function. If that | ||
49 | // manifest cannot be read then an error will be returned. | ||
50 | func NewLoader(config *Config) (*Loader, error) { | ||
51 | fs := afero.NewOsFs() | ||
52 | parser := configs.NewParser(fs) | ||
53 | reg := registry.NewClient(config.Services, nil) | ||
54 | |||
55 | ret := &Loader{ | ||
56 | parser: parser, | ||
57 | modules: moduleMgr{ | ||
58 | FS: afero.Afero{Fs: fs}, | ||
59 | CanInstall: true, | ||
60 | Dir: config.ModulesDir, | ||
61 | Services: config.Services, | ||
62 | Registry: reg, | ||
63 | }, | ||
64 | } | ||
65 | |||
66 | err := ret.modules.readModuleManifestSnapshot() | ||
67 | if err != nil { | ||
68 | return nil, fmt.Errorf("failed to read module manifest: %s", err) | ||
69 | } | ||
70 | |||
71 | return ret, nil | ||
72 | } | ||
73 | |||
74 | // ModulesDir returns the path to the directory where the loader will look for | ||
75 | // the local cache of remote module packages. | ||
76 | func (l *Loader) ModulesDir() string { | ||
77 | return l.modules.Dir | ||
78 | } | ||
79 | |||
80 | // RefreshModules updates the in-memory cache of the module manifest from the | ||
81 | // module manifest file on disk. This is not necessary in normal use because | ||
82 | // module installation and configuration loading are separate steps, but it | ||
83 | // can be useful in tests where module installation is done as a part of | ||
84 | // configuration loading by a helper function. | ||
85 | // | ||
86 | // Call this function after any module installation where an existing loader | ||
87 | // is already alive and may be used again later. | ||
88 | // | ||
89 | // An error is returned if the manifest file cannot be read. | ||
90 | func (l *Loader) RefreshModules() error { | ||
91 | if l == nil { | ||
92 | // Nothing to do, then. | ||
93 | return nil | ||
94 | } | ||
95 | return l.modules.readModuleManifestSnapshot() | ||
96 | } | ||
97 | |||
98 | // Parser returns the underlying parser for this loader. | ||
99 | // | ||
100 | // This is useful for loading other sorts of files than the module directories | ||
101 | // that a loader deals with, since then they will share the source code cache | ||
102 | // for this loader and can thus be shown as snippets in diagnostic messages. | ||
103 | func (l *Loader) Parser() *configs.Parser { | ||
104 | return l.parser | ||
105 | } | ||
106 | |||
107 | // Sources returns the source code cache for the underlying parser of this | ||
108 | // loader. This is a shorthand for l.Parser().Sources(). | ||
109 | func (l *Loader) Sources() map[string][]byte { | ||
110 | return l.parser.Sources() | ||
111 | } | ||
112 | |||
113 | // IsConfigDir returns true if and only if the given directory contains at | ||
114 | // least one Terraform configuration file. This is a wrapper around calling | ||
115 | // the same method name on the loader's parser. | ||
116 | func (l *Loader) IsConfigDir(path string) bool { | ||
117 | return l.parser.IsConfigDir(path) | ||
118 | } | ||
119 | |||
120 | // ImportSources writes into the receiver's source code the given source | ||
121 | // code buffers. | ||
122 | // | ||
123 | // This is useful in the situation where an ancillary loader is created for | ||
124 | // some reason (e.g. loading config from a plan file) but the cached source | ||
125 | // code from that loader must be imported into the "main" loader in order | ||
126 | // to return source code snapshots in diagnostic messages. | ||
127 | // | ||
128 | // loader.ImportSources(otherLoader.Sources()) | ||
129 | func (l *Loader) ImportSources(sources map[string][]byte) { | ||
130 | p := l.Parser() | ||
131 | for name, src := range sources { | ||
132 | p.ForceFileSource(name, src) | ||
133 | } | ||
134 | } | ||
135 | |||
136 | // ImportSourcesFromSnapshot writes into the receiver's source code the | ||
137 | // source files from the given snapshot. | ||
138 | // | ||
139 | // This is similar to ImportSources but knows how to unpack and flatten a | ||
140 | // snapshot data structure to get the corresponding flat source file map. | ||
141 | func (l *Loader) ImportSourcesFromSnapshot(snap *Snapshot) { | ||
142 | p := l.Parser() | ||
143 | for _, m := range snap.Modules { | ||
144 | baseDir := m.Dir | ||
145 | for fn, src := range m.Files { | ||
146 | fullPath := filepath.Join(baseDir, fn) | ||
147 | p.ForceFileSource(fullPath, src) | ||
148 | } | ||
149 | } | ||
150 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/loader_load.go b/vendor/github.com/hashicorp/terraform/configs/configload/loader_load.go new file mode 100644 index 0000000..93a9420 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/loader_load.go | |||
@@ -0,0 +1,97 @@ | |||
1 | package configload | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | version "github.com/hashicorp/go-version" | ||
7 | "github.com/hashicorp/hcl2/hcl" | ||
8 | "github.com/hashicorp/terraform/configs" | ||
9 | ) | ||
10 | |||
11 | // LoadConfig reads the Terraform module in the given directory and uses it as the | ||
12 | // root module to build the static module tree that represents a configuration, | ||
13 | // assuming that all required descendent modules have already been installed. | ||
14 | // | ||
15 | // If error diagnostics are returned, the returned configuration may be either | ||
16 | // nil or incomplete. In the latter case, cautious static analysis is possible | ||
17 | // in spite of the errors. | ||
18 | // | ||
19 | // LoadConfig performs the basic syntax and uniqueness validations that are | ||
20 | // required to process the individual modules, and also detects | ||
21 | func (l *Loader) LoadConfig(rootDir string) (*configs.Config, hcl.Diagnostics) { | ||
22 | rootMod, diags := l.parser.LoadConfigDir(rootDir) | ||
23 | if rootMod == nil { | ||
24 | return nil, diags | ||
25 | } | ||
26 | |||
27 | cfg, cDiags := configs.BuildConfig(rootMod, configs.ModuleWalkerFunc(l.moduleWalkerLoad)) | ||
28 | diags = append(diags, cDiags...) | ||
29 | |||
30 | return cfg, diags | ||
31 | } | ||
32 | |||
33 | // moduleWalkerLoad is a configs.ModuleWalkerFunc for loading modules that | ||
34 | // are presumed to have already been installed. A different function | ||
35 | // (moduleWalkerInstall) is used for installation. | ||
36 | func (l *Loader) moduleWalkerLoad(req *configs.ModuleRequest) (*configs.Module, *version.Version, hcl.Diagnostics) { | ||
37 | // Since we're just loading here, we expect that all referenced modules | ||
38 | // will be already installed and described in our manifest. However, we | ||
39 | // do verify that the manifest and the configuration are in agreement | ||
40 | // so that we can prompt the user to run "terraform init" if not. | ||
41 | |||
42 | key := l.modules.manifest.ModuleKey(req.Path) | ||
43 | record, exists := l.modules.manifest[key] | ||
44 | |||
45 | if !exists { | ||
46 | return nil, nil, hcl.Diagnostics{ | ||
47 | { | ||
48 | Severity: hcl.DiagError, | ||
49 | Summary: "Module not installed", | ||
50 | Detail: "This module is not yet installed. Run \"terraform init\" to install all modules required by this configuration.", | ||
51 | Subject: &req.CallRange, | ||
52 | }, | ||
53 | } | ||
54 | } | ||
55 | |||
56 | var diags hcl.Diagnostics | ||
57 | |||
58 | // Check for inconsistencies between manifest and config | ||
59 | if req.SourceAddr != record.SourceAddr { | ||
60 | diags = append(diags, &hcl.Diagnostic{ | ||
61 | Severity: hcl.DiagError, | ||
62 | Summary: "Module source has changed", | ||
63 | Detail: "The source address was changed since this module was installed. Run \"terraform init\" to install all modules required by this configuration.", | ||
64 | Subject: &req.SourceAddrRange, | ||
65 | }) | ||
66 | } | ||
67 | if !req.VersionConstraint.Required.Check(record.Version) { | ||
68 | diags = append(diags, &hcl.Diagnostic{ | ||
69 | Severity: hcl.DiagError, | ||
70 | Summary: "Module version requirements have changed", | ||
71 | Detail: fmt.Sprintf( | ||
72 | "The version requirements have changed since this module was installed and the installed version (%s) is no longer acceptable. Run \"terraform init\" to install all modules required by this configuration.", | ||
73 | record.Version, | ||
74 | ), | ||
75 | Subject: &req.SourceAddrRange, | ||
76 | }) | ||
77 | } | ||
78 | |||
79 | mod, mDiags := l.parser.LoadConfigDir(record.Dir) | ||
80 | diags = append(diags, mDiags...) | ||
81 | if mod == nil { | ||
82 | // nil specifically indicates that the directory does not exist or | ||
83 | // cannot be read, so in this case we'll discard any generic diagnostics | ||
84 | // returned from LoadConfigDir and produce our own context-sensitive | ||
85 | // error message. | ||
86 | return nil, nil, hcl.Diagnostics{ | ||
87 | { | ||
88 | Severity: hcl.DiagError, | ||
89 | Summary: "Module not installed", | ||
90 | Detail: fmt.Sprintf("This module's local cache directory %s could not be read. Run \"terraform init\" to install all modules required by this configuration.", record.Dir), | ||
91 | Subject: &req.CallRange, | ||
92 | }, | ||
93 | } | ||
94 | } | ||
95 | |||
96 | return mod, record.Version, diags | ||
97 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/loader_snapshot.go b/vendor/github.com/hashicorp/terraform/configs/configload/loader_snapshot.go new file mode 100644 index 0000000..44c6439 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/loader_snapshot.go | |||
@@ -0,0 +1,504 @@ | |||
1 | package configload | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "io" | ||
6 | "os" | ||
7 | "path/filepath" | ||
8 | "sort" | ||
9 | "time" | ||
10 | |||
11 | version "github.com/hashicorp/go-version" | ||
12 | "github.com/hashicorp/hcl2/hcl" | ||
13 | "github.com/hashicorp/terraform/configs" | ||
14 | "github.com/hashicorp/terraform/internal/modsdir" | ||
15 | "github.com/spf13/afero" | ||
16 | ) | ||
17 | |||
18 | // LoadConfigWithSnapshot is a variant of LoadConfig that also simultaneously | ||
19 | // creates an in-memory snapshot of the configuration files used, which can | ||
20 | // be later used to create a loader that may read only from this snapshot. | ||
21 | func (l *Loader) LoadConfigWithSnapshot(rootDir string) (*configs.Config, *Snapshot, hcl.Diagnostics) { | ||
22 | rootMod, diags := l.parser.LoadConfigDir(rootDir) | ||
23 | if rootMod == nil { | ||
24 | return nil, nil, diags | ||
25 | } | ||
26 | |||
27 | snap := &Snapshot{ | ||
28 | Modules: map[string]*SnapshotModule{}, | ||
29 | } | ||
30 | walker := l.makeModuleWalkerSnapshot(snap) | ||
31 | cfg, cDiags := configs.BuildConfig(rootMod, walker) | ||
32 | diags = append(diags, cDiags...) | ||
33 | |||
34 | addDiags := l.addModuleToSnapshot(snap, "", rootDir, "", nil) | ||
35 | diags = append(diags, addDiags...) | ||
36 | |||
37 | return cfg, snap, diags | ||
38 | } | ||
39 | |||
40 | // NewLoaderFromSnapshot creates a Loader that reads files only from the | ||
41 | // given snapshot. | ||
42 | // | ||
43 | // A snapshot-based loader cannot install modules, so calling InstallModules | ||
44 | // on the return value will cause a panic. | ||
45 | // | ||
46 | // A snapshot-based loader also has access only to configuration files. Its | ||
47 | // underlying parser does not have access to other files in the native | ||
48 | // filesystem, such as values files. For those, either use a normal loader | ||
49 | // (created by NewLoader) or use the configs.Parser API directly. | ||
50 | func NewLoaderFromSnapshot(snap *Snapshot) *Loader { | ||
51 | fs := snapshotFS{snap} | ||
52 | parser := configs.NewParser(fs) | ||
53 | |||
54 | ret := &Loader{ | ||
55 | parser: parser, | ||
56 | modules: moduleMgr{ | ||
57 | FS: afero.Afero{Fs: fs}, | ||
58 | CanInstall: false, | ||
59 | manifest: snap.moduleManifest(), | ||
60 | }, | ||
61 | } | ||
62 | |||
63 | return ret | ||
64 | } | ||
65 | |||
66 | // Snapshot is an in-memory representation of the source files from a | ||
67 | // configuration, which can be used as an alternative configurations source | ||
68 | // for a loader with NewLoaderFromSnapshot. | ||
69 | // | ||
70 | // The primary purpose of a Snapshot is to build the configuration portion | ||
71 | // of a plan file (see ../../plans/planfile) so that it can later be reloaded | ||
72 | // and used to recover the exact configuration that the plan was built from. | ||
73 | type Snapshot struct { | ||
74 | // Modules is a map from opaque module keys (suitable for use as directory | ||
75 | // names on all supported operating systems) to the snapshot information | ||
76 | // about each module. | ||
77 | Modules map[string]*SnapshotModule | ||
78 | } | ||
79 | |||
80 | // NewEmptySnapshot constructs and returns a snapshot containing only an empty | ||
81 | // root module. This is not useful for anything except placeholders in tests. | ||
82 | func NewEmptySnapshot() *Snapshot { | ||
83 | return &Snapshot{ | ||
84 | Modules: map[string]*SnapshotModule{ | ||
85 | "": &SnapshotModule{ | ||
86 | Files: map[string][]byte{}, | ||
87 | }, | ||
88 | }, | ||
89 | } | ||
90 | } | ||
91 | |||
92 | // SnapshotModule represents a single module within a Snapshot. | ||
93 | type SnapshotModule struct { | ||
94 | // Dir is the path, relative to the root directory given when the | ||
95 | // snapshot was created, where the module appears in the snapshot's | ||
96 | // virtual filesystem. | ||
97 | Dir string | ||
98 | |||
99 | // Files is a map from each configuration file filename for the | ||
100 | // module to a raw byte representation of the source file contents. | ||
101 | Files map[string][]byte | ||
102 | |||
103 | // SourceAddr is the source address given for this module in configuration. | ||
104 | SourceAddr string `json:"Source"` | ||
105 | |||
106 | // Version is the version of the module that is installed, or nil if | ||
107 | // the module is installed from a source that does not support versions. | ||
108 | Version *version.Version `json:"-"` | ||
109 | } | ||
110 | |||
111 | // moduleManifest constructs a module manifest based on the contents of | ||
112 | // the receiving snapshot. | ||
113 | func (s *Snapshot) moduleManifest() modsdir.Manifest { | ||
114 | ret := make(modsdir.Manifest) | ||
115 | |||
116 | for k, modSnap := range s.Modules { | ||
117 | ret[k] = modsdir.Record{ | ||
118 | Key: k, | ||
119 | Dir: modSnap.Dir, | ||
120 | SourceAddr: modSnap.SourceAddr, | ||
121 | Version: modSnap.Version, | ||
122 | } | ||
123 | } | ||
124 | |||
125 | return ret | ||
126 | } | ||
127 | |||
128 | // makeModuleWalkerSnapshot creates a configs.ModuleWalker that will exhibit | ||
129 | // the same lookup behaviors as l.moduleWalkerLoad but will additionally write | ||
130 | // source files from the referenced modules into the given snapshot. | ||
131 | func (l *Loader) makeModuleWalkerSnapshot(snap *Snapshot) configs.ModuleWalker { | ||
132 | return configs.ModuleWalkerFunc( | ||
133 | func(req *configs.ModuleRequest) (*configs.Module, *version.Version, hcl.Diagnostics) { | ||
134 | mod, v, diags := l.moduleWalkerLoad(req) | ||
135 | if diags.HasErrors() { | ||
136 | return mod, v, diags | ||
137 | } | ||
138 | |||
139 | key := l.modules.manifest.ModuleKey(req.Path) | ||
140 | record, exists := l.modules.manifest[key] | ||
141 | |||
142 | if !exists { | ||
143 | // Should never happen, since otherwise moduleWalkerLoader would've | ||
144 | // returned an error and we would've returned already. | ||
145 | panic(fmt.Sprintf("module %s is not present in manifest", key)) | ||
146 | } | ||
147 | |||
148 | addDiags := l.addModuleToSnapshot(snap, key, record.Dir, record.SourceAddr, record.Version) | ||
149 | diags = append(diags, addDiags...) | ||
150 | |||
151 | return mod, v, diags | ||
152 | }, | ||
153 | ) | ||
154 | } | ||
155 | |||
156 | func (l *Loader) addModuleToSnapshot(snap *Snapshot, key string, dir string, sourceAddr string, v *version.Version) hcl.Diagnostics { | ||
157 | var diags hcl.Diagnostics | ||
158 | |||
159 | primaryFiles, overrideFiles, moreDiags := l.parser.ConfigDirFiles(dir) | ||
160 | if moreDiags.HasErrors() { | ||
161 | // Any diagnostics we get here should be already present | ||
162 | // in diags, so it's weird if we get here but we'll allow it | ||
163 | // and return a general error message in that case. | ||
164 | diags = append(diags, &hcl.Diagnostic{ | ||
165 | Severity: hcl.DiagError, | ||
166 | Summary: "Failed to read directory for module", | ||
167 | Detail: fmt.Sprintf("The source directory %s could not be read", dir), | ||
168 | }) | ||
169 | return diags | ||
170 | } | ||
171 | |||
172 | snapMod := &SnapshotModule{ | ||
173 | Dir: dir, | ||
174 | Files: map[string][]byte{}, | ||
175 | SourceAddr: sourceAddr, | ||
176 | Version: v, | ||
177 | } | ||
178 | |||
179 | files := make([]string, 0, len(primaryFiles)+len(overrideFiles)) | ||
180 | files = append(files, primaryFiles...) | ||
181 | files = append(files, overrideFiles...) | ||
182 | sources := l.Sources() // should be populated with all the files we need by now | ||
183 | for _, filePath := range files { | ||
184 | filename := filepath.Base(filePath) | ||
185 | src, exists := sources[filePath] | ||
186 | if !exists { | ||
187 | diags = append(diags, &hcl.Diagnostic{ | ||
188 | Severity: hcl.DiagError, | ||
189 | Summary: "Missing source file for snapshot", | ||
190 | Detail: fmt.Sprintf("The source code for file %s could not be found to produce a configuration snapshot.", filePath), | ||
191 | }) | ||
192 | continue | ||
193 | } | ||
194 | snapMod.Files[filepath.Clean(filename)] = src | ||
195 | } | ||
196 | |||
197 | snap.Modules[key] = snapMod | ||
198 | |||
199 | return diags | ||
200 | } | ||
201 | |||
202 | // snapshotFS is an implementation of afero.Fs that reads from a snapshot. | ||
203 | // | ||
204 | // This is not intended as a general-purpose filesystem implementation. Instead, | ||
205 | // it just supports the minimal functionality required to support the | ||
206 | // configuration loader and parser as an implementation detail of creating | ||
207 | // a loader from a snapshot. | ||
208 | type snapshotFS struct { | ||
209 | snap *Snapshot | ||
210 | } | ||
211 | |||
212 | var _ afero.Fs = snapshotFS{} | ||
213 | |||
214 | func (fs snapshotFS) Create(name string) (afero.File, error) { | ||
215 | return nil, fmt.Errorf("cannot create file inside configuration snapshot") | ||
216 | } | ||
217 | |||
218 | func (fs snapshotFS) Mkdir(name string, perm os.FileMode) error { | ||
219 | return fmt.Errorf("cannot create directory inside configuration snapshot") | ||
220 | } | ||
221 | |||
222 | func (fs snapshotFS) MkdirAll(name string, perm os.FileMode) error { | ||
223 | return fmt.Errorf("cannot create directories inside configuration snapshot") | ||
224 | } | ||
225 | |||
226 | func (fs snapshotFS) Open(name string) (afero.File, error) { | ||
227 | |||
228 | // Our "filesystem" is sparsely populated only with the directories | ||
229 | // mentioned by modules in our snapshot, so the high-level process | ||
230 | // for opening a file is: | ||
231 | // - Find the module snapshot corresponding to the containing directory | ||
232 | // - Find the file within that snapshot | ||
233 | // - Wrap the resulting byte slice in a snapshotFile to return | ||
234 | // | ||
235 | // The other possibility handled here is if the given name is for the | ||
236 | // module directory itself, in which case we'll return a snapshotDir | ||
237 | // instead. | ||
238 | // | ||
239 | // This function doesn't try to be incredibly robust in supporting | ||
240 | // different permutations of paths, etc because in practice we only | ||
241 | // need to support the path forms that our own loader and parser will | ||
242 | // generate. | ||
243 | |||
244 | dir := filepath.Dir(name) | ||
245 | fn := filepath.Base(name) | ||
246 | directDir := filepath.Clean(name) | ||
247 | |||
248 | // First we'll check to see if this is an exact path for a module directory. | ||
249 | // We need to do this first (rather than as part of the next loop below) | ||
250 | // because a module in a child directory of another module can otherwise | ||
251 | // appear to be a file in that parent directory. | ||
252 | for _, candidate := range fs.snap.Modules { | ||
253 | modDir := filepath.Clean(candidate.Dir) | ||
254 | if modDir == directDir { | ||
255 | // We've matched the module directory itself | ||
256 | filenames := make([]string, 0, len(candidate.Files)) | ||
257 | for n := range candidate.Files { | ||
258 | filenames = append(filenames, n) | ||
259 | } | ||
260 | sort.Strings(filenames) | ||
261 | return snapshotDir{ | ||
262 | filenames: filenames, | ||
263 | }, nil | ||
264 | } | ||
265 | } | ||
266 | |||
267 | // If we get here then the given path isn't a module directory exactly, so | ||
268 | // we'll treat it as a file path and try to find a module directory it | ||
269 | // could be located in. | ||
270 | var modSnap *SnapshotModule | ||
271 | for _, candidate := range fs.snap.Modules { | ||
272 | modDir := filepath.Clean(candidate.Dir) | ||
273 | if modDir == dir { | ||
274 | modSnap = candidate | ||
275 | break | ||
276 | } | ||
277 | } | ||
278 | if modSnap == nil { | ||
279 | return nil, os.ErrNotExist | ||
280 | } | ||
281 | |||
282 | src, exists := modSnap.Files[fn] | ||
283 | if !exists { | ||
284 | return nil, os.ErrNotExist | ||
285 | } | ||
286 | |||
287 | return &snapshotFile{ | ||
288 | src: src, | ||
289 | }, nil | ||
290 | } | ||
291 | |||
292 | func (fs snapshotFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { | ||
293 | return fs.Open(name) | ||
294 | } | ||
295 | |||
296 | func (fs snapshotFS) Remove(name string) error { | ||
297 | return fmt.Errorf("cannot remove file inside configuration snapshot") | ||
298 | } | ||
299 | |||
300 | func (fs snapshotFS) RemoveAll(path string) error { | ||
301 | return fmt.Errorf("cannot remove files inside configuration snapshot") | ||
302 | } | ||
303 | |||
304 | func (fs snapshotFS) Rename(old, new string) error { | ||
305 | return fmt.Errorf("cannot rename file inside configuration snapshot") | ||
306 | } | ||
307 | |||
308 | func (fs snapshotFS) Stat(name string) (os.FileInfo, error) { | ||
309 | f, err := fs.Open(name) | ||
310 | if err != nil { | ||
311 | return nil, err | ||
312 | } | ||
313 | _, isDir := f.(snapshotDir) | ||
314 | return snapshotFileInfo{ | ||
315 | name: filepath.Base(name), | ||
316 | isDir: isDir, | ||
317 | }, nil | ||
318 | } | ||
319 | |||
320 | func (fs snapshotFS) Name() string { | ||
321 | return "ConfigSnapshotFS" | ||
322 | } | ||
323 | |||
324 | func (fs snapshotFS) Chmod(name string, mode os.FileMode) error { | ||
325 | return fmt.Errorf("cannot set file mode inside configuration snapshot") | ||
326 | } | ||
327 | |||
328 | func (fs snapshotFS) Chtimes(name string, atime, mtime time.Time) error { | ||
329 | return fmt.Errorf("cannot set file times inside configuration snapshot") | ||
330 | } | ||
331 | |||
332 | type snapshotFile struct { | ||
333 | snapshotFileStub | ||
334 | src []byte | ||
335 | at int64 | ||
336 | } | ||
337 | |||
338 | var _ afero.File = (*snapshotFile)(nil) | ||
339 | |||
340 | func (f *snapshotFile) Read(p []byte) (n int, err error) { | ||
341 | if len(p) > 0 && f.at == int64(len(f.src)) { | ||
342 | return 0, io.EOF | ||
343 | } | ||
344 | if f.at > int64(len(f.src)) { | ||
345 | return 0, io.ErrUnexpectedEOF | ||
346 | } | ||
347 | if int64(len(f.src))-f.at >= int64(len(p)) { | ||
348 | n = len(p) | ||
349 | } else { | ||
350 | n = int(int64(len(f.src)) - f.at) | ||
351 | } | ||
352 | copy(p, f.src[f.at:f.at+int64(n)]) | ||
353 | f.at += int64(n) | ||
354 | return | ||
355 | } | ||
356 | |||
357 | func (f *snapshotFile) ReadAt(p []byte, off int64) (n int, err error) { | ||
358 | f.at = off | ||
359 | return f.Read(p) | ||
360 | } | ||
361 | |||
362 | func (f *snapshotFile) Seek(offset int64, whence int) (int64, error) { | ||
363 | switch whence { | ||
364 | case 0: | ||
365 | f.at = offset | ||
366 | case 1: | ||
367 | f.at += offset | ||
368 | case 2: | ||
369 | f.at = int64(len(f.src)) + offset | ||
370 | } | ||
371 | return f.at, nil | ||
372 | } | ||
373 | |||
374 | type snapshotDir struct { | ||
375 | snapshotFileStub | ||
376 | filenames []string | ||
377 | at int | ||
378 | } | ||
379 | |||
380 | var _ afero.File = snapshotDir{} | ||
381 | |||
382 | func (f snapshotDir) Readdir(count int) ([]os.FileInfo, error) { | ||
383 | names, err := f.Readdirnames(count) | ||
384 | if err != nil { | ||
385 | return nil, err | ||
386 | } | ||
387 | ret := make([]os.FileInfo, len(names)) | ||
388 | for i, name := range names { | ||
389 | ret[i] = snapshotFileInfo{ | ||
390 | name: name, | ||
391 | isDir: false, | ||
392 | } | ||
393 | } | ||
394 | return ret, nil | ||
395 | } | ||
396 | |||
397 | func (f snapshotDir) Readdirnames(count int) ([]string, error) { | ||
398 | var outLen int | ||
399 | names := f.filenames[f.at:] | ||
400 | if count > 0 { | ||
401 | if len(names) < count { | ||
402 | outLen = len(names) | ||
403 | } else { | ||
404 | outLen = count | ||
405 | } | ||
406 | if len(names) == 0 { | ||
407 | return nil, io.EOF | ||
408 | } | ||
409 | } else { | ||
410 | outLen = len(names) | ||
411 | } | ||
412 | f.at += outLen | ||
413 | |||
414 | return names[:outLen], nil | ||
415 | } | ||
416 | |||
417 | // snapshotFileInfo is a minimal implementation of os.FileInfo to support our | ||
418 | // virtual filesystem from snapshots. | ||
419 | type snapshotFileInfo struct { | ||
420 | name string | ||
421 | isDir bool | ||
422 | } | ||
423 | |||
424 | var _ os.FileInfo = snapshotFileInfo{} | ||
425 | |||
426 | func (fi snapshotFileInfo) Name() string { | ||
427 | return fi.name | ||
428 | } | ||
429 | |||
430 | func (fi snapshotFileInfo) Size() int64 { | ||
431 | // In practice, our parser and loader never call Size | ||
432 | return -1 | ||
433 | } | ||
434 | |||
435 | func (fi snapshotFileInfo) Mode() os.FileMode { | ||
436 | return os.ModePerm | ||
437 | } | ||
438 | |||
439 | func (fi snapshotFileInfo) ModTime() time.Time { | ||
440 | return time.Now() | ||
441 | } | ||
442 | |||
443 | func (fi snapshotFileInfo) IsDir() bool { | ||
444 | return fi.isDir | ||
445 | } | ||
446 | |||
447 | func (fi snapshotFileInfo) Sys() interface{} { | ||
448 | return nil | ||
449 | } | ||
450 | |||
451 | type snapshotFileStub struct{} | ||
452 | |||
453 | func (f snapshotFileStub) Close() error { | ||
454 | return nil | ||
455 | } | ||
456 | |||
457 | func (f snapshotFileStub) Read(p []byte) (n int, err error) { | ||
458 | return 0, fmt.Errorf("cannot read") | ||
459 | } | ||
460 | |||
461 | func (f snapshotFileStub) ReadAt(p []byte, off int64) (n int, err error) { | ||
462 | return 0, fmt.Errorf("cannot read") | ||
463 | } | ||
464 | |||
465 | func (f snapshotFileStub) Seek(offset int64, whence int) (int64, error) { | ||
466 | return 0, fmt.Errorf("cannot seek") | ||
467 | } | ||
468 | |||
469 | func (f snapshotFileStub) Write(p []byte) (n int, err error) { | ||
470 | return f.WriteAt(p, 0) | ||
471 | } | ||
472 | |||
473 | func (f snapshotFileStub) WriteAt(p []byte, off int64) (n int, err error) { | ||
474 | return 0, fmt.Errorf("cannot write to file in snapshot") | ||
475 | } | ||
476 | |||
477 | func (f snapshotFileStub) WriteString(s string) (n int, err error) { | ||
478 | return 0, fmt.Errorf("cannot write to file in snapshot") | ||
479 | } | ||
480 | |||
481 | func (f snapshotFileStub) Name() string { | ||
482 | // in practice, the loader and parser never use this | ||
483 | return "<unimplemented>" | ||
484 | } | ||
485 | |||
486 | func (f snapshotFileStub) Readdir(count int) ([]os.FileInfo, error) { | ||
487 | return nil, fmt.Errorf("cannot use Readdir on a file") | ||
488 | } | ||
489 | |||
490 | func (f snapshotFileStub) Readdirnames(count int) ([]string, error) { | ||
491 | return nil, fmt.Errorf("cannot use Readdir on a file") | ||
492 | } | ||
493 | |||
494 | func (f snapshotFileStub) Stat() (os.FileInfo, error) { | ||
495 | return nil, fmt.Errorf("cannot stat") | ||
496 | } | ||
497 | |||
498 | func (f snapshotFileStub) Sync() error { | ||
499 | return nil | ||
500 | } | ||
501 | |||
502 | func (f snapshotFileStub) Truncate(size int64) error { | ||
503 | return fmt.Errorf("cannot write to file in snapshot") | ||
504 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/module_mgr.go b/vendor/github.com/hashicorp/terraform/configs/configload/module_mgr.go new file mode 100644 index 0000000..3c410ee --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/module_mgr.go | |||
@@ -0,0 +1,76 @@ | |||
1 | package configload | ||
2 | |||
3 | import ( | ||
4 | "os" | ||
5 | "path/filepath" | ||
6 | |||
7 | "github.com/hashicorp/terraform/internal/modsdir" | ||
8 | "github.com/hashicorp/terraform/registry" | ||
9 | "github.com/hashicorp/terraform/svchost/disco" | ||
10 | "github.com/spf13/afero" | ||
11 | ) | ||
12 | |||
13 | type moduleMgr struct { | ||
14 | FS afero.Afero | ||
15 | |||
16 | // CanInstall is true for a module manager that can support installation. | ||
17 | // | ||
18 | // This must be set only if FS is an afero.OsFs, because the installer | ||
19 | // (which uses go-getter) is not aware of the virtual filesystem | ||
20 | // abstraction and will always write into the "real" filesystem. | ||
21 | CanInstall bool | ||
22 | |||
23 | // Dir is the path where descendent modules are (or will be) installed. | ||
24 | Dir string | ||
25 | |||
26 | // Services is a service discovery client that will be used to find | ||
27 | // remote module registry endpoints. This object may be pre-loaded with | ||
28 | // cached discovery information. | ||
29 | Services *disco.Disco | ||
30 | |||
31 | // Registry is a client for the module registry protocol, which is used | ||
32 | // when a module is requested from a registry source. | ||
33 | Registry *registry.Client | ||
34 | |||
35 | // manifest tracks the currently-installed modules for this manager. | ||
36 | // | ||
37 | // The loader may read this. Only the installer may write to it, and | ||
38 | // after a set of updates are completed the installer must call | ||
39 | // writeModuleManifestSnapshot to persist a snapshot of the manifest | ||
40 | // to disk for use on subsequent runs. | ||
41 | manifest modsdir.Manifest | ||
42 | } | ||
43 | |||
44 | func (m *moduleMgr) manifestSnapshotPath() string { | ||
45 | return filepath.Join(m.Dir, modsdir.ManifestSnapshotFilename) | ||
46 | } | ||
47 | |||
48 | // readModuleManifestSnapshot loads a manifest snapshot from the filesystem. | ||
49 | func (m *moduleMgr) readModuleManifestSnapshot() error { | ||
50 | r, err := m.FS.Open(m.manifestSnapshotPath()) | ||
51 | if err != nil { | ||
52 | if os.IsNotExist(err) { | ||
53 | // We'll treat a missing file as an empty manifest | ||
54 | m.manifest = make(modsdir.Manifest) | ||
55 | return nil | ||
56 | } | ||
57 | return err | ||
58 | } | ||
59 | |||
60 | m.manifest, err = modsdir.ReadManifestSnapshot(r) | ||
61 | return err | ||
62 | } | ||
63 | |||
64 | // writeModuleManifestSnapshot writes a snapshot of the current manifest | ||
65 | // to the filesystem. | ||
66 | // | ||
67 | // The caller must guarantee no concurrent modifications of the manifest for | ||
68 | // the duration of a call to this function, or the behavior is undefined. | ||
69 | func (m *moduleMgr) writeModuleManifestSnapshot() error { | ||
70 | w, err := m.FS.Create(m.manifestSnapshotPath()) | ||
71 | if err != nil { | ||
72 | return err | ||
73 | } | ||
74 | |||
75 | return m.manifest.WriteSnapshot(w) | ||
76 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/source_addr.go b/vendor/github.com/hashicorp/terraform/configs/configload/source_addr.go new file mode 100644 index 0000000..594cf64 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/source_addr.go | |||
@@ -0,0 +1,45 @@ | |||
1 | package configload | ||
2 | |||
3 | import ( | ||
4 | "strings" | ||
5 | |||
6 | "github.com/hashicorp/go-getter" | ||
7 | |||
8 | "github.com/hashicorp/terraform/registry/regsrc" | ||
9 | ) | ||
10 | |||
11 | var localSourcePrefixes = []string{ | ||
12 | "./", | ||
13 | "../", | ||
14 | ".\\", | ||
15 | "..\\", | ||
16 | } | ||
17 | |||
18 | func isLocalSourceAddr(addr string) bool { | ||
19 | for _, prefix := range localSourcePrefixes { | ||
20 | if strings.HasPrefix(addr, prefix) { | ||
21 | return true | ||
22 | } | ||
23 | } | ||
24 | return false | ||
25 | } | ||
26 | |||
27 | func isRegistrySourceAddr(addr string) bool { | ||
28 | _, err := regsrc.ParseModuleSource(addr) | ||
29 | return err == nil | ||
30 | } | ||
31 | |||
32 | // splitAddrSubdir splits the given address (which is assumed to be a | ||
33 | // registry address or go-getter-style address) into a package portion | ||
34 | // and a sub-directory portion. | ||
35 | // | ||
36 | // The package portion defines what should be downloaded and then the | ||
37 | // sub-directory portion, if present, specifies a sub-directory within | ||
38 | // the downloaded object (an archive, VCS repository, etc) that contains | ||
39 | // the module's configuration files. | ||
40 | // | ||
41 | // The subDir portion will be returned as empty if no subdir separator | ||
42 | // ("//") is present in the address. | ||
43 | func splitAddrSubdir(addr string) (packageAddr, subDir string) { | ||
44 | return getter.SourceDirSubdir(addr) | ||
45 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configload/testing.go b/vendor/github.com/hashicorp/terraform/configs/configload/testing.go new file mode 100644 index 0000000..86ca9d1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configload/testing.go | |||
@@ -0,0 +1,43 @@ | |||
1 | package configload | ||
2 | |||
3 | import ( | ||
4 | "io/ioutil" | ||
5 | "os" | ||
6 | "testing" | ||
7 | ) | ||
8 | |||
9 | // NewLoaderForTests is a variant of NewLoader that is intended to be more | ||
10 | // convenient for unit tests. | ||
11 | // | ||
12 | // The loader's modules directory is a separate temporary directory created | ||
13 | // for each call. Along with the created loader, this function returns a | ||
14 | // cleanup function that should be called before the test completes in order | ||
15 | // to remove that temporary directory. | ||
16 | // | ||
17 | // In the case of any errors, t.Fatal (or similar) will be called to halt | ||
18 | // execution of the test, so the calling test does not need to handle errors | ||
19 | // itself. | ||
20 | func NewLoaderForTests(t *testing.T) (*Loader, func()) { | ||
21 | t.Helper() | ||
22 | |||
23 | modulesDir, err := ioutil.TempDir("", "tf-configs") | ||
24 | if err != nil { | ||
25 | t.Fatalf("failed to create temporary modules dir: %s", err) | ||
26 | return nil, func() {} | ||
27 | } | ||
28 | |||
29 | cleanup := func() { | ||
30 | os.RemoveAll(modulesDir) | ||
31 | } | ||
32 | |||
33 | loader, err := NewLoader(&Config{ | ||
34 | ModulesDir: modulesDir, | ||
35 | }) | ||
36 | if err != nil { | ||
37 | cleanup() | ||
38 | t.Fatalf("failed to create config loader: %s", err) | ||
39 | return nil, func() {} | ||
40 | } | ||
41 | |||
42 | return loader, cleanup | ||
43 | } | ||