diff options
Diffstat (limited to 'vendor/github.com/hashicorp/go-getter')
34 files changed, 3481 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/go-getter/LICENSE b/vendor/github.com/hashicorp/go-getter/LICENSE new file mode 100644 index 0000000..c33dcc7 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/LICENSE | |||
@@ -0,0 +1,354 @@ | |||
1 | Mozilla Public License, version 2.0 | ||
2 | |||
3 | 1. Definitions | ||
4 | |||
5 | 1.1. “Contributor” | ||
6 | |||
7 | means each individual or legal entity that creates, contributes to the | ||
8 | creation of, or owns Covered Software. | ||
9 | |||
10 | 1.2. “Contributor Version” | ||
11 | |||
12 | means the combination of the Contributions of others (if any) used by a | ||
13 | Contributor and that particular Contributor’s Contribution. | ||
14 | |||
15 | 1.3. “Contribution” | ||
16 | |||
17 | means Covered Software of a particular Contributor. | ||
18 | |||
19 | 1.4. “Covered Software” | ||
20 | |||
21 | means Source Code Form to which the initial Contributor has attached the | ||
22 | notice in Exhibit A, the Executable Form of such Source Code Form, and | ||
23 | Modifications of such Source Code Form, in each case including portions | ||
24 | thereof. | ||
25 | |||
26 | 1.5. “Incompatible With Secondary Licenses” | ||
27 | means | ||
28 | |||
29 | a. that the initial Contributor has attached the notice described in | ||
30 | Exhibit B to the Covered Software; or | ||
31 | |||
32 | b. that the Covered Software was made available under the terms of version | ||
33 | 1.1 or earlier of the License, but not also under the terms of a | ||
34 | Secondary License. | ||
35 | |||
36 | 1.6. “Executable Form” | ||
37 | |||
38 | means any form of the work other than Source Code Form. | ||
39 | |||
40 | 1.7. “Larger Work” | ||
41 | |||
42 | means a work that combines Covered Software with other material, in a separate | ||
43 | file or files, that is not Covered Software. | ||
44 | |||
45 | 1.8. “License” | ||
46 | |||
47 | means this document. | ||
48 | |||
49 | 1.9. “Licensable” | ||
50 | |||
51 | means having the right to grant, to the maximum extent possible, whether at the | ||
52 | time of the initial grant or subsequently, any and all of the rights conveyed by | ||
53 | this License. | ||
54 | |||
55 | 1.10. “Modifications” | ||
56 | |||
57 | means any of the following: | ||
58 | |||
59 | a. any file in Source Code Form that results from an addition to, deletion | ||
60 | from, or modification of the contents of Covered Software; or | ||
61 | |||
62 | b. any new file in Source Code Form that contains any Covered Software. | ||
63 | |||
64 | 1.11. “Patent Claims” of a Contributor | ||
65 | |||
66 | means any patent claim(s), including without limitation, method, process, | ||
67 | and apparatus claims, in any patent Licensable by such Contributor that | ||
68 | would be infringed, but for the grant of the License, by the making, | ||
69 | using, selling, offering for sale, having made, import, or transfer of | ||
70 | either its Contributions or its Contributor Version. | ||
71 | |||
72 | 1.12. “Secondary License” | ||
73 | |||
74 | means either the GNU General Public License, Version 2.0, the GNU Lesser | ||
75 | General Public License, Version 2.1, the GNU Affero General Public | ||
76 | License, Version 3.0, or any later versions of those licenses. | ||
77 | |||
78 | 1.13. “Source Code Form” | ||
79 | |||
80 | means the form of the work preferred for making modifications. | ||
81 | |||
82 | 1.14. “You” (or “Your”) | ||
83 | |||
84 | means an individual or a legal entity exercising rights under this | ||
85 | License. For legal entities, “You” includes any entity that controls, is | ||
86 | controlled by, or is under common control with You. For purposes of this | ||
87 | definition, “control” means (a) the power, direct or indirect, to cause | ||
88 | the direction or management of such entity, whether by contract or | ||
89 | otherwise, or (b) ownership of more than fifty percent (50%) of the | ||
90 | outstanding shares or beneficial ownership of such entity. | ||
91 | |||
92 | |||
93 | 2. License Grants and Conditions | ||
94 | |||
95 | 2.1. Grants | ||
96 | |||
97 | Each Contributor hereby grants You a world-wide, royalty-free, | ||
98 | non-exclusive license: | ||
99 | |||
100 | a. under intellectual property rights (other than patent or trademark) | ||
101 | Licensable by such Contributor to use, reproduce, make available, | ||
102 | modify, display, perform, distribute, and otherwise exploit its | ||
103 | Contributions, either on an unmodified basis, with Modifications, or as | ||
104 | part of a Larger Work; and | ||
105 | |||
106 | b. under Patent Claims of such Contributor to make, use, sell, offer for | ||
107 | sale, have made, import, and otherwise transfer either its Contributions | ||
108 | or its Contributor Version. | ||
109 | |||
110 | 2.2. Effective Date | ||
111 | |||
112 | The licenses granted in Section 2.1 with respect to any Contribution become | ||
113 | effective for each Contribution on the date the Contributor first distributes | ||
114 | such Contribution. | ||
115 | |||
116 | 2.3. Limitations on Grant Scope | ||
117 | |||
118 | The licenses granted in this Section 2 are the only rights granted under this | ||
119 | License. No additional rights or licenses will be implied from the distribution | ||
120 | or licensing of Covered Software under this License. Notwithstanding Section | ||
121 | 2.1(b) above, no patent license is granted by a Contributor: | ||
122 | |||
123 | a. for any code that a Contributor has removed from Covered Software; or | ||
124 | |||
125 | b. for infringements caused by: (i) Your and any other third party’s | ||
126 | modifications of Covered Software, or (ii) the combination of its | ||
127 | Contributions with other software (except as part of its Contributor | ||
128 | Version); or | ||
129 | |||
130 | c. under Patent Claims infringed by Covered Software in the absence of its | ||
131 | Contributions. | ||
132 | |||
133 | This License does not grant any rights in the trademarks, service marks, or | ||
134 | logos of any Contributor (except as may be necessary to comply with the | ||
135 | notice requirements in Section 3.4). | ||
136 | |||
137 | 2.4. Subsequent Licenses | ||
138 | |||
139 | No Contributor makes additional grants as a result of Your choice to | ||
140 | distribute the Covered Software under a subsequent version of this License | ||
141 | (see Section 10.2) or under the terms of a Secondary License (if permitted | ||
142 | under the terms of Section 3.3). | ||
143 | |||
144 | 2.5. Representation | ||
145 | |||
146 | Each Contributor represents that the Contributor believes its Contributions | ||
147 | are its original creation(s) or it has sufficient rights to grant the | ||
148 | rights to its Contributions conveyed by this License. | ||
149 | |||
150 | 2.6. Fair Use | ||
151 | |||
152 | This License is not intended to limit any rights You have under applicable | ||
153 | copyright doctrines of fair use, fair dealing, or other equivalents. | ||
154 | |||
155 | 2.7. Conditions | ||
156 | |||
157 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | ||
158 | Section 2.1. | ||
159 | |||
160 | |||
161 | 3. Responsibilities | ||
162 | |||
163 | 3.1. Distribution of Source Form | ||
164 | |||
165 | All distribution of Covered Software in Source Code Form, including any | ||
166 | Modifications that You create or to which You contribute, must be under the | ||
167 | terms of this License. You must inform recipients that the Source Code Form | ||
168 | of the Covered Software is governed by the terms of this License, and how | ||
169 | they can obtain a copy of this License. You may not attempt to alter or | ||
170 | restrict the recipients’ rights in the Source Code Form. | ||
171 | |||
172 | 3.2. Distribution of Executable Form | ||
173 | |||
174 | If You distribute Covered Software in Executable Form then: | ||
175 | |||
176 | a. such Covered Software must also be made available in Source Code Form, | ||
177 | as described in Section 3.1, and You must inform recipients of the | ||
178 | Executable Form how they can obtain a copy of such Source Code Form by | ||
179 | reasonable means in a timely manner, at a charge no more than the cost | ||
180 | of distribution to the recipient; and | ||
181 | |||
182 | b. You may distribute such Executable Form under the terms of this License, | ||
183 | or sublicense it under different terms, provided that the license for | ||
184 | the Executable Form does not attempt to limit or alter the recipients’ | ||
185 | rights in the Source Code Form under this License. | ||
186 | |||
187 | 3.3. Distribution of a Larger Work | ||
188 | |||
189 | You may create and distribute a Larger Work under terms of Your choice, | ||
190 | provided that You also comply with the requirements of this License for the | ||
191 | Covered Software. If the Larger Work is a combination of Covered Software | ||
192 | with a work governed by one or more Secondary Licenses, and the Covered | ||
193 | Software is not Incompatible With Secondary Licenses, this License permits | ||
194 | You to additionally distribute such Covered Software under the terms of | ||
195 | such Secondary License(s), so that the recipient of the Larger Work may, at | ||
196 | their option, further distribute the Covered Software under the terms of | ||
197 | either this License or such Secondary License(s). | ||
198 | |||
199 | 3.4. Notices | ||
200 | |||
201 | You may not remove or alter the substance of any license notices (including | ||
202 | copyright notices, patent notices, disclaimers of warranty, or limitations | ||
203 | of liability) contained within the Source Code Form of the Covered | ||
204 | Software, except that You may alter any license notices to the extent | ||
205 | required to remedy known factual inaccuracies. | ||
206 | |||
207 | 3.5. Application of Additional Terms | ||
208 | |||
209 | You may choose to offer, and to charge a fee for, warranty, support, | ||
210 | indemnity or liability obligations to one or more recipients of Covered | ||
211 | Software. However, You may do so only on Your own behalf, and not on behalf | ||
212 | of any Contributor. You must make it absolutely clear that any such | ||
213 | warranty, support, indemnity, or liability obligation is offered by You | ||
214 | alone, and You hereby agree to indemnify every Contributor for any | ||
215 | liability incurred by such Contributor as a result of warranty, support, | ||
216 | indemnity or liability terms You offer. You may include additional | ||
217 | disclaimers of warranty and limitations of liability specific to any | ||
218 | jurisdiction. | ||
219 | |||
220 | 4. Inability to Comply Due to Statute or Regulation | ||
221 | |||
222 | If it is impossible for You to comply with any of the terms of this License | ||
223 | with respect to some or all of the Covered Software due to statute, judicial | ||
224 | order, or regulation then You must: (a) comply with the terms of this License | ||
225 | to the maximum extent possible; and (b) describe the limitations and the code | ||
226 | they affect. Such description must be placed in a text file included with all | ||
227 | distributions of the Covered Software under this License. Except to the | ||
228 | extent prohibited by statute or regulation, such description must be | ||
229 | sufficiently detailed for a recipient of ordinary skill to be able to | ||
230 | understand it. | ||
231 | |||
232 | 5. Termination | ||
233 | |||
234 | 5.1. The rights granted under this License will terminate automatically if You | ||
235 | fail to comply with any of its terms. However, if You become compliant, | ||
236 | then the rights granted under this License from a particular Contributor | ||
237 | are reinstated (a) provisionally, unless and until such Contributor | ||
238 | explicitly and finally terminates Your grants, and (b) on an ongoing basis, | ||
239 | if such Contributor fails to notify You of the non-compliance by some | ||
240 | reasonable means prior to 60 days after You have come back into compliance. | ||
241 | Moreover, Your grants from a particular Contributor are reinstated on an | ||
242 | ongoing basis if such Contributor notifies You of the non-compliance by | ||
243 | some reasonable means, this is the first time You have received notice of | ||
244 | non-compliance with this License from such Contributor, and You become | ||
245 | compliant prior to 30 days after Your receipt of the notice. | ||
246 | |||
247 | 5.2. If You initiate litigation against any entity by asserting a patent | ||
248 | infringement claim (excluding declaratory judgment actions, counter-claims, | ||
249 | and cross-claims) alleging that a Contributor Version directly or | ||
250 | indirectly infringes any patent, then the rights granted to You by any and | ||
251 | all Contributors for the Covered Software under Section 2.1 of this License | ||
252 | shall terminate. | ||
253 | |||
254 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | ||
255 | license agreements (excluding distributors and resellers) which have been | ||
256 | validly granted by You or Your distributors under this License prior to | ||
257 | termination shall survive termination. | ||
258 | |||
259 | 6. Disclaimer of Warranty | ||
260 | |||
261 | Covered Software is provided under this License on an “as is” basis, without | ||
262 | warranty of any kind, either expressed, implied, or statutory, including, | ||
263 | without limitation, warranties that the Covered Software is free of defects, | ||
264 | merchantable, fit for a particular purpose or non-infringing. The entire | ||
265 | risk as to the quality and performance of the Covered Software is with You. | ||
266 | Should any Covered Software prove defective in any respect, You (not any | ||
267 | Contributor) assume the cost of any necessary servicing, repair, or | ||
268 | correction. This disclaimer of warranty constitutes an essential part of this | ||
269 | License. No use of any Covered Software is authorized under this License | ||
270 | except under this disclaimer. | ||
271 | |||
272 | 7. Limitation of Liability | ||
273 | |||
274 | Under no circumstances and under no legal theory, whether tort (including | ||
275 | negligence), contract, or otherwise, shall any Contributor, or anyone who | ||
276 | distributes Covered Software as permitted above, be liable to You for any | ||
277 | direct, indirect, special, incidental, or consequential damages of any | ||
278 | character including, without limitation, damages for lost profits, loss of | ||
279 | goodwill, work stoppage, computer failure or malfunction, or any and all | ||
280 | other commercial damages or losses, even if such party shall have been | ||
281 | informed of the possibility of such damages. This limitation of liability | ||
282 | shall not apply to liability for death or personal injury resulting from such | ||
283 | party’s negligence to the extent applicable law prohibits such limitation. | ||
284 | Some jurisdictions do not allow the exclusion or limitation of incidental or | ||
285 | consequential damages, so this exclusion and limitation may not apply to You. | ||
286 | |||
287 | 8. Litigation | ||
288 | |||
289 | Any litigation relating to this License may be brought only in the courts of | ||
290 | a jurisdiction where the defendant maintains its principal place of business | ||
291 | and such litigation shall be governed by laws of that jurisdiction, without | ||
292 | reference to its conflict-of-law provisions. Nothing in this Section shall | ||
293 | prevent a party’s ability to bring cross-claims or counter-claims. | ||
294 | |||
295 | 9. Miscellaneous | ||
296 | |||
297 | This License represents the complete agreement concerning the subject matter | ||
298 | hereof. If any provision of this License is held to be unenforceable, such | ||
299 | provision shall be reformed only to the extent necessary to make it | ||
300 | enforceable. Any law or regulation which provides that the language of a | ||
301 | contract shall be construed against the drafter shall not be used to construe | ||
302 | this License against a Contributor. | ||
303 | |||
304 | |||
305 | 10. Versions of the License | ||
306 | |||
307 | 10.1. New Versions | ||
308 | |||
309 | Mozilla Foundation is the license steward. Except as provided in Section | ||
310 | 10.3, no one other than the license steward has the right to modify or | ||
311 | publish new versions of this License. Each version will be given a | ||
312 | distinguishing version number. | ||
313 | |||
314 | 10.2. Effect of New Versions | ||
315 | |||
316 | You may distribute the Covered Software under the terms of the version of | ||
317 | the License under which You originally received the Covered Software, or | ||
318 | under the terms of any subsequent version published by the license | ||
319 | steward. | ||
320 | |||
321 | 10.3. Modified Versions | ||
322 | |||
323 | If you create software not governed by this License, and you want to | ||
324 | create a new license for such software, you may create and use a modified | ||
325 | version of this License if you rename the license and remove any | ||
326 | references to the name of the license steward (except to note that such | ||
327 | modified license differs from this License). | ||
328 | |||
329 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses | ||
330 | If You choose to distribute Source Code Form that is Incompatible With | ||
331 | Secondary Licenses under the terms of this version of the License, the | ||
332 | notice described in Exhibit B of this License must be attached. | ||
333 | |||
334 | Exhibit A - Source Code Form License Notice | ||
335 | |||
336 | This Source Code Form is subject to the | ||
337 | terms of the Mozilla Public License, v. | ||
338 | 2.0. If a copy of the MPL was not | ||
339 | distributed with this file, You can | ||
340 | obtain one at | ||
341 | http://mozilla.org/MPL/2.0/. | ||
342 | |||
343 | If it is not possible or desirable to put the notice in a particular file, then | ||
344 | You may include the notice in a location (such as a LICENSE file in a relevant | ||
345 | directory) where a recipient would be likely to look for such a notice. | ||
346 | |||
347 | You may add additional accurate notices of copyright ownership. | ||
348 | |||
349 | Exhibit B - “Incompatible With Secondary Licenses” Notice | ||
350 | |||
351 | This Source Code Form is “Incompatible | ||
352 | With Secondary Licenses”, as defined by | ||
353 | the Mozilla Public License, v. 2.0. | ||
354 | |||
diff --git a/vendor/github.com/hashicorp/go-getter/README.md b/vendor/github.com/hashicorp/go-getter/README.md new file mode 100644 index 0000000..4a0b6a6 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/README.md | |||
@@ -0,0 +1,253 @@ | |||
1 | # go-getter | ||
2 | |||
3 | [![Build Status](http://img.shields.io/travis/hashicorp/go-getter.svg?style=flat-square)][travis] | ||
4 | [![Build status](https://ci.appveyor.com/api/projects/status/ulq3qr43n62croyq/branch/master?svg=true)][appveyor] | ||
5 | [![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs] | ||
6 | |||
7 | [travis]: http://travis-ci.org/hashicorp/go-getter | ||
8 | [godocs]: http://godoc.org/github.com/hashicorp/go-getter | ||
9 | [appveyor]: https://ci.appveyor.com/project/hashicorp/go-getter/branch/master | ||
10 | |||
11 | go-getter is a library for Go (golang) for downloading files or directories | ||
12 | from various sources using a URL as the primary form of input. | ||
13 | |||
14 | The power of this library is being flexible in being able to download | ||
15 | from a number of different sources (file paths, Git, HTTP, Mercurial, etc.) | ||
16 | using a single string as input. This removes the burden of knowing how to | ||
17 | download from a variety of sources from the implementer. | ||
18 | |||
19 | The concept of a _detector_ automatically turns invalid URLs into proper | ||
20 | URLs. For example: "github.com/hashicorp/go-getter" would turn into a | ||
21 | Git URL. Or "./foo" would turn into a file URL. These are extensible. | ||
22 | |||
23 | This library is used by [Terraform](https://terraform.io) for | ||
24 | downloading modules, [Otto](https://ottoproject.io) for dependencies and | ||
25 | Appfile imports, and [Nomad](https://nomadproject.io) for downloading | ||
26 | binaries. | ||
27 | |||
28 | ## Installation and Usage | ||
29 | |||
30 | Package documentation can be found on | ||
31 | [GoDoc](http://godoc.org/github.com/hashicorp/go-getter). | ||
32 | |||
33 | Installation can be done with a normal `go get`: | ||
34 | |||
35 | ``` | ||
36 | $ go get github.com/hashicorp/go-getter | ||
37 | ``` | ||
38 | |||
39 | go-getter also has a command you can use to test URL strings: | ||
40 | |||
41 | ``` | ||
42 | $ go install github.com/hashicorp/go-getter/cmd/go-getter | ||
43 | ... | ||
44 | |||
45 | $ go-getter github.com/foo/bar ./foo | ||
46 | ... | ||
47 | ``` | ||
48 | |||
49 | The command is useful for verifying URL structures. | ||
50 | |||
51 | ## URL Format | ||
52 | |||
53 | go-getter uses a single string URL as input to download from a variety of | ||
54 | protocols. go-getter has various "tricks" with this URL to do certain things. | ||
55 | This section documents the URL format. | ||
56 | |||
57 | ### Supported Protocols and Detectors | ||
58 | |||
59 | **Protocols** are used to download files/directories using a specific | ||
60 | mechanism. Example protocols are Git and HTTP. | ||
61 | |||
62 | **Detectors** are used to transform a valid or invalid URL into another | ||
63 | URL if it matches a certain pattern. Example: "github.com/user/repo" is | ||
64 | automatically transformed into a fully valid Git URL. This allows go-getter | ||
65 | to be very user friendly. | ||
66 | |||
67 | go-getter out of the box supports the following protocols. Additional protocols | ||
68 | can be augmented at runtime by implementing the `Getter` interface. | ||
69 | |||
70 | * Local files | ||
71 | * Git | ||
72 | * Mercurial | ||
73 | * HTTP | ||
74 | * Amazon S3 | ||
75 | |||
76 | In addition to the above protocols, go-getter has what are called "detectors." | ||
77 | These take a URL and attempt to automatically choose the best protocol for | ||
78 | it, which might involve even changing the protocol. The following detection | ||
79 | is built-in by default: | ||
80 | |||
81 | * File paths such as "./foo" are automatically changed to absolute | ||
82 | file URLs. | ||
83 | * GitHub URLs, such as "github.com/mitchellh/vagrant" are automatically | ||
84 | changed to Git protocol over HTTP. | ||
85 | * BitBucket URLs, such as "bitbucket.org/mitchellh/vagrant" are automatically | ||
86 | changed to a Git or mercurial protocol using the BitBucket API. | ||
87 | |||
88 | ### Forced Protocol | ||
89 | |||
90 | In some cases, the protocol to use is ambiguous depending on the source | ||
91 | URL. For example, "http://github.com/mitchellh/vagrant.git" could reference | ||
92 | an HTTP URL or a Git URL. Forced protocol syntax is used to disambiguate this | ||
93 | URL. | ||
94 | |||
95 | Forced protocol can be done by prefixing the URL with the protocol followed | ||
96 | by double colons. For example: `git::http://github.com/mitchellh/vagrant.git` | ||
97 | would download the given HTTP URL using the Git protocol. | ||
98 | |||
99 | Forced protocols will also override any detectors. | ||
100 | |||
101 | In the absense of a forced protocol, detectors may be run on the URL, transforming | ||
102 | the protocol anyways. The above example would've used the Git protocol either | ||
103 | way since the Git detector would've detected it was a GitHub URL. | ||
104 | |||
105 | ### Protocol-Specific Options | ||
106 | |||
107 | Each protocol can support protocol-specific options to configure that | ||
108 | protocol. For example, the `git` protocol supports specifying a `ref` | ||
109 | query parameter that tells it what ref to checkout for that Git | ||
110 | repository. | ||
111 | |||
112 | The options are specified as query parameters on the URL (or URL-like string) | ||
113 | given to go-getter. Using the Git example above, the URL below is a valid | ||
114 | input to go-getter: | ||
115 | |||
116 | github.com/hashicorp/go-getter?ref=abcd1234 | ||
117 | |||
118 | The protocol-specific options are documented below the URL format | ||
119 | section. But because they are part of the URL, we point it out here so | ||
120 | you know they exist. | ||
121 | |||
122 | ### Checksumming | ||
123 | |||
124 | For file downloads of any protocol, go-getter can automatically verify | ||
125 | a checksum for you. Note that checksumming only works for downloading files, | ||
126 | not directories, but checksumming will work for any protocol. | ||
127 | |||
128 | To checksum a file, append a `checksum` query parameter to the URL. | ||
129 | The paramter value should be in the format of `type:value`, where | ||
130 | type is "md5", "sha1", "sha256", or "sha512". The "value" should be | ||
131 | the actual checksum value. go-getter will parse out this query parameter | ||
132 | automatically and use it to verify the checksum. An example URL | ||
133 | is shown below: | ||
134 | |||
135 | ``` | ||
136 | ./foo.txt?checksum=md5:b7d96c89d09d9e204f5fedc4d5d55b21 | ||
137 | ``` | ||
138 | |||
139 | The checksum query parameter is never sent to the backend protocol | ||
140 | implementation. It is used at a higher level by go-getter itself. | ||
141 | |||
142 | ### Unarchiving | ||
143 | |||
144 | go-getter will automatically unarchive files into a file or directory | ||
145 | based on the extension of the file being requested (over any protocol). | ||
146 | This works for both file and directory downloads. | ||
147 | |||
148 | go-getter looks for an `archive` query parameter to specify the format of | ||
149 | the archive. If this isn't specified, go-getter will use the extension of | ||
150 | the path to see if it appears archived. Unarchiving can be explicitly | ||
151 | disabled by setting the `archive` query parameter to `false`. | ||
152 | |||
153 | The following archive formats are supported: | ||
154 | |||
155 | * `tar.gz` and `tgz` | ||
156 | * `tar.bz2` and `tbz2` | ||
157 | * `zip` | ||
158 | * `gz` | ||
159 | * `bz2` | ||
160 | |||
161 | For example, an example URL is shown below: | ||
162 | |||
163 | ``` | ||
164 | ./foo.zip | ||
165 | ``` | ||
166 | |||
167 | This will automatically be inferred to be a ZIP file and will be extracted. | ||
168 | You can also be explicit about the archive type: | ||
169 | |||
170 | ``` | ||
171 | ./some/other/path?archive=zip | ||
172 | ``` | ||
173 | |||
174 | And finally, you can disable archiving completely: | ||
175 | |||
176 | ``` | ||
177 | ./some/path?archive=false | ||
178 | ``` | ||
179 | |||
180 | You can combine unarchiving with the other features of go-getter such | ||
181 | as checksumming. The special `archive` query parameter will be removed | ||
182 | from the URL before going to the final protocol downloader. | ||
183 | |||
184 | ## Protocol-Specific Options | ||
185 | |||
186 | This section documents the protocol-specific options that can be specified | ||
187 | for go-getter. These options should be appended to the input as normal query | ||
188 | parameters. Depending on the usage of go-getter, applications may provide | ||
189 | alternate ways of inputting options. For example, [Nomad](https://www.nomadproject.io) | ||
190 | provides a nice options block for specifying options rather than in the URL. | ||
191 | |||
192 | ## General (All Protocols) | ||
193 | |||
194 | The options below are available to all protocols: | ||
195 | |||
196 | * `archive` - The archive format to use to unarchive this file, or "" (empty | ||
197 | string) to disable unarchiving. For more details, see the complete section | ||
198 | on archive support above. | ||
199 | |||
200 | * `checksum` - Checksum to verify the downloaded file or archive. See | ||
201 | the entire section on checksumming above for format and more details. | ||
202 | |||
203 | ### Local Files (`file`) | ||
204 | |||
205 | None | ||
206 | |||
207 | ### Git (`git`) | ||
208 | |||
209 | * `ref` - The Git ref to checkout. This is a ref, so it can point to | ||
210 | a commit SHA, a branch name, etc. If it is a named ref such as a branch | ||
211 | name, go-getter will update it to the latest on each get. | ||
212 | |||
213 | * `sshkey` - An SSH private key to use during clones. The provided key must | ||
214 | be a base64-encoded string. For example, to generate a suitable `sshkey` | ||
215 | from a private key file on disk, you would run `base64 -w0 <file>`. | ||
216 | |||
217 | **Note**: Git 2.3+ is required to use this feature. | ||
218 | |||
219 | ### Mercurial (`hg`) | ||
220 | |||
221 | * `rev` - The Mercurial revision to checkout. | ||
222 | |||
223 | ### HTTP (`http`) | ||
224 | |||
225 | None | ||
226 | |||
227 | ### S3 (`s3`) | ||
228 | |||
229 | S3 takes various access configurations in the URL. Note that it will also | ||
230 | read these from standard AWS environment variables if they're set. If | ||
231 | the query parameters are present, these take priority. | ||
232 | |||
233 | * `aws_access_key_id` - AWS access key. | ||
234 | * `aws_access_key_secret` - AWS access key secret. | ||
235 | * `aws_access_token` - AWS access token if this is being used. | ||
236 | |||
237 | #### Using IAM Instance Profiles with S3 | ||
238 | |||
239 | If you use go-getter and want to use an EC2 IAM Instance Profile to avoid | ||
240 | using credentials, then just omit these and the profile, if available will | ||
241 | be used automatically. | ||
242 | |||
243 | #### S3 Bucket Examples | ||
244 | |||
245 | S3 has several addressing schemes used to reference your bucket. These are | ||
246 | listed here: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html#access-bucket-intro | ||
247 | |||
248 | Some examples for these addressing schemes: | ||
249 | - s3::https://s3.amazonaws.com/bucket/foo | ||
250 | - s3::https://s3-eu-west-1.amazonaws.com/bucket/foo | ||
251 | - bucket.s3.amazonaws.com/foo | ||
252 | - bucket.s3-eu-west-1.amazonaws.com/foo/bar | ||
253 | |||
diff --git a/vendor/github.com/hashicorp/go-getter/appveyor.yml b/vendor/github.com/hashicorp/go-getter/appveyor.yml new file mode 100644 index 0000000..159dad4 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/appveyor.yml | |||
@@ -0,0 +1,16 @@ | |||
1 | version: "build-{branch}-{build}" | ||
2 | image: Visual Studio 2015 | ||
3 | clone_folder: c:\gopath\github.com\hashicorp\go-getter | ||
4 | environment: | ||
5 | GOPATH: c:\gopath | ||
6 | install: | ||
7 | - cmd: >- | ||
8 | echo %Path% | ||
9 | |||
10 | go version | ||
11 | |||
12 | go env | ||
13 | |||
14 | go get -d -v -t ./... | ||
15 | build_script: | ||
16 | - cmd: go test -v ./... | ||
diff --git a/vendor/github.com/hashicorp/go-getter/client.go b/vendor/github.com/hashicorp/go-getter/client.go new file mode 100644 index 0000000..876812a --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/client.go | |||
@@ -0,0 +1,335 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "crypto/md5" | ||
6 | "crypto/sha1" | ||
7 | "crypto/sha256" | ||
8 | "crypto/sha512" | ||
9 | "encoding/hex" | ||
10 | "fmt" | ||
11 | "hash" | ||
12 | "io" | ||
13 | "io/ioutil" | ||
14 | "os" | ||
15 | "path/filepath" | ||
16 | "strconv" | ||
17 | "strings" | ||
18 | |||
19 | urlhelper "github.com/hashicorp/go-getter/helper/url" | ||
20 | ) | ||
21 | |||
22 | // Client is a client for downloading things. | ||
23 | // | ||
24 | // Top-level functions such as Get are shortcuts for interacting with a client. | ||
25 | // Using a client directly allows more fine-grained control over how downloading | ||
26 | // is done, as well as customizing the protocols supported. | ||
27 | type Client struct { | ||
28 | // Src is the source URL to get. | ||
29 | // | ||
30 | // Dst is the path to save the downloaded thing as. If Dir is set to | ||
31 | // true, then this should be a directory. If the directory doesn't exist, | ||
32 | // it will be created for you. | ||
33 | // | ||
34 | // Pwd is the working directory for detection. If this isn't set, some | ||
35 | // detection may fail. Client will not default pwd to the current | ||
36 | // working directory for security reasons. | ||
37 | Src string | ||
38 | Dst string | ||
39 | Pwd string | ||
40 | |||
41 | // Mode is the method of download the client will use. See ClientMode | ||
42 | // for documentation. | ||
43 | Mode ClientMode | ||
44 | |||
45 | // Detectors is the list of detectors that are tried on the source. | ||
46 | // If this is nil, then the default Detectors will be used. | ||
47 | Detectors []Detector | ||
48 | |||
49 | // Decompressors is the map of decompressors supported by this client. | ||
50 | // If this is nil, then the default value is the Decompressors global. | ||
51 | Decompressors map[string]Decompressor | ||
52 | |||
53 | // Getters is the map of protocols supported by this client. If this | ||
54 | // is nil, then the default Getters variable will be used. | ||
55 | Getters map[string]Getter | ||
56 | |||
57 | // Dir, if true, tells the Client it is downloading a directory (versus | ||
58 | // a single file). This distinction is necessary since filenames and | ||
59 | // directory names follow the same format so disambiguating is impossible | ||
60 | // without knowing ahead of time. | ||
61 | // | ||
62 | // WARNING: deprecated. If Mode is set, that will take precedence. | ||
63 | Dir bool | ||
64 | } | ||
65 | |||
66 | // Get downloads the configured source to the destination. | ||
67 | func (c *Client) Get() error { | ||
68 | // Store this locally since there are cases we swap this | ||
69 | mode := c.Mode | ||
70 | if mode == ClientModeInvalid { | ||
71 | if c.Dir { | ||
72 | mode = ClientModeDir | ||
73 | } else { | ||
74 | mode = ClientModeFile | ||
75 | } | ||
76 | } | ||
77 | |||
78 | // Default decompressor value | ||
79 | decompressors := c.Decompressors | ||
80 | if decompressors == nil { | ||
81 | decompressors = Decompressors | ||
82 | } | ||
83 | |||
84 | // Detect the URL. This is safe if it is already detected. | ||
85 | detectors := c.Detectors | ||
86 | if detectors == nil { | ||
87 | detectors = Detectors | ||
88 | } | ||
89 | src, err := Detect(c.Src, c.Pwd, detectors) | ||
90 | if err != nil { | ||
91 | return err | ||
92 | } | ||
93 | |||
94 | // Determine if we have a forced protocol, i.e. "git::http://..." | ||
95 | force, src := getForcedGetter(src) | ||
96 | |||
97 | // If there is a subdir component, then we download the root separately | ||
98 | // and then copy over the proper subdir. | ||
99 | var realDst string | ||
100 | dst := c.Dst | ||
101 | src, subDir := SourceDirSubdir(src) | ||
102 | if subDir != "" { | ||
103 | tmpDir, err := ioutil.TempDir("", "tf") | ||
104 | if err != nil { | ||
105 | return err | ||
106 | } | ||
107 | if err := os.RemoveAll(tmpDir); err != nil { | ||
108 | return err | ||
109 | } | ||
110 | defer os.RemoveAll(tmpDir) | ||
111 | |||
112 | realDst = dst | ||
113 | dst = tmpDir | ||
114 | } | ||
115 | |||
116 | u, err := urlhelper.Parse(src) | ||
117 | if err != nil { | ||
118 | return err | ||
119 | } | ||
120 | if force == "" { | ||
121 | force = u.Scheme | ||
122 | } | ||
123 | |||
124 | getters := c.Getters | ||
125 | if getters == nil { | ||
126 | getters = Getters | ||
127 | } | ||
128 | |||
129 | g, ok := getters[force] | ||
130 | if !ok { | ||
131 | return fmt.Errorf( | ||
132 | "download not supported for scheme '%s'", force) | ||
133 | } | ||
134 | |||
135 | // We have magic query parameters that we use to signal different features | ||
136 | q := u.Query() | ||
137 | |||
138 | // Determine if we have an archive type | ||
139 | archiveV := q.Get("archive") | ||
140 | if archiveV != "" { | ||
141 | // Delete the paramter since it is a magic parameter we don't | ||
142 | // want to pass on to the Getter | ||
143 | q.Del("archive") | ||
144 | u.RawQuery = q.Encode() | ||
145 | |||
146 | // If we can parse the value as a bool and it is false, then | ||
147 | // set the archive to "-" which should never map to a decompressor | ||
148 | if b, err := strconv.ParseBool(archiveV); err == nil && !b { | ||
149 | archiveV = "-" | ||
150 | } | ||
151 | } | ||
152 | if archiveV == "" { | ||
153 | // We don't appear to... but is it part of the filename? | ||
154 | matchingLen := 0 | ||
155 | for k, _ := range decompressors { | ||
156 | if strings.HasSuffix(u.Path, "."+k) && len(k) > matchingLen { | ||
157 | archiveV = k | ||
158 | matchingLen = len(k) | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | |||
163 | // If we have a decompressor, then we need to change the destination | ||
164 | // to download to a temporary path. We unarchive this into the final, | ||
165 | // real path. | ||
166 | var decompressDst string | ||
167 | var decompressDir bool | ||
168 | decompressor := decompressors[archiveV] | ||
169 | if decompressor != nil { | ||
170 | // Create a temporary directory to store our archive. We delete | ||
171 | // this at the end of everything. | ||
172 | td, err := ioutil.TempDir("", "getter") | ||
173 | if err != nil { | ||
174 | return fmt.Errorf( | ||
175 | "Error creating temporary directory for archive: %s", err) | ||
176 | } | ||
177 | defer os.RemoveAll(td) | ||
178 | |||
179 | // Swap the download directory to be our temporary path and | ||
180 | // store the old values. | ||
181 | decompressDst = dst | ||
182 | decompressDir = mode != ClientModeFile | ||
183 | dst = filepath.Join(td, "archive") | ||
184 | mode = ClientModeFile | ||
185 | } | ||
186 | |||
187 | // Determine if we have a checksum | ||
188 | var checksumHash hash.Hash | ||
189 | var checksumValue []byte | ||
190 | if v := q.Get("checksum"); v != "" { | ||
191 | // Delete the query parameter if we have it. | ||
192 | q.Del("checksum") | ||
193 | u.RawQuery = q.Encode() | ||
194 | |||
195 | // Determine the checksum hash type | ||
196 | checksumType := "" | ||
197 | idx := strings.Index(v, ":") | ||
198 | if idx > -1 { | ||
199 | checksumType = v[:idx] | ||
200 | } | ||
201 | switch checksumType { | ||
202 | case "md5": | ||
203 | checksumHash = md5.New() | ||
204 | case "sha1": | ||
205 | checksumHash = sha1.New() | ||
206 | case "sha256": | ||
207 | checksumHash = sha256.New() | ||
208 | case "sha512": | ||
209 | checksumHash = sha512.New() | ||
210 | default: | ||
211 | return fmt.Errorf( | ||
212 | "unsupported checksum type: %s", checksumType) | ||
213 | } | ||
214 | |||
215 | // Get the remainder of the value and parse it into bytes | ||
216 | b, err := hex.DecodeString(v[idx+1:]) | ||
217 | if err != nil { | ||
218 | return fmt.Errorf("invalid checksum: %s", err) | ||
219 | } | ||
220 | |||
221 | // Set our value | ||
222 | checksumValue = b | ||
223 | } | ||
224 | |||
225 | if mode == ClientModeAny { | ||
226 | // Ask the getter which client mode to use | ||
227 | mode, err = g.ClientMode(u) | ||
228 | if err != nil { | ||
229 | return err | ||
230 | } | ||
231 | |||
232 | // Destination is the base name of the URL path in "any" mode when | ||
233 | // a file source is detected. | ||
234 | if mode == ClientModeFile { | ||
235 | dst = filepath.Join(dst, filepath.Base(u.Path)) | ||
236 | } | ||
237 | } | ||
238 | |||
239 | // If we're not downloading a directory, then just download the file | ||
240 | // and return. | ||
241 | if mode == ClientModeFile { | ||
242 | err := g.GetFile(dst, u) | ||
243 | if err != nil { | ||
244 | return err | ||
245 | } | ||
246 | |||
247 | if checksumHash != nil { | ||
248 | if err := checksum(dst, checksumHash, checksumValue); err != nil { | ||
249 | return err | ||
250 | } | ||
251 | } | ||
252 | |||
253 | if decompressor != nil { | ||
254 | // We have a decompressor, so decompress the current destination | ||
255 | // into the final destination with the proper mode. | ||
256 | err := decompressor.Decompress(decompressDst, dst, decompressDir) | ||
257 | if err != nil { | ||
258 | return err | ||
259 | } | ||
260 | |||
261 | // Swap the information back | ||
262 | dst = decompressDst | ||
263 | if decompressDir { | ||
264 | mode = ClientModeAny | ||
265 | } else { | ||
266 | mode = ClientModeFile | ||
267 | } | ||
268 | } | ||
269 | |||
270 | // We check the dir value again because it can be switched back | ||
271 | // if we were unarchiving. If we're still only Get-ing a file, then | ||
272 | // we're done. | ||
273 | if mode == ClientModeFile { | ||
274 | return nil | ||
275 | } | ||
276 | } | ||
277 | |||
278 | // If we're at this point we're either downloading a directory or we've | ||
279 | // downloaded and unarchived a directory and we're just checking subdir. | ||
280 | // In the case we have a decompressor we don't Get because it was Get | ||
281 | // above. | ||
282 | if decompressor == nil { | ||
283 | // If we're getting a directory, then this is an error. You cannot | ||
284 | // checksum a directory. TODO: test | ||
285 | if checksumHash != nil { | ||
286 | return fmt.Errorf( | ||
287 | "checksum cannot be specified for directory download") | ||
288 | } | ||
289 | |||
290 | // We're downloading a directory, which might require a bit more work | ||
291 | // if we're specifying a subdir. | ||
292 | err := g.Get(dst, u) | ||
293 | if err != nil { | ||
294 | err = fmt.Errorf("error downloading '%s': %s", src, err) | ||
295 | return err | ||
296 | } | ||
297 | } | ||
298 | |||
299 | // If we have a subdir, copy that over | ||
300 | if subDir != "" { | ||
301 | if err := os.RemoveAll(realDst); err != nil { | ||
302 | return err | ||
303 | } | ||
304 | if err := os.MkdirAll(realDst, 0755); err != nil { | ||
305 | return err | ||
306 | } | ||
307 | |||
308 | return copyDir(realDst, filepath.Join(dst, subDir), false) | ||
309 | } | ||
310 | |||
311 | return nil | ||
312 | } | ||
313 | |||
314 | // checksum is a simple method to compute the checksum of a source file | ||
315 | // and compare it to the given expected value. | ||
316 | func checksum(source string, h hash.Hash, v []byte) error { | ||
317 | f, err := os.Open(source) | ||
318 | if err != nil { | ||
319 | return fmt.Errorf("Failed to open file for checksum: %s", err) | ||
320 | } | ||
321 | defer f.Close() | ||
322 | |||
323 | if _, err := io.Copy(h, f); err != nil { | ||
324 | return fmt.Errorf("Failed to hash: %s", err) | ||
325 | } | ||
326 | |||
327 | if actual := h.Sum(nil); !bytes.Equal(actual, v) { | ||
328 | return fmt.Errorf( | ||
329 | "Checksums did not match.\nExpected: %s\nGot: %s", | ||
330 | hex.EncodeToString(v), | ||
331 | hex.EncodeToString(actual)) | ||
332 | } | ||
333 | |||
334 | return nil | ||
335 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/client_mode.go b/vendor/github.com/hashicorp/go-getter/client_mode.go new file mode 100644 index 0000000..7f02509 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/client_mode.go | |||
@@ -0,0 +1,24 @@ | |||
1 | package getter | ||
2 | |||
3 | // ClientMode is the mode that the client operates in. | ||
4 | type ClientMode uint | ||
5 | |||
6 | const ( | ||
7 | ClientModeInvalid ClientMode = iota | ||
8 | |||
9 | // ClientModeAny downloads anything it can. In this mode, dst must | ||
10 | // be a directory. If src is a file, it is saved into the directory | ||
11 | // with the basename of the URL. If src is a directory or archive, | ||
12 | // it is unpacked directly into dst. | ||
13 | ClientModeAny | ||
14 | |||
15 | // ClientModeFile downloads a single file. In this mode, dst must | ||
16 | // be a file path (doesn't have to exist). src must point to a single | ||
17 | // file. It is saved as dst. | ||
18 | ClientModeFile | ||
19 | |||
20 | // ClientModeDir downloads a directory. In this mode, dst must be | ||
21 | // a directory path (doesn't have to exist). src must point to an | ||
22 | // archive or directory (such as in s3). | ||
23 | ClientModeDir | ||
24 | ) | ||
diff --git a/vendor/github.com/hashicorp/go-getter/copy_dir.go b/vendor/github.com/hashicorp/go-getter/copy_dir.go new file mode 100644 index 0000000..2f58e8a --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/copy_dir.go | |||
@@ -0,0 +1,78 @@ | |||
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 | // | ||
13 | // If ignoreDot is set to true, then dot-prefixed files/folders are ignored. | ||
14 | func copyDir(dst string, src string, ignoreDot bool) error { | ||
15 | src, err := filepath.EvalSymlinks(src) | ||
16 | if err != nil { | ||
17 | return err | ||
18 | } | ||
19 | |||
20 | walkFn := func(path string, info os.FileInfo, err error) error { | ||
21 | if err != nil { | ||
22 | return err | ||
23 | } | ||
24 | if path == src { | ||
25 | return nil | ||
26 | } | ||
27 | |||
28 | if ignoreDot && strings.HasPrefix(filepath.Base(path), ".") { | ||
29 | // Skip any dot files | ||
30 | if info.IsDir() { | ||
31 | return filepath.SkipDir | ||
32 | } else { | ||
33 | return nil | ||
34 | } | ||
35 | } | ||
36 | |||
37 | // The "path" has the src prefixed to it. We need to join our | ||
38 | // destination with the path without the src on it. | ||
39 | dstPath := filepath.Join(dst, path[len(src):]) | ||
40 | |||
41 | // If we have a directory, make that subdirectory, then continue | ||
42 | // the walk. | ||
43 | if info.IsDir() { | ||
44 | if path == filepath.Join(src, dst) { | ||
45 | // dst is in src; don't walk it. | ||
46 | return nil | ||
47 | } | ||
48 | |||
49 | if err := os.MkdirAll(dstPath, 0755); err != nil { | ||
50 | return err | ||
51 | } | ||
52 | |||
53 | return nil | ||
54 | } | ||
55 | |||
56 | // If we have a file, copy the contents. | ||
57 | srcF, err := os.Open(path) | ||
58 | if err != nil { | ||
59 | return err | ||
60 | } | ||
61 | defer srcF.Close() | ||
62 | |||
63 | dstF, err := os.Create(dstPath) | ||
64 | if err != nil { | ||
65 | return err | ||
66 | } | ||
67 | defer dstF.Close() | ||
68 | |||
69 | if _, err := io.Copy(dstF, srcF); err != nil { | ||
70 | return err | ||
71 | } | ||
72 | |||
73 | // Chmod it | ||
74 | return os.Chmod(dstPath, info.Mode()) | ||
75 | } | ||
76 | |||
77 | return filepath.Walk(src, walkFn) | ||
78 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/decompress.go b/vendor/github.com/hashicorp/go-getter/decompress.go new file mode 100644 index 0000000..d18174c --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/decompress.go | |||
@@ -0,0 +1,29 @@ | |||
1 | package getter | ||
2 | |||
3 | // Decompressor defines the interface that must be implemented to add | ||
4 | // support for decompressing a type. | ||
5 | type Decompressor interface { | ||
6 | // Decompress should decompress src to dst. dir specifies whether dst | ||
7 | // is a directory or single file. src is guaranteed to be a single file | ||
8 | // that exists. dst is not guaranteed to exist already. | ||
9 | Decompress(dst, src string, dir bool) error | ||
10 | } | ||
11 | |||
12 | // Decompressors is the mapping of extension to the Decompressor implementation | ||
13 | // that will decompress that extension/type. | ||
14 | var Decompressors map[string]Decompressor | ||
15 | |||
16 | func init() { | ||
17 | tbzDecompressor := new(TarBzip2Decompressor) | ||
18 | tgzDecompressor := new(TarGzipDecompressor) | ||
19 | |||
20 | Decompressors = map[string]Decompressor{ | ||
21 | "bz2": new(Bzip2Decompressor), | ||
22 | "gz": new(GzipDecompressor), | ||
23 | "tar.bz2": tbzDecompressor, | ||
24 | "tar.gz": tgzDecompressor, | ||
25 | "tbz2": tbzDecompressor, | ||
26 | "tgz": tgzDecompressor, | ||
27 | "zip": new(ZipDecompressor), | ||
28 | } | ||
29 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/decompress_bzip2.go b/vendor/github.com/hashicorp/go-getter/decompress_bzip2.go new file mode 100644 index 0000000..339f4cf --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/decompress_bzip2.go | |||
@@ -0,0 +1,45 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "compress/bzip2" | ||
5 | "fmt" | ||
6 | "io" | ||
7 | "os" | ||
8 | "path/filepath" | ||
9 | ) | ||
10 | |||
11 | // Bzip2Decompressor is an implementation of Decompressor that can | ||
12 | // decompress bz2 files. | ||
13 | type Bzip2Decompressor struct{} | ||
14 | |||
15 | func (d *Bzip2Decompressor) Decompress(dst, src string, dir bool) error { | ||
16 | // Directory isn't supported at all | ||
17 | if dir { | ||
18 | return fmt.Errorf("bzip2-compressed files can only unarchive to a single file") | ||
19 | } | ||
20 | |||
21 | // If we're going into a directory we should make that first | ||
22 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
23 | return err | ||
24 | } | ||
25 | |||
26 | // File first | ||
27 | f, err := os.Open(src) | ||
28 | if err != nil { | ||
29 | return err | ||
30 | } | ||
31 | defer f.Close() | ||
32 | |||
33 | // Bzip2 compression is second | ||
34 | bzipR := bzip2.NewReader(f) | ||
35 | |||
36 | // Copy it out | ||
37 | dstF, err := os.Create(dst) | ||
38 | if err != nil { | ||
39 | return err | ||
40 | } | ||
41 | defer dstF.Close() | ||
42 | |||
43 | _, err = io.Copy(dstF, bzipR) | ||
44 | return err | ||
45 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/decompress_gzip.go b/vendor/github.com/hashicorp/go-getter/decompress_gzip.go new file mode 100644 index 0000000..2001054 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/decompress_gzip.go | |||
@@ -0,0 +1,49 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "compress/gzip" | ||
5 | "fmt" | ||
6 | "io" | ||
7 | "os" | ||
8 | "path/filepath" | ||
9 | ) | ||
10 | |||
11 | // GzipDecompressor is an implementation of Decompressor that can | ||
12 | // decompress bz2 files. | ||
13 | type GzipDecompressor struct{} | ||
14 | |||
15 | func (d *GzipDecompressor) Decompress(dst, src string, dir bool) error { | ||
16 | // Directory isn't supported at all | ||
17 | if dir { | ||
18 | return fmt.Errorf("gzip-compressed files can only unarchive to a single file") | ||
19 | } | ||
20 | |||
21 | // If we're going into a directory we should make that first | ||
22 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
23 | return err | ||
24 | } | ||
25 | |||
26 | // File first | ||
27 | f, err := os.Open(src) | ||
28 | if err != nil { | ||
29 | return err | ||
30 | } | ||
31 | defer f.Close() | ||
32 | |||
33 | // gzip compression is second | ||
34 | gzipR, err := gzip.NewReader(f) | ||
35 | if err != nil { | ||
36 | return err | ||
37 | } | ||
38 | defer gzipR.Close() | ||
39 | |||
40 | // Copy it out | ||
41 | dstF, err := os.Create(dst) | ||
42 | if err != nil { | ||
43 | return err | ||
44 | } | ||
45 | defer dstF.Close() | ||
46 | |||
47 | _, err = io.Copy(dstF, gzipR) | ||
48 | return err | ||
49 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/decompress_tbz2.go b/vendor/github.com/hashicorp/go-getter/decompress_tbz2.go new file mode 100644 index 0000000..c46ed44 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/decompress_tbz2.go | |||
@@ -0,0 +1,95 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "archive/tar" | ||
5 | "compress/bzip2" | ||
6 | "fmt" | ||
7 | "io" | ||
8 | "os" | ||
9 | "path/filepath" | ||
10 | ) | ||
11 | |||
12 | // TarBzip2Decompressor is an implementation of Decompressor that can | ||
13 | // decompress tar.bz2 files. | ||
14 | type TarBzip2Decompressor struct{} | ||
15 | |||
16 | func (d *TarBzip2Decompressor) Decompress(dst, src string, dir bool) error { | ||
17 | // If we're going into a directory we should make that first | ||
18 | mkdir := dst | ||
19 | if !dir { | ||
20 | mkdir = filepath.Dir(dst) | ||
21 | } | ||
22 | if err := os.MkdirAll(mkdir, 0755); err != nil { | ||
23 | return err | ||
24 | } | ||
25 | |||
26 | // File first | ||
27 | f, err := os.Open(src) | ||
28 | if err != nil { | ||
29 | return err | ||
30 | } | ||
31 | defer f.Close() | ||
32 | |||
33 | // Bzip2 compression is second | ||
34 | bzipR := bzip2.NewReader(f) | ||
35 | |||
36 | // Once bzip decompressed we have a tar format | ||
37 | tarR := tar.NewReader(bzipR) | ||
38 | done := false | ||
39 | for { | ||
40 | hdr, err := tarR.Next() | ||
41 | if err == io.EOF { | ||
42 | if !done { | ||
43 | // Empty archive | ||
44 | return fmt.Errorf("empty archive: %s", src) | ||
45 | } | ||
46 | |||
47 | return nil | ||
48 | } | ||
49 | if err != nil { | ||
50 | return err | ||
51 | } | ||
52 | |||
53 | path := dst | ||
54 | if dir { | ||
55 | path = filepath.Join(path, hdr.Name) | ||
56 | } | ||
57 | |||
58 | if hdr.FileInfo().IsDir() { | ||
59 | if dir { | ||
60 | return fmt.Errorf("expected a single file: %s", src) | ||
61 | } | ||
62 | |||
63 | // A directory, just make the directory and continue unarchiving... | ||
64 | if err := os.MkdirAll(path, 0755); err != nil { | ||
65 | return err | ||
66 | } | ||
67 | |||
68 | continue | ||
69 | } | ||
70 | |||
71 | // We have a file. If we already decoded, then it is an error | ||
72 | if !dir && done { | ||
73 | return fmt.Errorf("expected a single file, got multiple: %s", src) | ||
74 | } | ||
75 | |||
76 | // Mark that we're done so future in single file mode errors | ||
77 | done = true | ||
78 | |||
79 | // Open the file for writing | ||
80 | dstF, err := os.Create(path) | ||
81 | if err != nil { | ||
82 | return err | ||
83 | } | ||
84 | _, err = io.Copy(dstF, tarR) | ||
85 | dstF.Close() | ||
86 | if err != nil { | ||
87 | return err | ||
88 | } | ||
89 | |||
90 | // Chmod the file | ||
91 | if err := os.Chmod(path, hdr.FileInfo().Mode()); err != nil { | ||
92 | return err | ||
93 | } | ||
94 | } | ||
95 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/decompress_testing.go b/vendor/github.com/hashicorp/go-getter/decompress_testing.go new file mode 100644 index 0000000..686d6c2 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/decompress_testing.go | |||
@@ -0,0 +1,134 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "crypto/md5" | ||
5 | "encoding/hex" | ||
6 | "io" | ||
7 | "io/ioutil" | ||
8 | "os" | ||
9 | "path/filepath" | ||
10 | "reflect" | ||
11 | "runtime" | ||
12 | "sort" | ||
13 | "strings" | ||
14 | "testing" | ||
15 | ) | ||
16 | |||
17 | // TestDecompressCase is a single test case for testing decompressors | ||
18 | type TestDecompressCase struct { | ||
19 | Input string // Input is the complete path to the input file | ||
20 | Dir bool // Dir is whether or not we're testing directory mode | ||
21 | Err bool // Err is whether we expect an error or not | ||
22 | DirList []string // DirList is the list of files for Dir mode | ||
23 | FileMD5 string // FileMD5 is the expected MD5 for a single file | ||
24 | } | ||
25 | |||
26 | // TestDecompressor is a helper function for testing generic decompressors. | ||
27 | func TestDecompressor(t *testing.T, d Decompressor, cases []TestDecompressCase) { | ||
28 | for _, tc := range cases { | ||
29 | t.Logf("Testing: %s", tc.Input) | ||
30 | |||
31 | // Temporary dir to store stuff | ||
32 | td, err := ioutil.TempDir("", "getter") | ||
33 | if err != nil { | ||
34 | t.Fatalf("err: %s", err) | ||
35 | } | ||
36 | |||
37 | // Destination is always joining result so that we have a new path | ||
38 | dst := filepath.Join(td, "subdir", "result") | ||
39 | |||
40 | // We use a function so defers work | ||
41 | func() { | ||
42 | defer os.RemoveAll(td) | ||
43 | |||
44 | // Decompress | ||
45 | err := d.Decompress(dst, tc.Input, tc.Dir) | ||
46 | if (err != nil) != tc.Err { | ||
47 | t.Fatalf("err %s: %s", tc.Input, err) | ||
48 | } | ||
49 | if tc.Err { | ||
50 | return | ||
51 | } | ||
52 | |||
53 | // If it isn't a directory, then check for a single file | ||
54 | if !tc.Dir { | ||
55 | fi, err := os.Stat(dst) | ||
56 | if err != nil { | ||
57 | t.Fatalf("err %s: %s", tc.Input, err) | ||
58 | } | ||
59 | if fi.IsDir() { | ||
60 | t.Fatalf("err %s: expected file, got directory", tc.Input) | ||
61 | } | ||
62 | if tc.FileMD5 != "" { | ||
63 | actual := testMD5(t, dst) | ||
64 | expected := tc.FileMD5 | ||
65 | if actual != expected { | ||
66 | t.Fatalf("err %s: expected MD5 %s, got %s", tc.Input, expected, actual) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | return | ||
71 | } | ||
72 | |||
73 | // Convert expected for windows | ||
74 | expected := tc.DirList | ||
75 | if runtime.GOOS == "windows" { | ||
76 | for i, v := range expected { | ||
77 | expected[i] = strings.Replace(v, "/", "\\", -1) | ||
78 | } | ||
79 | } | ||
80 | |||
81 | // Directory, check for the correct contents | ||
82 | actual := testListDir(t, dst) | ||
83 | if !reflect.DeepEqual(actual, expected) { | ||
84 | t.Fatalf("bad %s\n\n%#v\n\n%#v", tc.Input, actual, expected) | ||
85 | } | ||
86 | }() | ||
87 | } | ||
88 | } | ||
89 | |||
90 | func testListDir(t *testing.T, path string) []string { | ||
91 | var result []string | ||
92 | err := filepath.Walk(path, func(sub string, info os.FileInfo, err error) error { | ||
93 | if err != nil { | ||
94 | return err | ||
95 | } | ||
96 | |||
97 | sub = strings.TrimPrefix(sub, path) | ||
98 | if sub == "" { | ||
99 | return nil | ||
100 | } | ||
101 | sub = sub[1:] // Trim the leading path sep. | ||
102 | |||
103 | // If it is a dir, add trailing sep | ||
104 | if info.IsDir() { | ||
105 | sub += "/" | ||
106 | } | ||
107 | |||
108 | result = append(result, sub) | ||
109 | return nil | ||
110 | }) | ||
111 | if err != nil { | ||
112 | t.Fatalf("err: %s", err) | ||
113 | } | ||
114 | |||
115 | sort.Strings(result) | ||
116 | return result | ||
117 | } | ||
118 | |||
119 | func testMD5(t *testing.T, path string) string { | ||
120 | f, err := os.Open(path) | ||
121 | if err != nil { | ||
122 | t.Fatalf("err: %s", err) | ||
123 | } | ||
124 | defer f.Close() | ||
125 | |||
126 | h := md5.New() | ||
127 | _, err = io.Copy(h, f) | ||
128 | if err != nil { | ||
129 | t.Fatalf("err: %s", err) | ||
130 | } | ||
131 | |||
132 | result := h.Sum(nil) | ||
133 | return hex.EncodeToString(result) | ||
134 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/decompress_tgz.go b/vendor/github.com/hashicorp/go-getter/decompress_tgz.go new file mode 100644 index 0000000..e8b1c31 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/decompress_tgz.go | |||
@@ -0,0 +1,99 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "archive/tar" | ||
5 | "compress/gzip" | ||
6 | "fmt" | ||
7 | "io" | ||
8 | "os" | ||
9 | "path/filepath" | ||
10 | ) | ||
11 | |||
12 | // TarGzipDecompressor is an implementation of Decompressor that can | ||
13 | // decompress tar.gzip files. | ||
14 | type TarGzipDecompressor struct{} | ||
15 | |||
16 | func (d *TarGzipDecompressor) Decompress(dst, src string, dir bool) error { | ||
17 | // If we're going into a directory we should make that first | ||
18 | mkdir := dst | ||
19 | if !dir { | ||
20 | mkdir = filepath.Dir(dst) | ||
21 | } | ||
22 | if err := os.MkdirAll(mkdir, 0755); err != nil { | ||
23 | return err | ||
24 | } | ||
25 | |||
26 | // File first | ||
27 | f, err := os.Open(src) | ||
28 | if err != nil { | ||
29 | return err | ||
30 | } | ||
31 | defer f.Close() | ||
32 | |||
33 | // Gzip compression is second | ||
34 | gzipR, err := gzip.NewReader(f) | ||
35 | if err != nil { | ||
36 | return fmt.Errorf("Error opening a gzip reader for %s: %s", src, err) | ||
37 | } | ||
38 | defer gzipR.Close() | ||
39 | |||
40 | // Once gzip decompressed we have a tar format | ||
41 | tarR := tar.NewReader(gzipR) | ||
42 | done := false | ||
43 | for { | ||
44 | hdr, err := tarR.Next() | ||
45 | if err == io.EOF { | ||
46 | if !done { | ||
47 | // Empty archive | ||
48 | return fmt.Errorf("empty archive: %s", src) | ||
49 | } | ||
50 | |||
51 | return nil | ||
52 | } | ||
53 | if err != nil { | ||
54 | return err | ||
55 | } | ||
56 | |||
57 | path := dst | ||
58 | if dir { | ||
59 | path = filepath.Join(path, hdr.Name) | ||
60 | } | ||
61 | |||
62 | if hdr.FileInfo().IsDir() { | ||
63 | if !dir { | ||
64 | return fmt.Errorf("expected a single file: %s", src) | ||
65 | } | ||
66 | |||
67 | // A directory, just make the directory and continue unarchiving... | ||
68 | if err := os.MkdirAll(path, 0755); err != nil { | ||
69 | return err | ||
70 | } | ||
71 | |||
72 | continue | ||
73 | } | ||
74 | |||
75 | // We have a file. If we already decoded, then it is an error | ||
76 | if !dir && done { | ||
77 | return fmt.Errorf("expected a single file, got multiple: %s", src) | ||
78 | } | ||
79 | |||
80 | // Mark that we're done so future in single file mode errors | ||
81 | done = true | ||
82 | |||
83 | // Open the file for writing | ||
84 | dstF, err := os.Create(path) | ||
85 | if err != nil { | ||
86 | return err | ||
87 | } | ||
88 | _, err = io.Copy(dstF, tarR) | ||
89 | dstF.Close() | ||
90 | if err != nil { | ||
91 | return err | ||
92 | } | ||
93 | |||
94 | // Chmod the file | ||
95 | if err := os.Chmod(path, hdr.FileInfo().Mode()); err != nil { | ||
96 | return err | ||
97 | } | ||
98 | } | ||
99 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/decompress_zip.go b/vendor/github.com/hashicorp/go-getter/decompress_zip.go new file mode 100644 index 0000000..a065c07 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/decompress_zip.go | |||
@@ -0,0 +1,96 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "archive/zip" | ||
5 | "fmt" | ||
6 | "io" | ||
7 | "os" | ||
8 | "path/filepath" | ||
9 | ) | ||
10 | |||
11 | // ZipDecompressor is an implementation of Decompressor that can | ||
12 | // decompress tar.gzip files. | ||
13 | type ZipDecompressor struct{} | ||
14 | |||
15 | func (d *ZipDecompressor) Decompress(dst, src string, dir bool) error { | ||
16 | // If we're going into a directory we should make that first | ||
17 | mkdir := dst | ||
18 | if !dir { | ||
19 | mkdir = filepath.Dir(dst) | ||
20 | } | ||
21 | if err := os.MkdirAll(mkdir, 0755); err != nil { | ||
22 | return err | ||
23 | } | ||
24 | |||
25 | // Open the zip | ||
26 | zipR, err := zip.OpenReader(src) | ||
27 | if err != nil { | ||
28 | return err | ||
29 | } | ||
30 | defer zipR.Close() | ||
31 | |||
32 | // Check the zip integrity | ||
33 | if len(zipR.File) == 0 { | ||
34 | // Empty archive | ||
35 | return fmt.Errorf("empty archive: %s", src) | ||
36 | } | ||
37 | if !dir && len(zipR.File) > 1 { | ||
38 | return fmt.Errorf("expected a single file: %s", src) | ||
39 | } | ||
40 | |||
41 | // Go through and unarchive | ||
42 | for _, f := range zipR.File { | ||
43 | path := dst | ||
44 | if dir { | ||
45 | path = filepath.Join(path, f.Name) | ||
46 | } | ||
47 | |||
48 | if f.FileInfo().IsDir() { | ||
49 | if !dir { | ||
50 | return fmt.Errorf("expected a single file: %s", src) | ||
51 | } | ||
52 | |||
53 | // A directory, just make the directory and continue unarchiving... | ||
54 | if err := os.MkdirAll(path, 0755); err != nil { | ||
55 | return err | ||
56 | } | ||
57 | |||
58 | continue | ||
59 | } | ||
60 | |||
61 | // Create the enclosing directories if we must. ZIP files aren't | ||
62 | // required to contain entries for just the directories so this | ||
63 | // can happen. | ||
64 | if dir { | ||
65 | if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { | ||
66 | return err | ||
67 | } | ||
68 | } | ||
69 | |||
70 | // Open the file for reading | ||
71 | srcF, err := f.Open() | ||
72 | if err != nil { | ||
73 | return err | ||
74 | } | ||
75 | |||
76 | // Open the file for writing | ||
77 | dstF, err := os.Create(path) | ||
78 | if err != nil { | ||
79 | srcF.Close() | ||
80 | return err | ||
81 | } | ||
82 | _, err = io.Copy(dstF, srcF) | ||
83 | srcF.Close() | ||
84 | dstF.Close() | ||
85 | if err != nil { | ||
86 | return err | ||
87 | } | ||
88 | |||
89 | // Chmod the file | ||
90 | if err := os.Chmod(path, f.Mode()); err != nil { | ||
91 | return err | ||
92 | } | ||
93 | } | ||
94 | |||
95 | return nil | ||
96 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/detect.go b/vendor/github.com/hashicorp/go-getter/detect.go new file mode 100644 index 0000000..481b737 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/detect.go | |||
@@ -0,0 +1,97 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "path/filepath" | ||
6 | |||
7 | "github.com/hashicorp/go-getter/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(S3Detector), | ||
28 | new(FileDetector), | ||
29 | } | ||
30 | } | ||
31 | |||
32 | // Detect turns a source string into another source string if it is | ||
33 | // detected to be of a known pattern. | ||
34 | // | ||
35 | // The third parameter should be the list of detectors to use in the | ||
36 | // order to try them. If you don't want to configure this, just use | ||
37 | // the global Detectors variable. | ||
38 | // | ||
39 | // This is safe to be called with an already valid source string: Detect | ||
40 | // will just return it. | ||
41 | func Detect(src string, pwd string, ds []Detector) (string, error) { | ||
42 | getForce, getSrc := getForcedGetter(src) | ||
43 | |||
44 | // Separate out the subdir if there is one, we don't pass that to detect | ||
45 | getSrc, subDir := SourceDirSubdir(getSrc) | ||
46 | |||
47 | u, err := url.Parse(getSrc) | ||
48 | if err == nil && u.Scheme != "" { | ||
49 | // Valid URL | ||
50 | return src, nil | ||
51 | } | ||
52 | |||
53 | for _, d := range ds { | ||
54 | result, ok, err := d.Detect(getSrc, pwd) | ||
55 | if err != nil { | ||
56 | return "", err | ||
57 | } | ||
58 | if !ok { | ||
59 | continue | ||
60 | } | ||
61 | |||
62 | var detectForce string | ||
63 | detectForce, result = getForcedGetter(result) | ||
64 | result, detectSubdir := SourceDirSubdir(result) | ||
65 | |||
66 | // If we have a subdir from the detection, then prepend it to our | ||
67 | // requested subdir. | ||
68 | if detectSubdir != "" { | ||
69 | if subDir != "" { | ||
70 | subDir = filepath.Join(detectSubdir, subDir) | ||
71 | } else { | ||
72 | subDir = detectSubdir | ||
73 | } | ||
74 | } | ||
75 | if subDir != "" { | ||
76 | u, err := url.Parse(result) | ||
77 | if err != nil { | ||
78 | return "", fmt.Errorf("Error parsing URL: %s", err) | ||
79 | } | ||
80 | u.Path += "//" + subDir | ||
81 | result = u.String() | ||
82 | } | ||
83 | |||
84 | // Preserve the forced getter if it exists. We try to use the | ||
85 | // original set force first, followed by any force set by the | ||
86 | // detector. | ||
87 | if getForce != "" { | ||
88 | result = fmt.Sprintf("%s::%s", getForce, result) | ||
89 | } else if detectForce != "" { | ||
90 | result = fmt.Sprintf("%s::%s", detectForce, result) | ||
91 | } | ||
92 | |||
93 | return result, nil | ||
94 | } | ||
95 | |||
96 | return "", fmt.Errorf("invalid source string: %s", src) | ||
97 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/detect_bitbucket.go b/vendor/github.com/hashicorp/go-getter/detect_bitbucket.go new file mode 100644 index 0000000..a183a17 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/detect_bitbucket.go | |||
@@ -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 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/detect_file.go b/vendor/github.com/hashicorp/go-getter/detect_file.go new file mode 100644 index 0000000..756ea43 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/detect_file.go | |||
@@ -0,0 +1,67 @@ | |||
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 | // The symlink itself might be a relative path, so we have to | ||
41 | // resolve this to have a correctly rooted URL. | ||
42 | pwd, err = filepath.Abs(pwd) | ||
43 | if err != nil { | ||
44 | return "", true, err | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | |||
49 | src = filepath.Join(pwd, src) | ||
50 | } | ||
51 | |||
52 | return fmtFileURL(src), true, nil | ||
53 | } | ||
54 | |||
55 | func fmtFileURL(path string) string { | ||
56 | if runtime.GOOS == "windows" { | ||
57 | // Make sure we're using "/" on Windows. URLs are "/"-based. | ||
58 | path = filepath.ToSlash(path) | ||
59 | return fmt.Sprintf("file://%s", path) | ||
60 | } | ||
61 | |||
62 | // Make sure that we don't start with "/" since we add that below. | ||
63 | if path[0] == '/' { | ||
64 | path = path[1:] | ||
65 | } | ||
66 | return fmt.Sprintf("file:///%s", path) | ||
67 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/detect_github.go b/vendor/github.com/hashicorp/go-getter/detect_github.go new file mode 100644 index 0000000..c084ad9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/detect_github.go | |||
@@ -0,0 +1,73 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "net/url" | ||
6 | "strings" | ||
7 | ) | ||
8 | |||
9 | // GitHubDetector implements Detector to detect GitHub URLs and turn | ||
10 | // them into URLs that the Git Getter can understand. | ||
11 | type GitHubDetector struct{} | ||
12 | |||
13 | func (d *GitHubDetector) Detect(src, _ string) (string, bool, error) { | ||
14 | if len(src) == 0 { | ||
15 | return "", false, nil | ||
16 | } | ||
17 | |||
18 | if strings.HasPrefix(src, "github.com/") { | ||
19 | return d.detectHTTP(src) | ||
20 | } else if strings.HasPrefix(src, "git@github.com:") { | ||
21 | return d.detectSSH(src) | ||
22 | } | ||
23 | |||
24 | return "", false, nil | ||
25 | } | ||
26 | |||
27 | func (d *GitHubDetector) detectHTTP(src string) (string, bool, error) { | ||
28 | parts := strings.Split(src, "/") | ||
29 | if len(parts) < 3 { | ||
30 | return "", false, fmt.Errorf( | ||
31 | "GitHub URLs should be github.com/username/repo") | ||
32 | } | ||
33 | |||
34 | urlStr := fmt.Sprintf("https://%s", strings.Join(parts[:3], "/")) | ||
35 | url, err := url.Parse(urlStr) | ||
36 | if err != nil { | ||
37 | return "", true, fmt.Errorf("error parsing GitHub URL: %s", err) | ||
38 | } | ||
39 | |||
40 | if !strings.HasSuffix(url.Path, ".git") { | ||
41 | url.Path += ".git" | ||
42 | } | ||
43 | |||
44 | if len(parts) > 3 { | ||
45 | url.Path += "//" + strings.Join(parts[3:], "/") | ||
46 | } | ||
47 | |||
48 | return "git::" + url.String(), true, nil | ||
49 | } | ||
50 | |||
51 | func (d *GitHubDetector) detectSSH(src string) (string, bool, error) { | ||
52 | idx := strings.Index(src, ":") | ||
53 | qidx := strings.Index(src, "?") | ||
54 | if qidx == -1 { | ||
55 | qidx = len(src) | ||
56 | } | ||
57 | |||
58 | var u url.URL | ||
59 | u.Scheme = "ssh" | ||
60 | u.User = url.User("git") | ||
61 | u.Host = "github.com" | ||
62 | u.Path = src[idx+1 : qidx] | ||
63 | if qidx < len(src) { | ||
64 | q, err := url.ParseQuery(src[qidx+1:]) | ||
65 | if err != nil { | ||
66 | return "", true, fmt.Errorf("error parsing GitHub SSH URL: %s", err) | ||
67 | } | ||
68 | |||
69 | u.RawQuery = q.Encode() | ||
70 | } | ||
71 | |||
72 | return "git::" + u.String(), true, nil | ||
73 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/detect_s3.go b/vendor/github.com/hashicorp/go-getter/detect_s3.go new file mode 100644 index 0000000..8e0f4a0 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/detect_s3.go | |||
@@ -0,0 +1,61 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "net/url" | ||
6 | "strings" | ||
7 | ) | ||
8 | |||
9 | // S3Detector implements Detector to detect S3 URLs and turn | ||
10 | // them into URLs that the S3 getter can understand. | ||
11 | type S3Detector struct{} | ||
12 | |||
13 | func (d *S3Detector) Detect(src, _ string) (string, bool, error) { | ||
14 | if len(src) == 0 { | ||
15 | return "", false, nil | ||
16 | } | ||
17 | |||
18 | if strings.Contains(src, ".amazonaws.com/") { | ||
19 | return d.detectHTTP(src) | ||
20 | } | ||
21 | |||
22 | return "", false, nil | ||
23 | } | ||
24 | |||
25 | func (d *S3Detector) detectHTTP(src string) (string, bool, error) { | ||
26 | parts := strings.Split(src, "/") | ||
27 | if len(parts) < 2 { | ||
28 | return "", false, fmt.Errorf( | ||
29 | "URL is not a valid S3 URL") | ||
30 | } | ||
31 | |||
32 | hostParts := strings.Split(parts[0], ".") | ||
33 | if len(hostParts) == 3 { | ||
34 | return d.detectPathStyle(hostParts[0], parts[1:]) | ||
35 | } else if len(hostParts) == 4 { | ||
36 | return d.detectVhostStyle(hostParts[1], hostParts[0], parts[1:]) | ||
37 | } else { | ||
38 | return "", false, fmt.Errorf( | ||
39 | "URL is not a valid S3 URL") | ||
40 | } | ||
41 | } | ||
42 | |||
43 | func (d *S3Detector) detectPathStyle(region string, parts []string) (string, bool, error) { | ||
44 | urlStr := fmt.Sprintf("https://%s.amazonaws.com/%s", region, strings.Join(parts, "/")) | ||
45 | url, err := url.Parse(urlStr) | ||
46 | if err != nil { | ||
47 | return "", false, fmt.Errorf("error parsing S3 URL: %s", err) | ||
48 | } | ||
49 | |||
50 | return "s3::" + url.String(), true, nil | ||
51 | } | ||
52 | |||
53 | func (d *S3Detector) detectVhostStyle(region, bucket string, parts []string) (string, bool, error) { | ||
54 | urlStr := fmt.Sprintf("https://%s.amazonaws.com/%s/%s", region, bucket, strings.Join(parts, "/")) | ||
55 | url, err := url.Parse(urlStr) | ||
56 | if err != nil { | ||
57 | return "", false, fmt.Errorf("error parsing S3 URL: %s", err) | ||
58 | } | ||
59 | |||
60 | return "s3::" + url.String(), true, nil | ||
61 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/folder_storage.go b/vendor/github.com/hashicorp/go-getter/folder_storage.go new file mode 100644 index 0000000..647ccf4 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/folder_storage.go | |||
@@ -0,0 +1,65 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "crypto/md5" | ||
5 | "encoding/hex" | ||
6 | "fmt" | ||
7 | "os" | ||
8 | "path/filepath" | ||
9 | ) | ||
10 | |||
11 | // FolderStorage is an implementation of the Storage interface that manages | ||
12 | // modules on the disk. | ||
13 | type FolderStorage struct { | ||
14 | // StorageDir is the directory where the modules will be stored. | ||
15 | StorageDir string | ||
16 | } | ||
17 | |||
18 | // Dir implements Storage.Dir | ||
19 | func (s *FolderStorage) Dir(key string) (d string, e bool, err error) { | ||
20 | d = s.dir(key) | ||
21 | _, err = os.Stat(d) | ||
22 | if err == nil { | ||
23 | // Directory exists | ||
24 | e = true | ||
25 | return | ||
26 | } | ||
27 | if os.IsNotExist(err) { | ||
28 | // Directory doesn't exist | ||
29 | d = "" | ||
30 | e = false | ||
31 | err = nil | ||
32 | return | ||
33 | } | ||
34 | |||
35 | // An error | ||
36 | d = "" | ||
37 | e = false | ||
38 | return | ||
39 | } | ||
40 | |||
41 | // Get implements Storage.Get | ||
42 | func (s *FolderStorage) Get(key string, source string, update bool) error { | ||
43 | dir := s.dir(key) | ||
44 | if !update { | ||
45 | if _, err := os.Stat(dir); err == nil { | ||
46 | // If the directory already exists, then we're done since | ||
47 | // we're not updating. | ||
48 | return nil | ||
49 | } else if !os.IsNotExist(err) { | ||
50 | // If the error we got wasn't a file-not-exist error, then | ||
51 | // something went wrong and we should report it. | ||
52 | return fmt.Errorf("Error reading module directory: %s", err) | ||
53 | } | ||
54 | } | ||
55 | |||
56 | // Get the source. This always forces an update. | ||
57 | return Get(dir, source) | ||
58 | } | ||
59 | |||
60 | // dir returns the directory name internally that we'll use to map to | ||
61 | // internally. | ||
62 | func (s *FolderStorage) dir(key string) string { | ||
63 | sum := md5.Sum([]byte(key)) | ||
64 | return filepath.Join(s.StorageDir, hex.EncodeToString(sum[:])) | ||
65 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/get.go b/vendor/github.com/hashicorp/go-getter/get.go new file mode 100644 index 0000000..c3236f5 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get.go | |||
@@ -0,0 +1,139 @@ | |||
1 | // getter is a package for downloading files or directories from a variety of | ||
2 | // protocols. | ||
3 | // | ||
4 | // getter is unique in its ability to download both directories and files. | ||
5 | // It also detects certain source strings to be protocol-specific URLs. For | ||
6 | // example, "github.com/hashicorp/go-getter" would turn into a Git URL and | ||
7 | // use the Git protocol. | ||
8 | // | ||
9 | // Protocols and detectors are extensible. | ||
10 | // | ||
11 | // To get started, see Client. | ||
12 | package getter | ||
13 | |||
14 | import ( | ||
15 | "bytes" | ||
16 | "fmt" | ||
17 | "net/url" | ||
18 | "os/exec" | ||
19 | "regexp" | ||
20 | "syscall" | ||
21 | ) | ||
22 | |||
23 | // Getter defines the interface that schemes must implement to download | ||
24 | // things. | ||
25 | type Getter interface { | ||
26 | // Get downloads the given URL into the given directory. This always | ||
27 | // assumes that we're updating and gets the latest version that it can. | ||
28 | // | ||
29 | // The directory may already exist (if we're updating). If it is in a | ||
30 | // format that isn't understood, an error should be returned. Get shouldn't | ||
31 | // simply nuke the directory. | ||
32 | Get(string, *url.URL) error | ||
33 | |||
34 | // GetFile downloads the give URL into the given path. The URL must | ||
35 | // reference a single file. If possible, the Getter should check if | ||
36 | // the remote end contains the same file and no-op this operation. | ||
37 | GetFile(string, *url.URL) error | ||
38 | |||
39 | // ClientMode returns the mode based on the given URL. This is used to | ||
40 | // allow clients to let the getters decide which mode to use. | ||
41 | ClientMode(*url.URL) (ClientMode, error) | ||
42 | } | ||
43 | |||
44 | // Getters is the mapping of scheme to the Getter implementation that will | ||
45 | // be used to get a dependency. | ||
46 | var Getters map[string]Getter | ||
47 | |||
48 | // forcedRegexp is the regular expression that finds forced getters. This | ||
49 | // syntax is schema::url, example: git::https://foo.com | ||
50 | var forcedRegexp = regexp.MustCompile(`^([A-Za-z0-9]+)::(.+)$`) | ||
51 | |||
52 | func init() { | ||
53 | httpGetter := &HttpGetter{Netrc: true} | ||
54 | |||
55 | Getters = map[string]Getter{ | ||
56 | "file": new(FileGetter), | ||
57 | "git": new(GitGetter), | ||
58 | "hg": new(HgGetter), | ||
59 | "s3": new(S3Getter), | ||
60 | "http": httpGetter, | ||
61 | "https": httpGetter, | ||
62 | } | ||
63 | } | ||
64 | |||
65 | // Get downloads the directory specified by src into the folder specified by | ||
66 | // dst. If dst already exists, Get will attempt to update it. | ||
67 | // | ||
68 | // src is a URL, whereas dst is always just a file path to a folder. This | ||
69 | // folder doesn't need to exist. It will be created if it doesn't exist. | ||
70 | func Get(dst, src string) error { | ||
71 | return (&Client{ | ||
72 | Src: src, | ||
73 | Dst: dst, | ||
74 | Dir: true, | ||
75 | Getters: Getters, | ||
76 | }).Get() | ||
77 | } | ||
78 | |||
79 | // GetAny downloads a URL into the given destination. Unlike Get or | ||
80 | // GetFile, both directories and files are supported. | ||
81 | // | ||
82 | // dst must be a directory. If src is a file, it will be downloaded | ||
83 | // into dst with the basename of the URL. If src is a directory or | ||
84 | // archive, it will be unpacked directly into dst. | ||
85 | func GetAny(dst, src string) error { | ||
86 | return (&Client{ | ||
87 | Src: src, | ||
88 | Dst: dst, | ||
89 | Mode: ClientModeAny, | ||
90 | Getters: Getters, | ||
91 | }).Get() | ||
92 | } | ||
93 | |||
94 | // GetFile downloads the file specified by src into the path specified by | ||
95 | // dst. | ||
96 | func GetFile(dst, src string) error { | ||
97 | return (&Client{ | ||
98 | Src: src, | ||
99 | Dst: dst, | ||
100 | Dir: false, | ||
101 | Getters: Getters, | ||
102 | }).Get() | ||
103 | } | ||
104 | |||
105 | // getRunCommand is a helper that will run a command and capture the output | ||
106 | // in the case an error happens. | ||
107 | func getRunCommand(cmd *exec.Cmd) error { | ||
108 | var buf bytes.Buffer | ||
109 | cmd.Stdout = &buf | ||
110 | cmd.Stderr = &buf | ||
111 | err := cmd.Run() | ||
112 | if err == nil { | ||
113 | return nil | ||
114 | } | ||
115 | if exiterr, ok := err.(*exec.ExitError); ok { | ||
116 | // The program has exited with an exit code != 0 | ||
117 | if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { | ||
118 | return fmt.Errorf( | ||
119 | "%s exited with %d: %s", | ||
120 | cmd.Path, | ||
121 | status.ExitStatus(), | ||
122 | buf.String()) | ||
123 | } | ||
124 | } | ||
125 | |||
126 | return fmt.Errorf("error running %s: %s", cmd.Path, buf.String()) | ||
127 | } | ||
128 | |||
129 | // getForcedGetter takes a source and returns the tuple of the forced | ||
130 | // getter and the raw URL (without the force syntax). | ||
131 | func getForcedGetter(src string) (string, string) { | ||
132 | var forced string | ||
133 | if ms := forcedRegexp.FindStringSubmatch(src); ms != nil { | ||
134 | forced = ms[1] | ||
135 | src = ms[2] | ||
136 | } | ||
137 | |||
138 | return forced, src | ||
139 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/get_file.go b/vendor/github.com/hashicorp/go-getter/get_file.go new file mode 100644 index 0000000..e5d2d61 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_file.go | |||
@@ -0,0 +1,32 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "net/url" | ||
5 | "os" | ||
6 | ) | ||
7 | |||
8 | // FileGetter is a Getter implementation that will download a module from | ||
9 | // a file scheme. | ||
10 | type FileGetter struct { | ||
11 | // Copy, if set to true, will copy data instead of using a symlink | ||
12 | Copy bool | ||
13 | } | ||
14 | |||
15 | func (g *FileGetter) ClientMode(u *url.URL) (ClientMode, error) { | ||
16 | path := u.Path | ||
17 | if u.RawPath != "" { | ||
18 | path = u.RawPath | ||
19 | } | ||
20 | |||
21 | fi, err := os.Stat(path) | ||
22 | if err != nil { | ||
23 | return 0, err | ||
24 | } | ||
25 | |||
26 | // Check if the source is a directory. | ||
27 | if fi.IsDir() { | ||
28 | return ClientModeDir, nil | ||
29 | } | ||
30 | |||
31 | return ClientModeFile, nil | ||
32 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/get_file_unix.go b/vendor/github.com/hashicorp/go-getter/get_file_unix.go new file mode 100644 index 0000000..c89a2d5 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_file_unix.go | |||
@@ -0,0 +1,103 @@ | |||
1 | // +build !windows | ||
2 | |||
3 | package getter | ||
4 | |||
5 | import ( | ||
6 | "fmt" | ||
7 | "io" | ||
8 | "net/url" | ||
9 | "os" | ||
10 | "path/filepath" | ||
11 | ) | ||
12 | |||
13 | func (g *FileGetter) Get(dst string, u *url.URL) error { | ||
14 | path := u.Path | ||
15 | if u.RawPath != "" { | ||
16 | path = u.RawPath | ||
17 | } | ||
18 | |||
19 | // The source path must exist and be a directory to be usable. | ||
20 | if fi, err := os.Stat(path); err != nil { | ||
21 | return fmt.Errorf("source path error: %s", err) | ||
22 | } else if !fi.IsDir() { | ||
23 | return fmt.Errorf("source path must be a directory") | ||
24 | } | ||
25 | |||
26 | fi, err := os.Lstat(dst) | ||
27 | if err != nil && !os.IsNotExist(err) { | ||
28 | return err | ||
29 | } | ||
30 | |||
31 | // If the destination already exists, it must be a symlink | ||
32 | if err == nil { | ||
33 | mode := fi.Mode() | ||
34 | if mode&os.ModeSymlink == 0 { | ||
35 | return fmt.Errorf("destination exists and is not a symlink") | ||
36 | } | ||
37 | |||
38 | // Remove the destination | ||
39 | if err := os.Remove(dst); err != nil { | ||
40 | return err | ||
41 | } | ||
42 | } | ||
43 | |||
44 | // Create all the parent directories | ||
45 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
46 | return err | ||
47 | } | ||
48 | |||
49 | return os.Symlink(path, dst) | ||
50 | } | ||
51 | |||
52 | func (g *FileGetter) GetFile(dst string, u *url.URL) error { | ||
53 | path := u.Path | ||
54 | if u.RawPath != "" { | ||
55 | path = u.RawPath | ||
56 | } | ||
57 | |||
58 | // The source path must exist and be a file to be usable. | ||
59 | if fi, err := os.Stat(path); err != nil { | ||
60 | return fmt.Errorf("source path error: %s", err) | ||
61 | } else if fi.IsDir() { | ||
62 | return fmt.Errorf("source path must be a file") | ||
63 | } | ||
64 | |||
65 | _, err := os.Lstat(dst) | ||
66 | if err != nil && !os.IsNotExist(err) { | ||
67 | return err | ||
68 | } | ||
69 | |||
70 | // If the destination already exists, it must be a symlink | ||
71 | if err == nil { | ||
72 | // Remove the destination | ||
73 | if err := os.Remove(dst); err != nil { | ||
74 | return err | ||
75 | } | ||
76 | } | ||
77 | |||
78 | // Create all the parent directories | ||
79 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
80 | return err | ||
81 | } | ||
82 | |||
83 | // If we're not copying, just symlink and we're done | ||
84 | if !g.Copy { | ||
85 | return os.Symlink(path, dst) | ||
86 | } | ||
87 | |||
88 | // Copy | ||
89 | srcF, err := os.Open(path) | ||
90 | if err != nil { | ||
91 | return err | ||
92 | } | ||
93 | defer srcF.Close() | ||
94 | |||
95 | dstF, err := os.Create(dst) | ||
96 | if err != nil { | ||
97 | return err | ||
98 | } | ||
99 | defer dstF.Close() | ||
100 | |||
101 | _, err = io.Copy(dstF, srcF) | ||
102 | return err | ||
103 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/get_file_windows.go b/vendor/github.com/hashicorp/go-getter/get_file_windows.go new file mode 100644 index 0000000..f87ed0a --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_file_windows.go | |||
@@ -0,0 +1,120 @@ | |||
1 | // +build windows | ||
2 | |||
3 | package getter | ||
4 | |||
5 | import ( | ||
6 | "fmt" | ||
7 | "io" | ||
8 | "net/url" | ||
9 | "os" | ||
10 | "os/exec" | ||
11 | "path/filepath" | ||
12 | "strings" | ||
13 | ) | ||
14 | |||
15 | func (g *FileGetter) Get(dst string, u *url.URL) error { | ||
16 | path := u.Path | ||
17 | if u.RawPath != "" { | ||
18 | path = u.RawPath | ||
19 | } | ||
20 | |||
21 | // The source path must exist and be a directory to be usable. | ||
22 | if fi, err := os.Stat(path); err != nil { | ||
23 | return fmt.Errorf("source path error: %s", err) | ||
24 | } else if !fi.IsDir() { | ||
25 | return fmt.Errorf("source path must be a directory") | ||
26 | } | ||
27 | |||
28 | fi, err := os.Lstat(dst) | ||
29 | if err != nil && !os.IsNotExist(err) { | ||
30 | return err | ||
31 | } | ||
32 | |||
33 | // If the destination already exists, it must be a symlink | ||
34 | if err == nil { | ||
35 | mode := fi.Mode() | ||
36 | if mode&os.ModeSymlink == 0 { | ||
37 | return fmt.Errorf("destination exists and is not a symlink") | ||
38 | } | ||
39 | |||
40 | // Remove the destination | ||
41 | if err := os.Remove(dst); err != nil { | ||
42 | return err | ||
43 | } | ||
44 | } | ||
45 | |||
46 | // Create all the parent directories | ||
47 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
48 | return err | ||
49 | } | ||
50 | |||
51 | sourcePath := toBackslash(path) | ||
52 | |||
53 | // Use mklink to create a junction point | ||
54 | output, err := exec.Command("cmd", "/c", "mklink", "/J", dst, sourcePath).CombinedOutput() | ||
55 | if err != nil { | ||
56 | return fmt.Errorf("failed to run mklink %v %v: %v %q", dst, sourcePath, err, output) | ||
57 | } | ||
58 | |||
59 | return nil | ||
60 | } | ||
61 | |||
62 | func (g *FileGetter) GetFile(dst string, u *url.URL) error { | ||
63 | path := u.Path | ||
64 | if u.RawPath != "" { | ||
65 | path = u.RawPath | ||
66 | } | ||
67 | |||
68 | // The source path must exist and be a directory to be usable. | ||
69 | if fi, err := os.Stat(path); err != nil { | ||
70 | return fmt.Errorf("source path error: %s", err) | ||
71 | } else if fi.IsDir() { | ||
72 | return fmt.Errorf("source path must be a file") | ||
73 | } | ||
74 | |||
75 | _, err := os.Lstat(dst) | ||
76 | if err != nil && !os.IsNotExist(err) { | ||
77 | return err | ||
78 | } | ||
79 | |||
80 | // If the destination already exists, it must be a symlink | ||
81 | if err == nil { | ||
82 | // Remove the destination | ||
83 | if err := os.Remove(dst); err != nil { | ||
84 | return err | ||
85 | } | ||
86 | } | ||
87 | |||
88 | // Create all the parent directories | ||
89 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
90 | return err | ||
91 | } | ||
92 | |||
93 | // If we're not copying, just symlink and we're done | ||
94 | if !g.Copy { | ||
95 | return os.Symlink(path, dst) | ||
96 | } | ||
97 | |||
98 | // Copy | ||
99 | srcF, err := os.Open(path) | ||
100 | if err != nil { | ||
101 | return err | ||
102 | } | ||
103 | defer srcF.Close() | ||
104 | |||
105 | dstF, err := os.Create(dst) | ||
106 | if err != nil { | ||
107 | return err | ||
108 | } | ||
109 | defer dstF.Close() | ||
110 | |||
111 | _, err = io.Copy(dstF, srcF) | ||
112 | return err | ||
113 | } | ||
114 | |||
115 | // toBackslash returns the result of replacing each slash character | ||
116 | // in path with a backslash ('\') character. Multiple separators are | ||
117 | // replaced by multiple backslashes. | ||
118 | func toBackslash(path string) string { | ||
119 | return strings.Replace(path, "/", "\\", -1) | ||
120 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/get_git.go b/vendor/github.com/hashicorp/go-getter/get_git.go new file mode 100644 index 0000000..0728139 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_git.go | |||
@@ -0,0 +1,225 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "encoding/base64" | ||
5 | "fmt" | ||
6 | "io/ioutil" | ||
7 | "net/url" | ||
8 | "os" | ||
9 | "os/exec" | ||
10 | "path/filepath" | ||
11 | "strings" | ||
12 | |||
13 | urlhelper "github.com/hashicorp/go-getter/helper/url" | ||
14 | "github.com/hashicorp/go-version" | ||
15 | ) | ||
16 | |||
17 | // GitGetter is a Getter implementation that will download a module from | ||
18 | // a git repository. | ||
19 | type GitGetter struct{} | ||
20 | |||
21 | func (g *GitGetter) ClientMode(_ *url.URL) (ClientMode, error) { | ||
22 | return ClientModeDir, nil | ||
23 | } | ||
24 | |||
25 | func (g *GitGetter) Get(dst string, u *url.URL) error { | ||
26 | if _, err := exec.LookPath("git"); err != nil { | ||
27 | return fmt.Errorf("git must be available and on the PATH") | ||
28 | } | ||
29 | |||
30 | // Extract some query parameters we use | ||
31 | var ref, sshKey string | ||
32 | q := u.Query() | ||
33 | if len(q) > 0 { | ||
34 | ref = q.Get("ref") | ||
35 | q.Del("ref") | ||
36 | |||
37 | sshKey = q.Get("sshkey") | ||
38 | q.Del("sshkey") | ||
39 | |||
40 | // Copy the URL | ||
41 | var newU url.URL = *u | ||
42 | u = &newU | ||
43 | u.RawQuery = q.Encode() | ||
44 | } | ||
45 | |||
46 | var sshKeyFile string | ||
47 | if sshKey != "" { | ||
48 | // Check that the git version is sufficiently new. | ||
49 | if err := checkGitVersion("2.3"); err != nil { | ||
50 | return fmt.Errorf("Error using ssh key: %v", err) | ||
51 | } | ||
52 | |||
53 | // We have an SSH key - decode it. | ||
54 | raw, err := base64.StdEncoding.DecodeString(sshKey) | ||
55 | if err != nil { | ||
56 | return err | ||
57 | } | ||
58 | |||
59 | // Create a temp file for the key and ensure it is removed. | ||
60 | fh, err := ioutil.TempFile("", "go-getter") | ||
61 | if err != nil { | ||
62 | return err | ||
63 | } | ||
64 | sshKeyFile = fh.Name() | ||
65 | defer os.Remove(sshKeyFile) | ||
66 | |||
67 | // Set the permissions prior to writing the key material. | ||
68 | if err := os.Chmod(sshKeyFile, 0600); err != nil { | ||
69 | return err | ||
70 | } | ||
71 | |||
72 | // Write the raw key into the temp file. | ||
73 | _, err = fh.Write(raw) | ||
74 | fh.Close() | ||
75 | if err != nil { | ||
76 | return err | ||
77 | } | ||
78 | } | ||
79 | |||
80 | // Clone or update the repository | ||
81 | _, err := os.Stat(dst) | ||
82 | if err != nil && !os.IsNotExist(err) { | ||
83 | return err | ||
84 | } | ||
85 | if err == nil { | ||
86 | err = g.update(dst, sshKeyFile, ref) | ||
87 | } else { | ||
88 | err = g.clone(dst, sshKeyFile, u) | ||
89 | } | ||
90 | if err != nil { | ||
91 | return err | ||
92 | } | ||
93 | |||
94 | // Next: check out the proper tag/branch if it is specified, and checkout | ||
95 | if ref != "" { | ||
96 | if err := g.checkout(dst, ref); err != nil { | ||
97 | return err | ||
98 | } | ||
99 | } | ||
100 | |||
101 | // Lastly, download any/all submodules. | ||
102 | return g.fetchSubmodules(dst, sshKeyFile) | ||
103 | } | ||
104 | |||
105 | // GetFile for Git doesn't support updating at this time. It will download | ||
106 | // the file every time. | ||
107 | func (g *GitGetter) GetFile(dst string, u *url.URL) error { | ||
108 | td, err := ioutil.TempDir("", "getter-git") | ||
109 | if err != nil { | ||
110 | return err | ||
111 | } | ||
112 | if err := os.RemoveAll(td); err != nil { | ||
113 | return err | ||
114 | } | ||
115 | |||
116 | // Get the filename, and strip the filename from the URL so we can | ||
117 | // just get the repository directly. | ||
118 | filename := filepath.Base(u.Path) | ||
119 | u.Path = filepath.Dir(u.Path) | ||
120 | |||
121 | // Get the full repository | ||
122 | if err := g.Get(td, u); err != nil { | ||
123 | return err | ||
124 | } | ||
125 | |||
126 | // Copy the single file | ||
127 | u, err = urlhelper.Parse(fmtFileURL(filepath.Join(td, filename))) | ||
128 | if err != nil { | ||
129 | return err | ||
130 | } | ||
131 | |||
132 | fg := &FileGetter{Copy: true} | ||
133 | return fg.GetFile(dst, u) | ||
134 | } | ||
135 | |||
136 | func (g *GitGetter) checkout(dst string, ref string) error { | ||
137 | cmd := exec.Command("git", "checkout", ref) | ||
138 | cmd.Dir = dst | ||
139 | return getRunCommand(cmd) | ||
140 | } | ||
141 | |||
142 | func (g *GitGetter) clone(dst, sshKeyFile string, u *url.URL) error { | ||
143 | cmd := exec.Command("git", "clone", u.String(), dst) | ||
144 | setupGitEnv(cmd, sshKeyFile) | ||
145 | return getRunCommand(cmd) | ||
146 | } | ||
147 | |||
148 | func (g *GitGetter) update(dst, sshKeyFile, ref string) error { | ||
149 | // Determine if we're a branch. If we're NOT a branch, then we just | ||
150 | // switch to master prior to checking out | ||
151 | cmd := exec.Command("git", "show-ref", "-q", "--verify", "refs/heads/"+ref) | ||
152 | cmd.Dir = dst | ||
153 | |||
154 | if getRunCommand(cmd) != nil { | ||
155 | // Not a branch, switch to master. This will also catch non-existent | ||
156 | // branches, in which case we want to switch to master and then | ||
157 | // checkout the proper branch later. | ||
158 | ref = "master" | ||
159 | } | ||
160 | |||
161 | // We have to be on a branch to pull | ||
162 | if err := g.checkout(dst, ref); err != nil { | ||
163 | return err | ||
164 | } | ||
165 | |||
166 | cmd = exec.Command("git", "pull", "--ff-only") | ||
167 | cmd.Dir = dst | ||
168 | setupGitEnv(cmd, sshKeyFile) | ||
169 | return getRunCommand(cmd) | ||
170 | } | ||
171 | |||
172 | // fetchSubmodules downloads any configured submodules recursively. | ||
173 | func (g *GitGetter) fetchSubmodules(dst, sshKeyFile string) error { | ||
174 | cmd := exec.Command("git", "submodule", "update", "--init", "--recursive") | ||
175 | cmd.Dir = dst | ||
176 | setupGitEnv(cmd, sshKeyFile) | ||
177 | return getRunCommand(cmd) | ||
178 | } | ||
179 | |||
180 | // setupGitEnv sets up the environment for the given command. This is used to | ||
181 | // pass configuration data to git and ssh and enables advanced cloning methods. | ||
182 | func setupGitEnv(cmd *exec.Cmd, sshKeyFile string) { | ||
183 | var sshOpts []string | ||
184 | |||
185 | if sshKeyFile != "" { | ||
186 | // We have an SSH key temp file configured, tell ssh about this. | ||
187 | sshOpts = append(sshOpts, "-i", sshKeyFile) | ||
188 | } | ||
189 | |||
190 | cmd.Env = append(os.Environ(), | ||
191 | // Set the ssh command to use for clones. | ||
192 | "GIT_SSH_COMMAND=ssh "+strings.Join(sshOpts, " "), | ||
193 | ) | ||
194 | } | ||
195 | |||
196 | // checkGitVersion is used to check the version of git installed on the system | ||
197 | // against a known minimum version. Returns an error if the installed version | ||
198 | // is older than the given minimum. | ||
199 | func checkGitVersion(min string) error { | ||
200 | want, err := version.NewVersion(min) | ||
201 | if err != nil { | ||
202 | return err | ||
203 | } | ||
204 | |||
205 | out, err := exec.Command("git", "version").Output() | ||
206 | if err != nil { | ||
207 | return err | ||
208 | } | ||
209 | |||
210 | fields := strings.Fields(string(out)) | ||
211 | if len(fields) != 3 { | ||
212 | return fmt.Errorf("Unexpected 'git version' output: %q", string(out)) | ||
213 | } | ||
214 | |||
215 | have, err := version.NewVersion(fields[2]) | ||
216 | if err != nil { | ||
217 | return err | ||
218 | } | ||
219 | |||
220 | if have.LessThan(want) { | ||
221 | return fmt.Errorf("Required git version = %s, have %s", want, have) | ||
222 | } | ||
223 | |||
224 | return nil | ||
225 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/get_hg.go b/vendor/github.com/hashicorp/go-getter/get_hg.go new file mode 100644 index 0000000..820bdd4 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_hg.go | |||
@@ -0,0 +1,131 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "io/ioutil" | ||
6 | "net/url" | ||
7 | "os" | ||
8 | "os/exec" | ||
9 | "path/filepath" | ||
10 | "runtime" | ||
11 | |||
12 | urlhelper "github.com/hashicorp/go-getter/helper/url" | ||
13 | ) | ||
14 | |||
15 | // HgGetter is a Getter implementation that will download a module from | ||
16 | // a Mercurial repository. | ||
17 | type HgGetter struct{} | ||
18 | |||
19 | func (g *HgGetter) ClientMode(_ *url.URL) (ClientMode, error) { | ||
20 | return ClientModeDir, nil | ||
21 | } | ||
22 | |||
23 | func (g *HgGetter) Get(dst string, u *url.URL) error { | ||
24 | if _, err := exec.LookPath("hg"); err != nil { | ||
25 | return fmt.Errorf("hg must be available and on the PATH") | ||
26 | } | ||
27 | |||
28 | newURL, err := urlhelper.Parse(u.String()) | ||
29 | if err != nil { | ||
30 | return err | ||
31 | } | ||
32 | if fixWindowsDrivePath(newURL) { | ||
33 | // See valid file path form on http://www.selenic.com/hg/help/urls | ||
34 | newURL.Path = fmt.Sprintf("/%s", newURL.Path) | ||
35 | } | ||
36 | |||
37 | // Extract some query parameters we use | ||
38 | var rev string | ||
39 | q := newURL.Query() | ||
40 | if len(q) > 0 { | ||
41 | rev = q.Get("rev") | ||
42 | q.Del("rev") | ||
43 | |||
44 | newURL.RawQuery = q.Encode() | ||
45 | } | ||
46 | |||
47 | _, err = os.Stat(dst) | ||
48 | if err != nil && !os.IsNotExist(err) { | ||
49 | return err | ||
50 | } | ||
51 | if err != nil { | ||
52 | if err := g.clone(dst, newURL); err != nil { | ||
53 | return err | ||
54 | } | ||
55 | } | ||
56 | |||
57 | if err := g.pull(dst, newURL); err != nil { | ||
58 | return err | ||
59 | } | ||
60 | |||
61 | return g.update(dst, newURL, rev) | ||
62 | } | ||
63 | |||
64 | // GetFile for Hg doesn't support updating at this time. It will download | ||
65 | // the file every time. | ||
66 | func (g *HgGetter) GetFile(dst string, u *url.URL) error { | ||
67 | td, err := ioutil.TempDir("", "getter-hg") | ||
68 | if err != nil { | ||
69 | return err | ||
70 | } | ||
71 | if err := os.RemoveAll(td); err != nil { | ||
72 | return err | ||
73 | } | ||
74 | |||
75 | // Get the filename, and strip the filename from the URL so we can | ||
76 | // just get the repository directly. | ||
77 | filename := filepath.Base(u.Path) | ||
78 | u.Path = filepath.ToSlash(filepath.Dir(u.Path)) | ||
79 | |||
80 | // If we're on Windows, we need to set the host to "localhost" for hg | ||
81 | if runtime.GOOS == "windows" { | ||
82 | u.Host = "localhost" | ||
83 | } | ||
84 | |||
85 | // Get the full repository | ||
86 | if err := g.Get(td, u); err != nil { | ||
87 | return err | ||
88 | } | ||
89 | |||
90 | // Copy the single file | ||
91 | u, err = urlhelper.Parse(fmtFileURL(filepath.Join(td, filename))) | ||
92 | if err != nil { | ||
93 | return err | ||
94 | } | ||
95 | |||
96 | fg := &FileGetter{Copy: true} | ||
97 | return fg.GetFile(dst, u) | ||
98 | } | ||
99 | |||
100 | func (g *HgGetter) clone(dst string, u *url.URL) error { | ||
101 | cmd := exec.Command("hg", "clone", "-U", u.String(), dst) | ||
102 | return getRunCommand(cmd) | ||
103 | } | ||
104 | |||
105 | func (g *HgGetter) pull(dst string, u *url.URL) error { | ||
106 | cmd := exec.Command("hg", "pull") | ||
107 | cmd.Dir = dst | ||
108 | return getRunCommand(cmd) | ||
109 | } | ||
110 | |||
111 | func (g *HgGetter) update(dst string, u *url.URL, rev string) error { | ||
112 | args := []string{"update"} | ||
113 | if rev != "" { | ||
114 | args = append(args, rev) | ||
115 | } | ||
116 | |||
117 | cmd := exec.Command("hg", args...) | ||
118 | cmd.Dir = dst | ||
119 | return getRunCommand(cmd) | ||
120 | } | ||
121 | |||
122 | func fixWindowsDrivePath(u *url.URL) bool { | ||
123 | // hg assumes a file:/// prefix for Windows drive letter file paths. | ||
124 | // (e.g. file:///c:/foo/bar) | ||
125 | // If the URL Path does not begin with a '/' character, the resulting URL | ||
126 | // path will have a file:// prefix. (e.g. file://c:/foo/bar) | ||
127 | // See http://www.selenic.com/hg/help/urls and the examples listed in | ||
128 | // http://selenic.com/repo/hg-stable/file/1265a3a71d75/mercurial/util.py#l1936 | ||
129 | return runtime.GOOS == "windows" && u.Scheme == "file" && | ||
130 | len(u.Path) > 1 && u.Path[0] != '/' && u.Path[1] == ':' | ||
131 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/get_http.go b/vendor/github.com/hashicorp/go-getter/get_http.go new file mode 100644 index 0000000..3c02034 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_http.go | |||
@@ -0,0 +1,219 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "encoding/xml" | ||
5 | "fmt" | ||
6 | "io" | ||
7 | "io/ioutil" | ||
8 | "net/http" | ||
9 | "net/url" | ||
10 | "os" | ||
11 | "path/filepath" | ||
12 | "strings" | ||
13 | ) | ||
14 | |||
15 | // HttpGetter is a Getter implementation that will download from an HTTP | ||
16 | // endpoint. | ||
17 | // | ||
18 | // For file downloads, HTTP is used directly. | ||
19 | // | ||
20 | // The protocol for downloading a directory from an HTTP endpoing is as follows: | ||
21 | // | ||
22 | // An HTTP GET request is made to the URL with the additional GET parameter | ||
23 | // "terraform-get=1". This lets you handle that scenario specially if you | ||
24 | // wish. The response must be a 2xx. | ||
25 | // | ||
26 | // First, a header is looked for "X-Terraform-Get" which should contain | ||
27 | // a source URL to download. | ||
28 | // | ||
29 | // If the header is not present, then a meta tag is searched for named | ||
30 | // "terraform-get" and the content should be a source URL. | ||
31 | // | ||
32 | // The source URL, whether from the header or meta tag, must be a fully | ||
33 | // formed URL. The shorthand syntax of "github.com/foo/bar" or relative | ||
34 | // paths are not allowed. | ||
35 | type HttpGetter struct { | ||
36 | // Netrc, if true, will lookup and use auth information found | ||
37 | // in the user's netrc file if available. | ||
38 | Netrc bool | ||
39 | } | ||
40 | |||
41 | func (g *HttpGetter) ClientMode(u *url.URL) (ClientMode, error) { | ||
42 | if strings.HasSuffix(u.Path, "/") { | ||
43 | return ClientModeDir, nil | ||
44 | } | ||
45 | return ClientModeFile, nil | ||
46 | } | ||
47 | |||
48 | func (g *HttpGetter) Get(dst string, u *url.URL) error { | ||
49 | // Copy the URL so we can modify it | ||
50 | var newU url.URL = *u | ||
51 | u = &newU | ||
52 | |||
53 | if g.Netrc { | ||
54 | // Add auth from netrc if we can | ||
55 | if err := addAuthFromNetrc(u); err != nil { | ||
56 | return err | ||
57 | } | ||
58 | } | ||
59 | |||
60 | // Add terraform-get to the parameter. | ||
61 | q := u.Query() | ||
62 | q.Add("terraform-get", "1") | ||
63 | u.RawQuery = q.Encode() | ||
64 | |||
65 | // Get the URL | ||
66 | resp, err := http.Get(u.String()) | ||
67 | if err != nil { | ||
68 | return err | ||
69 | } | ||
70 | defer resp.Body.Close() | ||
71 | if resp.StatusCode < 200 || resp.StatusCode >= 300 { | ||
72 | return fmt.Errorf("bad response code: %d", resp.StatusCode) | ||
73 | } | ||
74 | |||
75 | // Extract the source URL | ||
76 | var source string | ||
77 | if v := resp.Header.Get("X-Terraform-Get"); v != "" { | ||
78 | source = v | ||
79 | } else { | ||
80 | source, err = g.parseMeta(resp.Body) | ||
81 | if err != nil { | ||
82 | return err | ||
83 | } | ||
84 | } | ||
85 | if source == "" { | ||
86 | return fmt.Errorf("no source URL was returned") | ||
87 | } | ||
88 | |||
89 | // If there is a subdir component, then we download the root separately | ||
90 | // into a temporary directory, then copy over the proper subdir. | ||
91 | source, subDir := SourceDirSubdir(source) | ||
92 | if subDir == "" { | ||
93 | return Get(dst, source) | ||
94 | } | ||
95 | |||
96 | // We have a subdir, time to jump some hoops | ||
97 | return g.getSubdir(dst, source, subDir) | ||
98 | } | ||
99 | |||
100 | func (g *HttpGetter) GetFile(dst string, u *url.URL) error { | ||
101 | resp, err := http.Get(u.String()) | ||
102 | if err != nil { | ||
103 | return err | ||
104 | } | ||
105 | defer resp.Body.Close() | ||
106 | if resp.StatusCode != 200 { | ||
107 | return fmt.Errorf("bad response code: %d", resp.StatusCode) | ||
108 | } | ||
109 | |||
110 | // Create all the parent directories | ||
111 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
112 | return err | ||
113 | } | ||
114 | |||
115 | f, err := os.Create(dst) | ||
116 | if err != nil { | ||
117 | return err | ||
118 | } | ||
119 | defer f.Close() | ||
120 | |||
121 | _, err = io.Copy(f, resp.Body) | ||
122 | return err | ||
123 | } | ||
124 | |||
125 | // getSubdir downloads the source into the destination, but with | ||
126 | // the proper subdir. | ||
127 | func (g *HttpGetter) getSubdir(dst, source, subDir string) error { | ||
128 | // Create a temporary directory to store the full source | ||
129 | td, err := ioutil.TempDir("", "tf") | ||
130 | if err != nil { | ||
131 | return err | ||
132 | } | ||
133 | defer os.RemoveAll(td) | ||
134 | |||
135 | // Download that into the given directory | ||
136 | if err := Get(td, source); err != nil { | ||
137 | return err | ||
138 | } | ||
139 | |||
140 | // Make sure the subdir path actually exists | ||
141 | sourcePath := filepath.Join(td, subDir) | ||
142 | if _, err := os.Stat(sourcePath); err != nil { | ||
143 | return fmt.Errorf( | ||
144 | "Error downloading %s: %s", source, err) | ||
145 | } | ||
146 | |||
147 | // Copy the subdirectory into our actual destination. | ||
148 | if err := os.RemoveAll(dst); err != nil { | ||
149 | return err | ||
150 | } | ||
151 | |||
152 | // Make the final destination | ||
153 | if err := os.MkdirAll(dst, 0755); err != nil { | ||
154 | return err | ||
155 | } | ||
156 | |||
157 | return copyDir(dst, sourcePath, false) | ||
158 | } | ||
159 | |||
160 | // parseMeta looks for the first meta tag in the given reader that | ||
161 | // will give us the source URL. | ||
162 | func (g *HttpGetter) parseMeta(r io.Reader) (string, error) { | ||
163 | d := xml.NewDecoder(r) | ||
164 | d.CharsetReader = charsetReader | ||
165 | d.Strict = false | ||
166 | var err error | ||
167 | var t xml.Token | ||
168 | for { | ||
169 | t, err = d.Token() | ||
170 | if err != nil { | ||
171 | if err == io.EOF { | ||
172 | err = nil | ||
173 | } | ||
174 | return "", err | ||
175 | } | ||
176 | if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") { | ||
177 | return "", nil | ||
178 | } | ||
179 | if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") { | ||
180 | return "", nil | ||
181 | } | ||
182 | e, ok := t.(xml.StartElement) | ||
183 | if !ok || !strings.EqualFold(e.Name.Local, "meta") { | ||
184 | continue | ||
185 | } | ||
186 | if attrValue(e.Attr, "name") != "terraform-get" { | ||
187 | continue | ||
188 | } | ||
189 | if f := attrValue(e.Attr, "content"); f != "" { | ||
190 | return f, nil | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | |||
195 | // attrValue returns the attribute value for the case-insensitive key | ||
196 | // `name', or the empty string if nothing is found. | ||
197 | func attrValue(attrs []xml.Attr, name string) string { | ||
198 | for _, a := range attrs { | ||
199 | if strings.EqualFold(a.Name.Local, name) { | ||
200 | return a.Value | ||
201 | } | ||
202 | } | ||
203 | return "" | ||
204 | } | ||
205 | |||
206 | // charsetReader returns a reader for the given charset. Currently | ||
207 | // it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful | ||
208 | // error which is printed by go get, so the user can find why the package | ||
209 | // wasn't downloaded if the encoding is not supported. Note that, in | ||
210 | // order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters | ||
211 | // greater than 0x7f are not rejected). | ||
212 | func charsetReader(charset string, input io.Reader) (io.Reader, error) { | ||
213 | switch strings.ToLower(charset) { | ||
214 | case "ascii": | ||
215 | return input, nil | ||
216 | default: | ||
217 | return nil, fmt.Errorf("can't decode XML document using charset %q", charset) | ||
218 | } | ||
219 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/get_mock.go b/vendor/github.com/hashicorp/go-getter/get_mock.go new file mode 100644 index 0000000..882e694 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_mock.go | |||
@@ -0,0 +1,52 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "net/url" | ||
5 | ) | ||
6 | |||
7 | // MockGetter is an implementation of Getter that can be used for tests. | ||
8 | type MockGetter struct { | ||
9 | // Proxy, if set, will be called after recording the calls below. | ||
10 | // If it isn't set, then the *Err values will be returned. | ||
11 | Proxy Getter | ||
12 | |||
13 | GetCalled bool | ||
14 | GetDst string | ||
15 | GetURL *url.URL | ||
16 | GetErr error | ||
17 | |||
18 | GetFileCalled bool | ||
19 | GetFileDst string | ||
20 | GetFileURL *url.URL | ||
21 | GetFileErr error | ||
22 | } | ||
23 | |||
24 | func (g *MockGetter) Get(dst string, u *url.URL) error { | ||
25 | g.GetCalled = true | ||
26 | g.GetDst = dst | ||
27 | g.GetURL = u | ||
28 | |||
29 | if g.Proxy != nil { | ||
30 | return g.Proxy.Get(dst, u) | ||
31 | } | ||
32 | |||
33 | return g.GetErr | ||
34 | } | ||
35 | |||
36 | func (g *MockGetter) GetFile(dst string, u *url.URL) error { | ||
37 | g.GetFileCalled = true | ||
38 | g.GetFileDst = dst | ||
39 | g.GetFileURL = u | ||
40 | |||
41 | if g.Proxy != nil { | ||
42 | return g.Proxy.GetFile(dst, u) | ||
43 | } | ||
44 | return g.GetFileErr | ||
45 | } | ||
46 | |||
47 | func (g *MockGetter) ClientMode(u *url.URL) (ClientMode, error) { | ||
48 | if l := len(u.Path); l > 0 && u.Path[l-1:] == "/" { | ||
49 | return ClientModeDir, nil | ||
50 | } | ||
51 | return ClientModeFile, nil | ||
52 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/get_s3.go b/vendor/github.com/hashicorp/go-getter/get_s3.go new file mode 100644 index 0000000..d3bffeb --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/get_s3.go | |||
@@ -0,0 +1,243 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "io" | ||
6 | "net/url" | ||
7 | "os" | ||
8 | "path/filepath" | ||
9 | "strings" | ||
10 | |||
11 | "github.com/aws/aws-sdk-go/aws" | ||
12 | "github.com/aws/aws-sdk-go/aws/credentials" | ||
13 | "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" | ||
14 | "github.com/aws/aws-sdk-go/aws/ec2metadata" | ||
15 | "github.com/aws/aws-sdk-go/aws/session" | ||
16 | "github.com/aws/aws-sdk-go/service/s3" | ||
17 | ) | ||
18 | |||
19 | // S3Getter is a Getter implementation that will download a module from | ||
20 | // a S3 bucket. | ||
21 | type S3Getter struct{} | ||
22 | |||
23 | func (g *S3Getter) ClientMode(u *url.URL) (ClientMode, error) { | ||
24 | // Parse URL | ||
25 | region, bucket, path, _, creds, err := g.parseUrl(u) | ||
26 | if err != nil { | ||
27 | return 0, err | ||
28 | } | ||
29 | |||
30 | // Create client config | ||
31 | config := g.getAWSConfig(region, creds) | ||
32 | sess := session.New(config) | ||
33 | client := s3.New(sess) | ||
34 | |||
35 | // List the object(s) at the given prefix | ||
36 | req := &s3.ListObjectsInput{ | ||
37 | Bucket: aws.String(bucket), | ||
38 | Prefix: aws.String(path), | ||
39 | } | ||
40 | resp, err := client.ListObjects(req) | ||
41 | if err != nil { | ||
42 | return 0, err | ||
43 | } | ||
44 | |||
45 | for _, o := range resp.Contents { | ||
46 | // Use file mode on exact match. | ||
47 | if *o.Key == path { | ||
48 | return ClientModeFile, nil | ||
49 | } | ||
50 | |||
51 | // Use dir mode if child keys are found. | ||
52 | if strings.HasPrefix(*o.Key, path+"/") { | ||
53 | return ClientModeDir, nil | ||
54 | } | ||
55 | } | ||
56 | |||
57 | // There was no match, so just return file mode. The download is going | ||
58 | // to fail but we will let S3 return the proper error later. | ||
59 | return ClientModeFile, nil | ||
60 | } | ||
61 | |||
62 | func (g *S3Getter) Get(dst string, u *url.URL) error { | ||
63 | // Parse URL | ||
64 | region, bucket, path, _, creds, err := g.parseUrl(u) | ||
65 | if err != nil { | ||
66 | return err | ||
67 | } | ||
68 | |||
69 | // Remove destination if it already exists | ||
70 | _, err = os.Stat(dst) | ||
71 | if err != nil && !os.IsNotExist(err) { | ||
72 | return err | ||
73 | } | ||
74 | |||
75 | if err == nil { | ||
76 | // Remove the destination | ||
77 | if err := os.RemoveAll(dst); err != nil { | ||
78 | return err | ||
79 | } | ||
80 | } | ||
81 | |||
82 | // Create all the parent directories | ||
83 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
84 | return err | ||
85 | } | ||
86 | |||
87 | config := g.getAWSConfig(region, creds) | ||
88 | sess := session.New(config) | ||
89 | client := s3.New(sess) | ||
90 | |||
91 | // List files in path, keep listing until no more objects are found | ||
92 | lastMarker := "" | ||
93 | hasMore := true | ||
94 | for hasMore { | ||
95 | req := &s3.ListObjectsInput{ | ||
96 | Bucket: aws.String(bucket), | ||
97 | Prefix: aws.String(path), | ||
98 | } | ||
99 | if lastMarker != "" { | ||
100 | req.Marker = aws.String(lastMarker) | ||
101 | } | ||
102 | |||
103 | resp, err := client.ListObjects(req) | ||
104 | if err != nil { | ||
105 | return err | ||
106 | } | ||
107 | |||
108 | hasMore = aws.BoolValue(resp.IsTruncated) | ||
109 | |||
110 | // Get each object storing each file relative to the destination path | ||
111 | for _, object := range resp.Contents { | ||
112 | lastMarker = aws.StringValue(object.Key) | ||
113 | objPath := aws.StringValue(object.Key) | ||
114 | |||
115 | // If the key ends with a backslash assume it is a directory and ignore | ||
116 | if strings.HasSuffix(objPath, "/") { | ||
117 | continue | ||
118 | } | ||
119 | |||
120 | // Get the object destination path | ||
121 | objDst, err := filepath.Rel(path, objPath) | ||
122 | if err != nil { | ||
123 | return err | ||
124 | } | ||
125 | objDst = filepath.Join(dst, objDst) | ||
126 | |||
127 | if err := g.getObject(client, objDst, bucket, objPath, ""); err != nil { | ||
128 | return err | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | return nil | ||
134 | } | ||
135 | |||
136 | func (g *S3Getter) GetFile(dst string, u *url.URL) error { | ||
137 | region, bucket, path, version, creds, err := g.parseUrl(u) | ||
138 | if err != nil { | ||
139 | return err | ||
140 | } | ||
141 | |||
142 | config := g.getAWSConfig(region, creds) | ||
143 | sess := session.New(config) | ||
144 | client := s3.New(sess) | ||
145 | return g.getObject(client, dst, bucket, path, version) | ||
146 | } | ||
147 | |||
148 | func (g *S3Getter) getObject(client *s3.S3, dst, bucket, key, version string) error { | ||
149 | req := &s3.GetObjectInput{ | ||
150 | Bucket: aws.String(bucket), | ||
151 | Key: aws.String(key), | ||
152 | } | ||
153 | if version != "" { | ||
154 | req.VersionId = aws.String(version) | ||
155 | } | ||
156 | |||
157 | resp, err := client.GetObject(req) | ||
158 | if err != nil { | ||
159 | return err | ||
160 | } | ||
161 | |||
162 | // Create all the parent directories | ||
163 | if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { | ||
164 | return err | ||
165 | } | ||
166 | |||
167 | f, err := os.Create(dst) | ||
168 | if err != nil { | ||
169 | return err | ||
170 | } | ||
171 | defer f.Close() | ||
172 | |||
173 | _, err = io.Copy(f, resp.Body) | ||
174 | return err | ||
175 | } | ||
176 | |||
177 | func (g *S3Getter) getAWSConfig(region string, creds *credentials.Credentials) *aws.Config { | ||
178 | conf := &aws.Config{} | ||
179 | if creds == nil { | ||
180 | // Grab the metadata URL | ||
181 | metadataURL := os.Getenv("AWS_METADATA_URL") | ||
182 | if metadataURL == "" { | ||
183 | metadataURL = "http://169.254.169.254:80/latest" | ||
184 | } | ||
185 | |||
186 | creds = credentials.NewChainCredentials( | ||
187 | []credentials.Provider{ | ||
188 | &credentials.EnvProvider{}, | ||
189 | &credentials.SharedCredentialsProvider{Filename: "", Profile: ""}, | ||
190 | &ec2rolecreds.EC2RoleProvider{ | ||
191 | Client: ec2metadata.New(session.New(&aws.Config{ | ||
192 | Endpoint: aws.String(metadataURL), | ||
193 | })), | ||
194 | }, | ||
195 | }) | ||
196 | } | ||
197 | |||
198 | conf.Credentials = creds | ||
199 | if region != "" { | ||
200 | conf.Region = aws.String(region) | ||
201 | } | ||
202 | |||
203 | return conf | ||
204 | } | ||
205 | |||
206 | func (g *S3Getter) parseUrl(u *url.URL) (region, bucket, path, version string, creds *credentials.Credentials, err error) { | ||
207 | // Expected host style: s3.amazonaws.com. They always have 3 parts, | ||
208 | // although the first may differ if we're accessing a specific region. | ||
209 | hostParts := strings.Split(u.Host, ".") | ||
210 | if len(hostParts) != 3 { | ||
211 | err = fmt.Errorf("URL is not a valid S3 URL") | ||
212 | return | ||
213 | } | ||
214 | |||
215 | // Parse the region out of the first part of the host | ||
216 | region = strings.TrimPrefix(strings.TrimPrefix(hostParts[0], "s3-"), "s3") | ||
217 | if region == "" { | ||
218 | region = "us-east-1" | ||
219 | } | ||
220 | |||
221 | pathParts := strings.SplitN(u.Path, "/", 3) | ||
222 | if len(pathParts) != 3 { | ||
223 | err = fmt.Errorf("URL is not a valid S3 URL") | ||
224 | return | ||
225 | } | ||
226 | |||
227 | bucket = pathParts[1] | ||
228 | path = pathParts[2] | ||
229 | version = u.Query().Get("version") | ||
230 | |||
231 | _, hasAwsId := u.Query()["aws_access_key_id"] | ||
232 | _, hasAwsSecret := u.Query()["aws_access_key_secret"] | ||
233 | _, hasAwsToken := u.Query()["aws_access_token"] | ||
234 | if hasAwsId || hasAwsSecret || hasAwsToken { | ||
235 | creds = credentials.NewStaticCredentials( | ||
236 | u.Query().Get("aws_access_key_id"), | ||
237 | u.Query().Get("aws_access_key_secret"), | ||
238 | u.Query().Get("aws_access_token"), | ||
239 | ) | ||
240 | } | ||
241 | |||
242 | return | ||
243 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/helper/url/url.go b/vendor/github.com/hashicorp/go-getter/helper/url/url.go new file mode 100644 index 0000000..02497c2 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/helper/url/url.go | |||
@@ -0,0 +1,14 @@ | |||
1 | package url | ||
2 | |||
3 | import ( | ||
4 | "net/url" | ||
5 | ) | ||
6 | |||
7 | // Parse parses rawURL into a URL structure. | ||
8 | // The rawURL may be relative or absolute. | ||
9 | // | ||
10 | // Parse is a wrapper for the Go stdlib net/url Parse function, but returns | ||
11 | // Windows "safe" URLs on Windows platforms. | ||
12 | func Parse(rawURL string) (*url.URL, error) { | ||
13 | return parse(rawURL) | ||
14 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/helper/url/url_unix.go b/vendor/github.com/hashicorp/go-getter/helper/url/url_unix.go new file mode 100644 index 0000000..ed1352a --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/helper/url/url_unix.go | |||
@@ -0,0 +1,11 @@ | |||
1 | // +build !windows | ||
2 | |||
3 | package url | ||
4 | |||
5 | import ( | ||
6 | "net/url" | ||
7 | ) | ||
8 | |||
9 | func parse(rawURL string) (*url.URL, error) { | ||
10 | return url.Parse(rawURL) | ||
11 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/helper/url/url_windows.go b/vendor/github.com/hashicorp/go-getter/helper/url/url_windows.go new file mode 100644 index 0000000..4655226 --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/helper/url/url_windows.go | |||
@@ -0,0 +1,40 @@ | |||
1 | package url | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "net/url" | ||
6 | "path/filepath" | ||
7 | "strings" | ||
8 | ) | ||
9 | |||
10 | func parse(rawURL string) (*url.URL, error) { | ||
11 | // Make sure we're using "/" since URLs are "/"-based. | ||
12 | rawURL = filepath.ToSlash(rawURL) | ||
13 | |||
14 | u, err := url.Parse(rawURL) | ||
15 | if err != nil { | ||
16 | return nil, err | ||
17 | } | ||
18 | |||
19 | if len(rawURL) > 1 && rawURL[1] == ':' { | ||
20 | // Assume we're dealing with a drive letter file path where the drive | ||
21 | // letter has been parsed into the URL Scheme, and the rest of the path | ||
22 | // has been parsed into the URL Path without the leading ':' character. | ||
23 | u.Path = fmt.Sprintf("%s:%s", string(rawURL[0]), u.Path) | ||
24 | u.Scheme = "" | ||
25 | } | ||
26 | |||
27 | if len(u.Host) > 1 && u.Host[1] == ':' && strings.HasPrefix(rawURL, "file://") { | ||
28 | // Assume we're dealing with a drive letter file path where the drive | ||
29 | // letter has been parsed into the URL Host. | ||
30 | u.Path = fmt.Sprintf("%s%s", u.Host, u.Path) | ||
31 | u.Host = "" | ||
32 | } | ||
33 | |||
34 | // Remove leading slash for absolute file paths. | ||
35 | if len(u.Path) > 2 && u.Path[0] == '/' && u.Path[2] == ':' { | ||
36 | u.Path = u.Path[1:] | ||
37 | } | ||
38 | |||
39 | return u, err | ||
40 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/netrc.go b/vendor/github.com/hashicorp/go-getter/netrc.go new file mode 100644 index 0000000..c7f6a3f --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/netrc.go | |||
@@ -0,0 +1,67 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "net/url" | ||
6 | "os" | ||
7 | "runtime" | ||
8 | |||
9 | "github.com/bgentry/go-netrc/netrc" | ||
10 | "github.com/mitchellh/go-homedir" | ||
11 | ) | ||
12 | |||
13 | // addAuthFromNetrc adds auth information to the URL from the user's | ||
14 | // netrc file if it can be found. This will only add the auth info | ||
15 | // if the URL doesn't already have auth info specified and the | ||
16 | // the username is blank. | ||
17 | func addAuthFromNetrc(u *url.URL) error { | ||
18 | // If the URL already has auth information, do nothing | ||
19 | if u.User != nil && u.User.Username() != "" { | ||
20 | return nil | ||
21 | } | ||
22 | |||
23 | // Get the netrc file path | ||
24 | path := os.Getenv("NETRC") | ||
25 | if path == "" { | ||
26 | filename := ".netrc" | ||
27 | if runtime.GOOS == "windows" { | ||
28 | filename = "_netrc" | ||
29 | } | ||
30 | |||
31 | var err error | ||
32 | path, err = homedir.Expand("~/" + filename) | ||
33 | if err != nil { | ||
34 | return err | ||
35 | } | ||
36 | } | ||
37 | |||
38 | // If the file is not a file, then do nothing | ||
39 | if fi, err := os.Stat(path); err != nil { | ||
40 | // File doesn't exist, do nothing | ||
41 | if os.IsNotExist(err) { | ||
42 | return nil | ||
43 | } | ||
44 | |||
45 | // Some other error! | ||
46 | return err | ||
47 | } else if fi.IsDir() { | ||
48 | // File is directory, ignore | ||
49 | return nil | ||
50 | } | ||
51 | |||
52 | // Load up the netrc file | ||
53 | net, err := netrc.ParseFile(path) | ||
54 | if err != nil { | ||
55 | return fmt.Errorf("Error parsing netrc file at %q: %s", path, err) | ||
56 | } | ||
57 | |||
58 | machine := net.FindMachine(u.Host) | ||
59 | if machine == nil { | ||
60 | // Machine not found, no problem | ||
61 | return nil | ||
62 | } | ||
63 | |||
64 | // Set the user info | ||
65 | u.User = url.UserPassword(machine.Login, machine.Password) | ||
66 | return nil | ||
67 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/source.go b/vendor/github.com/hashicorp/go-getter/source.go new file mode 100644 index 0000000..4d5ee3c --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/source.go | |||
@@ -0,0 +1,36 @@ | |||
1 | package getter | ||
2 | |||
3 | import ( | ||
4 | "strings" | ||
5 | ) | ||
6 | |||
7 | // SourceDirSubdir takes a source and returns a tuple of the URL without | ||
8 | // the subdir and the URL with the subdir. | ||
9 | func SourceDirSubdir(src string) (string, string) { | ||
10 | // Calcaulate an offset to avoid accidentally marking the scheme | ||
11 | // as the dir. | ||
12 | var offset int | ||
13 | if idx := strings.Index(src, "://"); idx > -1 { | ||
14 | offset = idx + 3 | ||
15 | } | ||
16 | |||
17 | // First see if we even have an explicit subdir | ||
18 | idx := strings.Index(src[offset:], "//") | ||
19 | if idx == -1 { | ||
20 | return src, "" | ||
21 | } | ||
22 | |||
23 | idx += offset | ||
24 | subdir := src[idx+2:] | ||
25 | src = src[:idx] | ||
26 | |||
27 | // Next, check if we have query parameters and push them onto the | ||
28 | // URL. | ||
29 | if idx = strings.Index(subdir, "?"); idx > -1 { | ||
30 | query := subdir[idx:] | ||
31 | subdir = subdir[:idx] | ||
32 | src += query | ||
33 | } | ||
34 | |||
35 | return src, subdir | ||
36 | } | ||
diff --git a/vendor/github.com/hashicorp/go-getter/storage.go b/vendor/github.com/hashicorp/go-getter/storage.go new file mode 100644 index 0000000..2bc6b9e --- /dev/null +++ b/vendor/github.com/hashicorp/go-getter/storage.go | |||
@@ -0,0 +1,13 @@ | |||
1 | package getter | ||
2 | |||
3 | // Storage is an interface that knows how to lookup downloaded directories | ||
4 | // as well as download and update directories from their sources into the | ||
5 | // proper location. | ||
6 | type Storage interface { | ||
7 | // Dir returns the directory on local disk where the directory source | ||
8 | // can be loaded from. | ||
9 | Dir(string) (string, bool, error) | ||
10 | |||
11 | // Get will download and optionally update the given directory. | ||
12 | Get(string, string, bool) error | ||
13 | } | ||