]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/plugin/discovery/find.go
Merge pull request #32 from ndench/0.12-compatibility
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / plugin / discovery / find.go
CommitLineData
c680a8e1
RS
1package discovery
2
3import (
4 "io/ioutil"
5 "log"
15c0b25d 6 "os"
c680a8e1
RS
7 "path/filepath"
8 "strings"
9)
10
11// FindPlugins looks in the given directories for files whose filenames
12// suggest that they are plugins of the given kind (e.g. "provider") and
13// returns a PluginMetaSet representing the discovered potential-plugins.
14//
15// Currently this supports two different naming schemes. The current
16// standard naming scheme is a subdirectory called $GOOS-$GOARCH containing
17// files named terraform-$KIND-$NAME-V$VERSION. The legacy naming scheme is
18// files directly in the given directory whose names are like
19// terraform-$KIND-$NAME.
20//
21// Only one plugin will be returned for each unique plugin (name, version)
22// pair, with preference given to files found in earlier directories.
23//
24// This is a convenience wrapper around FindPluginPaths and ResolvePluginsPaths.
25func FindPlugins(kind string, dirs []string) PluginMetaSet {
26 return ResolvePluginPaths(FindPluginPaths(kind, dirs))
27}
28
29// FindPluginPaths looks in the given directories for files whose filenames
30// suggest that they are plugins of the given kind (e.g. "provider").
31//
32// The return value is a list of absolute paths that appear to refer to
33// plugins in the given directories, based only on what can be inferred
34// from the naming scheme. The paths returned are ordered such that files
35// in later dirs appear after files in earlier dirs in the given directory
36// list. Within the same directory plugins are returned in a consistent but
37// undefined order.
38func FindPluginPaths(kind string, dirs []string) []string {
39 // This is just a thin wrapper around findPluginPaths so that we can
40 // use the latter in tests with a fake machineName so we can use our
41 // test fixtures.
42 return findPluginPaths(kind, dirs)
43}
44
45func findPluginPaths(kind string, dirs []string) []string {
46 prefix := "terraform-" + kind + "-"
47
48 ret := make([]string, 0, len(dirs))
49
50 for _, dir := range dirs {
51 items, err := ioutil.ReadDir(dir)
52 if err != nil {
53 // Ignore missing dirs, non-dirs, etc
54 continue
55 }
56
57 log.Printf("[DEBUG] checking for %s in %q", kind, dir)
58
59 for _, item := range items {
60 fullName := item.Name()
61
62 if !strings.HasPrefix(fullName, prefix) {
c680a8e1
RS
63 continue
64 }
65
66 // New-style paths must have a version segment in filename
67 if strings.Contains(strings.ToLower(fullName), "_v") {
68 absPath, err := filepath.Abs(filepath.Join(dir, fullName))
69 if err != nil {
70 log.Printf("[ERROR] plugin filepath error: %s", err)
71 continue
72 }
73
15c0b25d
AP
74 // Check that the file we found is usable
75 if !pathIsFile(absPath) {
76 log.Printf("[ERROR] ignoring non-file %s", absPath)
77 continue
78 }
79
c680a8e1
RS
80 log.Printf("[DEBUG] found %s %q", kind, fullName)
81 ret = append(ret, filepath.Clean(absPath))
82 continue
83 }
84
85 // Legacy style with files directly in the base directory
86 absPath, err := filepath.Abs(filepath.Join(dir, fullName))
87 if err != nil {
88 log.Printf("[ERROR] plugin filepath error: %s", err)
89 continue
90 }
91
15c0b25d
AP
92 // Check that the file we found is usable
93 if !pathIsFile(absPath) {
94 log.Printf("[ERROR] ignoring non-file %s", absPath)
95 continue
96 }
97
98 log.Printf("[WARN] found legacy %s %q", kind, fullName)
c680a8e1
RS
99
100 ret = append(ret, filepath.Clean(absPath))
101 }
102 }
103
104 return ret
105}
106
15c0b25d
AP
107// Returns true if and only if the given path refers to a file or a symlink
108// to a file.
109func pathIsFile(path string) bool {
110 info, err := os.Stat(path)
111 if err != nil {
112 return false
113 }
114
115 return !info.IsDir()
116}
117
c680a8e1
RS
118// ResolvePluginPaths takes a list of paths to plugin executables (as returned
119// by e.g. FindPluginPaths) and produces a PluginMetaSet describing the
120// referenced plugins.
121//
122// If the same combination of plugin name and version appears multiple times,
123// the earlier reference will be preferred. Several different versions of
124// the same plugin name may be returned, in which case the methods of
125// PluginMetaSet can be used to filter down.
126func ResolvePluginPaths(paths []string) PluginMetaSet {
127 s := make(PluginMetaSet)
128
129 type nameVersion struct {
130 Name string
131 Version string
132 }
133 found := make(map[nameVersion]struct{})
134
135 for _, path := range paths {
136 baseName := strings.ToLower(filepath.Base(path))
137 if !strings.HasPrefix(baseName, "terraform-") {
138 // Should never happen with reasonable input
139 continue
140 }
141
142 baseName = baseName[10:]
143 firstDash := strings.Index(baseName, "-")
144 if firstDash == -1 {
145 // Should never happen with reasonable input
146 continue
147 }
148
149 baseName = baseName[firstDash+1:]
150 if baseName == "" {
151 // Should never happen with reasonable input
152 continue
153 }
154
155 // Trim the .exe suffix used on Windows before we start wrangling
156 // the remainder of the path.
157 if strings.HasSuffix(baseName, ".exe") {
158 baseName = baseName[:len(baseName)-4]
159 }
160
161 parts := strings.SplitN(baseName, "_v", 2)
162 name := parts[0]
163 version := VersionZero
164 if len(parts) == 2 {
165 version = parts[1]
166 }
167
168 // Auto-installed plugins contain an extra name portion representing
169 // the expected plugin version, which we must trim off.
170 if underX := strings.Index(version, "_x"); underX != -1 {
171 version = version[:underX]
172 }
173
174 if _, ok := found[nameVersion{name, version}]; ok {
175 // Skip duplicate versions of the same plugin
176 // (We do this during this step because after this we will be
177 // dealing with sets and thus lose our ordering with which to
178 // decide preference.)
179 continue
180 }
181
182 s.Add(PluginMeta{
183 Name: name,
184 Version: VersionStr(version),
185 Path: path,
186 })
187 found[nameVersion{name, version}] = struct{}{}
188 }
189
190 return s
191}