diff options
Diffstat (limited to 'vendor/github.com/hashicorp/go-plugin')
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/LICENSE | 353 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/README.md | 161 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/client.go | 581 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/discover.go | 28 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/error.go | 24 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/mux_broker.go | 204 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/plugin.go | 25 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/process.go | 24 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/process_posix.go | 19 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/process_windows.go | 29 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/rpc_client.go | 123 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/rpc_server.go | 185 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/server.go | 222 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/server_mux.go | 31 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/stream.go | 18 | ||||
-rw-r--r-- | vendor/github.com/hashicorp/go-plugin/testing.go | 76 |
16 files changed, 2103 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/go-plugin/LICENSE b/vendor/github.com/hashicorp/go-plugin/LICENSE new file mode 100644 index 0000000..82b4de9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/LICENSE | |||
@@ -0,0 +1,353 @@ | |||
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. | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/README.md b/vendor/github.com/hashicorp/go-plugin/README.md new file mode 100644 index 0000000..2058cfb --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/README.md | |||
@@ -0,0 +1,161 @@ | |||
1 | # Go Plugin System over RPC | ||
2 | |||
3 | `go-plugin` is a Go (golang) plugin system over RPC. It is the plugin system | ||
4 | that has been in use by HashiCorp tooling for over 3 years. While initially | ||
5 | created for [Packer](https://www.packer.io), it has since been used by | ||
6 | [Terraform](https://www.terraform.io) and [Otto](https://www.ottoproject.io), | ||
7 | with plans to also use it for [Nomad](https://www.nomadproject.io) and | ||
8 | [Vault](https://www.vaultproject.io). | ||
9 | |||
10 | While the plugin system is over RPC, it is currently only designed to work | ||
11 | over a local [reliable] network. Plugins over a real network are not supported | ||
12 | and will lead to unexpected behavior. | ||
13 | |||
14 | This plugin system has been used on millions of machines across many different | ||
15 | projects and has proven to be battle hardened and ready for production use. | ||
16 | |||
17 | ## Features | ||
18 | |||
19 | The HashiCorp plugin system supports a number of features: | ||
20 | |||
21 | **Plugins are Go interface implementations.** This makes writing and consuming | ||
22 | plugins feel very natural. To a plugin author: you just implement an | ||
23 | interface as if it were going to run in the same process. For a plugin user: | ||
24 | you just use and call functions on an interface as if it were in the same | ||
25 | process. This plugin system handles the communication in between. | ||
26 | |||
27 | **Complex arguments and return values are supported.** This library | ||
28 | provides APIs for handling complex arguments and return values such | ||
29 | as interfaces, `io.Reader/Writer`, etc. We do this by giving you a library | ||
30 | (`MuxBroker`) for creating new connections between the client/server to | ||
31 | serve additional interfaces or transfer raw data. | ||
32 | |||
33 | **Bidirectional communication.** Because the plugin system supports | ||
34 | complex arguments, the host process can send it interface implementations | ||
35 | and the plugin can call back into the host process. | ||
36 | |||
37 | **Built-in Logging.** Any plugins that use the `log` standard library | ||
38 | will have log data automatically sent to the host process. The host | ||
39 | process will mirror this output prefixed with the path to the plugin | ||
40 | binary. This makes debugging with plugins simple. | ||
41 | |||
42 | **Protocol Versioning.** A very basic "protocol version" is supported that | ||
43 | can be incremented to invalidate any previous plugins. This is useful when | ||
44 | interface signatures are changing, protocol level changes are necessary, | ||
45 | etc. When a protocol version is incompatible, a human friendly error | ||
46 | message is shown to the end user. | ||
47 | |||
48 | **Stdout/Stderr Syncing.** While plugins are subprocesses, they can continue | ||
49 | to use stdout/stderr as usual and the output will get mirrored back to | ||
50 | the host process. The host process can control what `io.Writer` these | ||
51 | streams go to to prevent this from happening. | ||
52 | |||
53 | **TTY Preservation.** Plugin subprocesses are connected to the identical | ||
54 | stdin file descriptor as the host process, allowing software that requires | ||
55 | a TTY to work. For example, a plugin can execute `ssh` and even though there | ||
56 | are multiple subprocesses and RPC happening, it will look and act perfectly | ||
57 | to the end user. | ||
58 | |||
59 | **Host upgrade while a plugin is running.** Plugins can be "reattached" | ||
60 | so that the host process can be upgraded while the plugin is still running. | ||
61 | This requires the host/plugin to know this is possible and daemonize | ||
62 | properly. `NewClient` takes a `ReattachConfig` to determine if and how to | ||
63 | reattach. | ||
64 | |||
65 | ## Architecture | ||
66 | |||
67 | The HashiCorp plugin system works by launching subprocesses and communicating | ||
68 | over RPC (using standard `net/rpc`). A single connection is made between | ||
69 | any plugin and the host process, and we use a | ||
70 | [connection multiplexing](https://github.com/hashicorp/yamux) | ||
71 | library to multiplex any other connections on top. | ||
72 | |||
73 | This architecture has a number of benefits: | ||
74 | |||
75 | * Plugins can't crash your host process: A panic in a plugin doesn't | ||
76 | panic the plugin user. | ||
77 | |||
78 | * Plugins are very easy to write: just write a Go application and `go build`. | ||
79 | Theoretically you could also use another language as long as it can | ||
80 | communicate the Go `net/rpc` protocol but this hasn't yet been tried. | ||
81 | |||
82 | * Plugins are very easy to install: just put the binary in a location where | ||
83 | the host will find it (depends on the host but this library also provides | ||
84 | helpers), and the plugin host handles the rest. | ||
85 | |||
86 | * Plugins can be relatively secure: The plugin only has access to the | ||
87 | interfaces and args given to it, not to the entire memory space of the | ||
88 | process. More security features are planned (see the coming soon section | ||
89 | below). | ||
90 | |||
91 | ## Usage | ||
92 | |||
93 | To use the plugin system, you must take the following steps. These are | ||
94 | high-level steps that must be done. Examples are available in the | ||
95 | `examples/` directory. | ||
96 | |||
97 | 1. Choose the interface(s) you want to expose for plugins. | ||
98 | |||
99 | 2. For each interface, implement an implementation of that interface | ||
100 | that communicates over an `*rpc.Client` (from the standard `net/rpc` | ||
101 | package) for every function call. Likewise, implement the RPC server | ||
102 | struct this communicates to which is then communicating to a real, | ||
103 | concrete implementation. | ||
104 | |||
105 | 3. Create a `Plugin` implementation that knows how to create the RPC | ||
106 | client/server for a given plugin type. | ||
107 | |||
108 | 4. Plugin authors call `plugin.Serve` to serve a plugin from the | ||
109 | `main` function. | ||
110 | |||
111 | 5. Plugin users use `plugin.Client` to launch a subprocess and request | ||
112 | an interface implementation over RPC. | ||
113 | |||
114 | That's it! In practice, step 2 is the most tedious and time consuming step. | ||
115 | Even so, it isn't very difficult and you can see examples in the `examples/` | ||
116 | directory as well as throughout our various open source projects. | ||
117 | |||
118 | For complete API documentation, see [GoDoc](https://godoc.org/github.com/hashicorp/go-plugin). | ||
119 | |||
120 | ## Roadmap | ||
121 | |||
122 | Our plugin system is constantly evolving. As we use the plugin system for | ||
123 | new projects or for new features in existing projects, we constantly find | ||
124 | improvements we can make. | ||
125 | |||
126 | At this point in time, the roadmap for the plugin system is: | ||
127 | |||
128 | **Cryptographically Secure Plugins.** We'll implement signing plugins | ||
129 | and loading signed plugins in order to allow Vault to make use of multi-process | ||
130 | in a secure way. | ||
131 | |||
132 | **Semantic Versioning.** Plugins will be able to implement a semantic version. | ||
133 | This plugin system will give host processes a system for constraining | ||
134 | versions. This is in addition to the protocol versioning already present | ||
135 | which is more for larger underlying changes. | ||
136 | |||
137 | **Plugin fetching.** We will integrate with [go-getter](https://github.com/hashicorp/go-getter) | ||
138 | to support automatic download + install of plugins. Paired with cryptographically | ||
139 | secure plugins (above), we can make this a safe operation for an amazing | ||
140 | user experience. | ||
141 | |||
142 | ## What About Shared Libraries? | ||
143 | |||
144 | When we started using plugins (late 2012, early 2013), plugins over RPC | ||
145 | were the only option since Go didn't support dynamic library loading. Today, | ||
146 | Go still doesn't support dynamic library loading, but they do intend to. | ||
147 | Since 2012, our plugin system has stabilized from millions of users using it, | ||
148 | and has many benefits we've come to value greatly. | ||
149 | |||
150 | For example, we intend to use this plugin system in | ||
151 | [Vault](https://www.vaultproject.io), and dynamic library loading will | ||
152 | simply never be acceptable in Vault for security reasons. That is an extreme | ||
153 | example, but we believe our library system has more upsides than downsides | ||
154 | over dynamic library loading and since we've had it built and tested for years, | ||
155 | we'll likely continue to use it. | ||
156 | |||
157 | Shared libraries have one major advantage over our system which is much | ||
158 | higher performance. In real world scenarios across our various tools, | ||
159 | we've never required any more performance out of our plugin system and it | ||
160 | has seen very high throughput, so this isn't a concern for us at the moment. | ||
161 | |||
diff --git a/vendor/github.com/hashicorp/go-plugin/client.go b/vendor/github.com/hashicorp/go-plugin/client.go new file mode 100644 index 0000000..9f8a0f2 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/client.go | |||
@@ -0,0 +1,581 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "bufio" | ||
5 | "errors" | ||
6 | "fmt" | ||
7 | "io" | ||
8 | "io/ioutil" | ||
9 | "log" | ||
10 | "net" | ||
11 | "os" | ||
12 | "os/exec" | ||
13 | "path/filepath" | ||
14 | "strconv" | ||
15 | "strings" | ||
16 | "sync" | ||
17 | "sync/atomic" | ||
18 | "time" | ||
19 | "unicode" | ||
20 | ) | ||
21 | |||
22 | // If this is 1, then we've called CleanupClients. This can be used | ||
23 | // by plugin RPC implementations to change error behavior since you | ||
24 | // can expected network connection errors at this point. This should be | ||
25 | // read by using sync/atomic. | ||
26 | var Killed uint32 = 0 | ||
27 | |||
28 | // This is a slice of the "managed" clients which are cleaned up when | ||
29 | // calling Cleanup | ||
30 | var managedClients = make([]*Client, 0, 5) | ||
31 | var managedClientsLock sync.Mutex | ||
32 | |||
33 | // Error types | ||
34 | var ( | ||
35 | // ErrProcessNotFound is returned when a client is instantiated to | ||
36 | // reattach to an existing process and it isn't found. | ||
37 | ErrProcessNotFound = errors.New("Reattachment process not found") | ||
38 | ) | ||
39 | |||
40 | // Client handles the lifecycle of a plugin application. It launches | ||
41 | // plugins, connects to them, dispenses interface implementations, and handles | ||
42 | // killing the process. | ||
43 | // | ||
44 | // Plugin hosts should use one Client for each plugin executable. To | ||
45 | // dispense a plugin type, use the `Client.Client` function, and then | ||
46 | // cal `Dispense`. This awkward API is mostly historical but is used to split | ||
47 | // the client that deals with subprocess management and the client that | ||
48 | // does RPC management. | ||
49 | // | ||
50 | // See NewClient and ClientConfig for using a Client. | ||
51 | type Client struct { | ||
52 | config *ClientConfig | ||
53 | exited bool | ||
54 | doneLogging chan struct{} | ||
55 | l sync.Mutex | ||
56 | address net.Addr | ||
57 | process *os.Process | ||
58 | client *RPCClient | ||
59 | } | ||
60 | |||
61 | // ClientConfig is the configuration used to initialize a new | ||
62 | // plugin client. After being used to initialize a plugin client, | ||
63 | // that configuration must not be modified again. | ||
64 | type ClientConfig struct { | ||
65 | // HandshakeConfig is the configuration that must match servers. | ||
66 | HandshakeConfig | ||
67 | |||
68 | // Plugins are the plugins that can be consumed. | ||
69 | Plugins map[string]Plugin | ||
70 | |||
71 | // One of the following must be set, but not both. | ||
72 | // | ||
73 | // Cmd is the unstarted subprocess for starting the plugin. If this is | ||
74 | // set, then the Client starts the plugin process on its own and connects | ||
75 | // to it. | ||
76 | // | ||
77 | // Reattach is configuration for reattaching to an existing plugin process | ||
78 | // that is already running. This isn't common. | ||
79 | Cmd *exec.Cmd | ||
80 | Reattach *ReattachConfig | ||
81 | |||
82 | // Managed represents if the client should be managed by the | ||
83 | // plugin package or not. If true, then by calling CleanupClients, | ||
84 | // it will automatically be cleaned up. Otherwise, the client | ||
85 | // user is fully responsible for making sure to Kill all plugin | ||
86 | // clients. By default the client is _not_ managed. | ||
87 | Managed bool | ||
88 | |||
89 | // The minimum and maximum port to use for communicating with | ||
90 | // the subprocess. If not set, this defaults to 10,000 and 25,000 | ||
91 | // respectively. | ||
92 | MinPort, MaxPort uint | ||
93 | |||
94 | // StartTimeout is the timeout to wait for the plugin to say it | ||
95 | // has started successfully. | ||
96 | StartTimeout time.Duration | ||
97 | |||
98 | // If non-nil, then the stderr of the client will be written to here | ||
99 | // (as well as the log). This is the original os.Stderr of the subprocess. | ||
100 | // This isn't the output of synced stderr. | ||
101 | Stderr io.Writer | ||
102 | |||
103 | // SyncStdout, SyncStderr can be set to override the | ||
104 | // respective os.Std* values in the plugin. Care should be taken to | ||
105 | // avoid races here. If these are nil, then this will automatically be | ||
106 | // hooked up to os.Stdin, Stdout, and Stderr, respectively. | ||
107 | // | ||
108 | // If the default values (nil) are used, then this package will not | ||
109 | // sync any of these streams. | ||
110 | SyncStdout io.Writer | ||
111 | SyncStderr io.Writer | ||
112 | } | ||
113 | |||
114 | // ReattachConfig is used to configure a client to reattach to an | ||
115 | // already-running plugin process. You can retrieve this information by | ||
116 | // calling ReattachConfig on Client. | ||
117 | type ReattachConfig struct { | ||
118 | Addr net.Addr | ||
119 | Pid int | ||
120 | } | ||
121 | |||
122 | // This makes sure all the managed subprocesses are killed and properly | ||
123 | // logged. This should be called before the parent process running the | ||
124 | // plugins exits. | ||
125 | // | ||
126 | // This must only be called _once_. | ||
127 | func CleanupClients() { | ||
128 | // Set the killed to true so that we don't get unexpected panics | ||
129 | atomic.StoreUint32(&Killed, 1) | ||
130 | |||
131 | // Kill all the managed clients in parallel and use a WaitGroup | ||
132 | // to wait for them all to finish up. | ||
133 | var wg sync.WaitGroup | ||
134 | managedClientsLock.Lock() | ||
135 | for _, client := range managedClients { | ||
136 | wg.Add(1) | ||
137 | |||
138 | go func(client *Client) { | ||
139 | client.Kill() | ||
140 | wg.Done() | ||
141 | }(client) | ||
142 | } | ||
143 | managedClientsLock.Unlock() | ||
144 | |||
145 | log.Println("[DEBUG] plugin: waiting for all plugin processes to complete...") | ||
146 | wg.Wait() | ||
147 | } | ||
148 | |||
149 | // Creates a new plugin client which manages the lifecycle of an external | ||
150 | // plugin and gets the address for the RPC connection. | ||
151 | // | ||
152 | // The client must be cleaned up at some point by calling Kill(). If | ||
153 | // the client is a managed client (created with NewManagedClient) you | ||
154 | // can just call CleanupClients at the end of your program and they will | ||
155 | // be properly cleaned. | ||
156 | func NewClient(config *ClientConfig) (c *Client) { | ||
157 | if config.MinPort == 0 && config.MaxPort == 0 { | ||
158 | config.MinPort = 10000 | ||
159 | config.MaxPort = 25000 | ||
160 | } | ||
161 | |||
162 | if config.StartTimeout == 0 { | ||
163 | config.StartTimeout = 1 * time.Minute | ||
164 | } | ||
165 | |||
166 | if config.Stderr == nil { | ||
167 | config.Stderr = ioutil.Discard | ||
168 | } | ||
169 | |||
170 | if config.SyncStdout == nil { | ||
171 | config.SyncStdout = ioutil.Discard | ||
172 | } | ||
173 | if config.SyncStderr == nil { | ||
174 | config.SyncStderr = ioutil.Discard | ||
175 | } | ||
176 | |||
177 | c = &Client{config: config} | ||
178 | if config.Managed { | ||
179 | managedClientsLock.Lock() | ||
180 | managedClients = append(managedClients, c) | ||
181 | managedClientsLock.Unlock() | ||
182 | } | ||
183 | |||
184 | return | ||
185 | } | ||
186 | |||
187 | // Client returns an RPC client for the plugin. | ||
188 | // | ||
189 | // Subsequent calls to this will return the same RPC client. | ||
190 | func (c *Client) Client() (*RPCClient, error) { | ||
191 | addr, err := c.Start() | ||
192 | if err != nil { | ||
193 | return nil, err | ||
194 | } | ||
195 | |||
196 | c.l.Lock() | ||
197 | defer c.l.Unlock() | ||
198 | |||
199 | if c.client != nil { | ||
200 | return c.client, nil | ||
201 | } | ||
202 | |||
203 | // Connect to the client | ||
204 | conn, err := net.Dial(addr.Network(), addr.String()) | ||
205 | if err != nil { | ||
206 | return nil, err | ||
207 | } | ||
208 | if tcpConn, ok := conn.(*net.TCPConn); ok { | ||
209 | // Make sure to set keep alive so that the connection doesn't die | ||
210 | tcpConn.SetKeepAlive(true) | ||
211 | } | ||
212 | |||
213 | // Create the actual RPC client | ||
214 | c.client, err = NewRPCClient(conn, c.config.Plugins) | ||
215 | if err != nil { | ||
216 | conn.Close() | ||
217 | return nil, err | ||
218 | } | ||
219 | |||
220 | // Begin the stream syncing so that stdin, out, err work properly | ||
221 | err = c.client.SyncStreams( | ||
222 | c.config.SyncStdout, | ||
223 | c.config.SyncStderr) | ||
224 | if err != nil { | ||
225 | c.client.Close() | ||
226 | c.client = nil | ||
227 | return nil, err | ||
228 | } | ||
229 | |||
230 | return c.client, nil | ||
231 | } | ||
232 | |||
233 | // Tells whether or not the underlying process has exited. | ||
234 | func (c *Client) Exited() bool { | ||
235 | c.l.Lock() | ||
236 | defer c.l.Unlock() | ||
237 | return c.exited | ||
238 | } | ||
239 | |||
240 | // End the executing subprocess (if it is running) and perform any cleanup | ||
241 | // tasks necessary such as capturing any remaining logs and so on. | ||
242 | // | ||
243 | // This method blocks until the process successfully exits. | ||
244 | // | ||
245 | // This method can safely be called multiple times. | ||
246 | func (c *Client) Kill() { | ||
247 | // Grab a lock to read some private fields. | ||
248 | c.l.Lock() | ||
249 | process := c.process | ||
250 | addr := c.address | ||
251 | doneCh := c.doneLogging | ||
252 | c.l.Unlock() | ||
253 | |||
254 | // If there is no process, we never started anything. Nothing to kill. | ||
255 | if process == nil { | ||
256 | return | ||
257 | } | ||
258 | |||
259 | // We need to check for address here. It is possible that the plugin | ||
260 | // started (process != nil) but has no address (addr == nil) if the | ||
261 | // plugin failed at startup. If we do have an address, we need to close | ||
262 | // the plugin net connections. | ||
263 | graceful := false | ||
264 | if addr != nil { | ||
265 | // Close the client to cleanly exit the process. | ||
266 | client, err := c.Client() | ||
267 | if err == nil { | ||
268 | err = client.Close() | ||
269 | |||
270 | // If there is no error, then we attempt to wait for a graceful | ||
271 | // exit. If there was an error, we assume that graceful cleanup | ||
272 | // won't happen and just force kill. | ||
273 | graceful = err == nil | ||
274 | if err != nil { | ||
275 | // If there was an error just log it. We're going to force | ||
276 | // kill in a moment anyways. | ||
277 | log.Printf( | ||
278 | "[WARN] plugin: error closing client during Kill: %s", err) | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | |||
283 | // If we're attempting a graceful exit, then we wait for a short period | ||
284 | // of time to allow that to happen. To wait for this we just wait on the | ||
285 | // doneCh which would be closed if the process exits. | ||
286 | if graceful { | ||
287 | select { | ||
288 | case <-doneCh: | ||
289 | return | ||
290 | case <-time.After(250 * time.Millisecond): | ||
291 | } | ||
292 | } | ||
293 | |||
294 | // If graceful exiting failed, just kill it | ||
295 | process.Kill() | ||
296 | |||
297 | // Wait for the client to finish logging so we have a complete log | ||
298 | <-doneCh | ||
299 | } | ||
300 | |||
301 | // Starts the underlying subprocess, communicating with it to negotiate | ||
302 | // a port for RPC connections, and returning the address to connect via RPC. | ||
303 | // | ||
304 | // This method is safe to call multiple times. Subsequent calls have no effect. | ||
305 | // Once a client has been started once, it cannot be started again, even if | ||
306 | // it was killed. | ||
307 | func (c *Client) Start() (addr net.Addr, err error) { | ||
308 | c.l.Lock() | ||
309 | defer c.l.Unlock() | ||
310 | |||
311 | if c.address != nil { | ||
312 | return c.address, nil | ||
313 | } | ||
314 | |||
315 | // If one of cmd or reattach isn't set, then it is an error. We wrap | ||
316 | // this in a {} for scoping reasons, and hopeful that the escape | ||
317 | // analysis will pop the stock here. | ||
318 | { | ||
319 | cmdSet := c.config.Cmd != nil | ||
320 | attachSet := c.config.Reattach != nil | ||
321 | if cmdSet == attachSet { | ||
322 | return nil, fmt.Errorf("Only one of Cmd or Reattach must be set") | ||
323 | } | ||
324 | } | ||
325 | |||
326 | // Create the logging channel for when we kill | ||
327 | c.doneLogging = make(chan struct{}) | ||
328 | |||
329 | if c.config.Reattach != nil { | ||
330 | // Verify the process still exists. If not, then it is an error | ||
331 | p, err := os.FindProcess(c.config.Reattach.Pid) | ||
332 | if err != nil { | ||
333 | return nil, err | ||
334 | } | ||
335 | |||
336 | // Attempt to connect to the addr since on Unix systems FindProcess | ||
337 | // doesn't actually return an error if it can't find the process. | ||
338 | conn, err := net.Dial( | ||
339 | c.config.Reattach.Addr.Network(), | ||
340 | c.config.Reattach.Addr.String()) | ||
341 | if err != nil { | ||
342 | p.Kill() | ||
343 | return nil, ErrProcessNotFound | ||
344 | } | ||
345 | conn.Close() | ||
346 | |||
347 | // Goroutine to mark exit status | ||
348 | go func(pid int) { | ||
349 | // Wait for the process to die | ||
350 | pidWait(pid) | ||
351 | |||
352 | // Log so we can see it | ||
353 | log.Printf("[DEBUG] plugin: reattached plugin process exited\n") | ||
354 | |||
355 | // Mark it | ||
356 | c.l.Lock() | ||
357 | defer c.l.Unlock() | ||
358 | c.exited = true | ||
359 | |||
360 | // Close the logging channel since that doesn't work on reattach | ||
361 | close(c.doneLogging) | ||
362 | }(p.Pid) | ||
363 | |||
364 | // Set the address and process | ||
365 | c.address = c.config.Reattach.Addr | ||
366 | c.process = p | ||
367 | |||
368 | return c.address, nil | ||
369 | } | ||
370 | |||
371 | env := []string{ | ||
372 | fmt.Sprintf("%s=%s", c.config.MagicCookieKey, c.config.MagicCookieValue), | ||
373 | fmt.Sprintf("PLUGIN_MIN_PORT=%d", c.config.MinPort), | ||
374 | fmt.Sprintf("PLUGIN_MAX_PORT=%d", c.config.MaxPort), | ||
375 | } | ||
376 | |||
377 | stdout_r, stdout_w := io.Pipe() | ||
378 | stderr_r, stderr_w := io.Pipe() | ||
379 | |||
380 | cmd := c.config.Cmd | ||
381 | cmd.Env = append(cmd.Env, os.Environ()...) | ||
382 | cmd.Env = append(cmd.Env, env...) | ||
383 | cmd.Stdin = os.Stdin | ||
384 | cmd.Stderr = stderr_w | ||
385 | cmd.Stdout = stdout_w | ||
386 | |||
387 | log.Printf("[DEBUG] plugin: starting plugin: %s %#v", cmd.Path, cmd.Args) | ||
388 | err = cmd.Start() | ||
389 | if err != nil { | ||
390 | return | ||
391 | } | ||
392 | |||
393 | // Set the process | ||
394 | c.process = cmd.Process | ||
395 | |||
396 | // Make sure the command is properly cleaned up if there is an error | ||
397 | defer func() { | ||
398 | r := recover() | ||
399 | |||
400 | if err != nil || r != nil { | ||
401 | cmd.Process.Kill() | ||
402 | } | ||
403 | |||
404 | if r != nil { | ||
405 | panic(r) | ||
406 | } | ||
407 | }() | ||
408 | |||
409 | // Start goroutine to wait for process to exit | ||
410 | exitCh := make(chan struct{}) | ||
411 | go func() { | ||
412 | // Make sure we close the write end of our stderr/stdout so | ||
413 | // that the readers send EOF properly. | ||
414 | defer stderr_w.Close() | ||
415 | defer stdout_w.Close() | ||
416 | |||
417 | // Wait for the command to end. | ||
418 | cmd.Wait() | ||
419 | |||
420 | // Log and make sure to flush the logs write away | ||
421 | log.Printf("[DEBUG] plugin: %s: plugin process exited\n", cmd.Path) | ||
422 | os.Stderr.Sync() | ||
423 | |||
424 | // Mark that we exited | ||
425 | close(exitCh) | ||
426 | |||
427 | // Set that we exited, which takes a lock | ||
428 | c.l.Lock() | ||
429 | defer c.l.Unlock() | ||
430 | c.exited = true | ||
431 | }() | ||
432 | |||
433 | // Start goroutine that logs the stderr | ||
434 | go c.logStderr(stderr_r) | ||
435 | |||
436 | // Start a goroutine that is going to be reading the lines | ||
437 | // out of stdout | ||
438 | linesCh := make(chan []byte) | ||
439 | go func() { | ||
440 | defer close(linesCh) | ||
441 | |||
442 | buf := bufio.NewReader(stdout_r) | ||
443 | for { | ||
444 | line, err := buf.ReadBytes('\n') | ||
445 | if line != nil { | ||
446 | linesCh <- line | ||
447 | } | ||
448 | |||
449 | if err == io.EOF { | ||
450 | return | ||
451 | } | ||
452 | } | ||
453 | }() | ||
454 | |||
455 | // Make sure after we exit we read the lines from stdout forever | ||
456 | // so they don't block since it is an io.Pipe | ||
457 | defer func() { | ||
458 | go func() { | ||
459 | for _ = range linesCh { | ||
460 | } | ||
461 | }() | ||
462 | }() | ||
463 | |||
464 | // Some channels for the next step | ||
465 | timeout := time.After(c.config.StartTimeout) | ||
466 | |||
467 | // Start looking for the address | ||
468 | log.Printf("[DEBUG] plugin: waiting for RPC address for: %s", cmd.Path) | ||
469 | select { | ||
470 | case <-timeout: | ||
471 | err = errors.New("timeout while waiting for plugin to start") | ||
472 | case <-exitCh: | ||
473 | err = errors.New("plugin exited before we could connect") | ||
474 | case lineBytes := <-linesCh: | ||
475 | // Trim the line and split by "|" in order to get the parts of | ||
476 | // the output. | ||
477 | line := strings.TrimSpace(string(lineBytes)) | ||
478 | parts := strings.SplitN(line, "|", 4) | ||
479 | if len(parts) < 4 { | ||
480 | err = fmt.Errorf( | ||
481 | "Unrecognized remote plugin message: %s\n\n"+ | ||
482 | "This usually means that the plugin is either invalid or simply\n"+ | ||
483 | "needs to be recompiled to support the latest protocol.", line) | ||
484 | return | ||
485 | } | ||
486 | |||
487 | // Check the core protocol. Wrapped in a {} for scoping. | ||
488 | { | ||
489 | var coreProtocol int64 | ||
490 | coreProtocol, err = strconv.ParseInt(parts[0], 10, 0) | ||
491 | if err != nil { | ||
492 | err = fmt.Errorf("Error parsing core protocol version: %s", err) | ||
493 | return | ||
494 | } | ||
495 | |||
496 | if int(coreProtocol) != CoreProtocolVersion { | ||
497 | err = fmt.Errorf("Incompatible core API version with plugin. "+ | ||
498 | "Plugin version: %s, Ours: %d\n\n"+ | ||
499 | "To fix this, the plugin usually only needs to be recompiled.\n"+ | ||
500 | "Please report this to the plugin author.", parts[0], CoreProtocolVersion) | ||
501 | return | ||
502 | } | ||
503 | } | ||
504 | |||
505 | // Parse the protocol version | ||
506 | var protocol int64 | ||
507 | protocol, err = strconv.ParseInt(parts[1], 10, 0) | ||
508 | if err != nil { | ||
509 | err = fmt.Errorf("Error parsing protocol version: %s", err) | ||
510 | return | ||
511 | } | ||
512 | |||
513 | // Test the API version | ||
514 | if uint(protocol) != c.config.ProtocolVersion { | ||
515 | err = fmt.Errorf("Incompatible API version with plugin. "+ | ||
516 | "Plugin version: %s, Ours: %d", parts[1], c.config.ProtocolVersion) | ||
517 | return | ||
518 | } | ||
519 | |||
520 | switch parts[2] { | ||
521 | case "tcp": | ||
522 | addr, err = net.ResolveTCPAddr("tcp", parts[3]) | ||
523 | case "unix": | ||
524 | addr, err = net.ResolveUnixAddr("unix", parts[3]) | ||
525 | default: | ||
526 | err = fmt.Errorf("Unknown address type: %s", parts[3]) | ||
527 | } | ||
528 | } | ||
529 | |||
530 | c.address = addr | ||
531 | return | ||
532 | } | ||
533 | |||
534 | // ReattachConfig returns the information that must be provided to NewClient | ||
535 | // to reattach to the plugin process that this client started. This is | ||
536 | // useful for plugins that detach from their parent process. | ||
537 | // | ||
538 | // If this returns nil then the process hasn't been started yet. Please | ||
539 | // call Start or Client before calling this. | ||
540 | func (c *Client) ReattachConfig() *ReattachConfig { | ||
541 | c.l.Lock() | ||
542 | defer c.l.Unlock() | ||
543 | |||
544 | if c.address == nil { | ||
545 | return nil | ||
546 | } | ||
547 | |||
548 | if c.config.Cmd != nil && c.config.Cmd.Process == nil { | ||
549 | return nil | ||
550 | } | ||
551 | |||
552 | // If we connected via reattach, just return the information as-is | ||
553 | if c.config.Reattach != nil { | ||
554 | return c.config.Reattach | ||
555 | } | ||
556 | |||
557 | return &ReattachConfig{ | ||
558 | Addr: c.address, | ||
559 | Pid: c.config.Cmd.Process.Pid, | ||
560 | } | ||
561 | } | ||
562 | |||
563 | func (c *Client) logStderr(r io.Reader) { | ||
564 | bufR := bufio.NewReader(r) | ||
565 | for { | ||
566 | line, err := bufR.ReadString('\n') | ||
567 | if line != "" { | ||
568 | c.config.Stderr.Write([]byte(line)) | ||
569 | |||
570 | line = strings.TrimRightFunc(line, unicode.IsSpace) | ||
571 | log.Printf("[DEBUG] plugin: %s: %s", filepath.Base(c.config.Cmd.Path), line) | ||
572 | } | ||
573 | |||
574 | if err == io.EOF { | ||
575 | break | ||
576 | } | ||
577 | } | ||
578 | |||
579 | // Flag that we've completed logging for others | ||
580 | close(c.doneLogging) | ||
581 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/discover.go b/vendor/github.com/hashicorp/go-plugin/discover.go new file mode 100644 index 0000000..d22c566 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/discover.go | |||
@@ -0,0 +1,28 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "path/filepath" | ||
5 | ) | ||
6 | |||
7 | // Discover discovers plugins that are in a given directory. | ||
8 | // | ||
9 | // The directory doesn't need to be absolute. For example, "." will work fine. | ||
10 | // | ||
11 | // This currently assumes any file matching the glob is a plugin. | ||
12 | // In the future this may be smarter about checking that a file is | ||
13 | // executable and so on. | ||
14 | // | ||
15 | // TODO: test | ||
16 | func Discover(glob, dir string) ([]string, error) { | ||
17 | var err error | ||
18 | |||
19 | // Make the directory absolute if it isn't already | ||
20 | if !filepath.IsAbs(dir) { | ||
21 | dir, err = filepath.Abs(dir) | ||
22 | if err != nil { | ||
23 | return nil, err | ||
24 | } | ||
25 | } | ||
26 | |||
27 | return filepath.Glob(filepath.Join(dir, glob)) | ||
28 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/error.go b/vendor/github.com/hashicorp/go-plugin/error.go new file mode 100644 index 0000000..22a7baa --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/error.go | |||
@@ -0,0 +1,24 @@ | |||
1 | package plugin | ||
2 | |||
3 | // This is a type that wraps error types so that they can be messaged | ||
4 | // across RPC channels. Since "error" is an interface, we can't always | ||
5 | // gob-encode the underlying structure. This is a valid error interface | ||
6 | // implementer that we will push across. | ||
7 | type BasicError struct { | ||
8 | Message string | ||
9 | } | ||
10 | |||
11 | // NewBasicError is used to create a BasicError. | ||
12 | // | ||
13 | // err is allowed to be nil. | ||
14 | func NewBasicError(err error) *BasicError { | ||
15 | if err == nil { | ||
16 | return nil | ||
17 | } | ||
18 | |||
19 | return &BasicError{err.Error()} | ||
20 | } | ||
21 | |||
22 | func (e *BasicError) Error() string { | ||
23 | return e.Message | ||
24 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/mux_broker.go b/vendor/github.com/hashicorp/go-plugin/mux_broker.go new file mode 100644 index 0000000..01c45ad --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/mux_broker.go | |||
@@ -0,0 +1,204 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "encoding/binary" | ||
5 | "fmt" | ||
6 | "log" | ||
7 | "net" | ||
8 | "sync" | ||
9 | "sync/atomic" | ||
10 | "time" | ||
11 | |||
12 | "github.com/hashicorp/yamux" | ||
13 | ) | ||
14 | |||
15 | // MuxBroker is responsible for brokering multiplexed connections by unique ID. | ||
16 | // | ||
17 | // It is used by plugins to multiplex multiple RPC connections and data | ||
18 | // streams on top of a single connection between the plugin process and the | ||
19 | // host process. | ||
20 | // | ||
21 | // This allows a plugin to request a channel with a specific ID to connect to | ||
22 | // or accept a connection from, and the broker handles the details of | ||
23 | // holding these channels open while they're being negotiated. | ||
24 | // | ||
25 | // The Plugin interface has access to these for both Server and Client. | ||
26 | // The broker can be used by either (optionally) to reserve and connect to | ||
27 | // new multiplexed streams. This is useful for complex args and return values, | ||
28 | // or anything else you might need a data stream for. | ||
29 | type MuxBroker struct { | ||
30 | nextId uint32 | ||
31 | session *yamux.Session | ||
32 | streams map[uint32]*muxBrokerPending | ||
33 | |||
34 | sync.Mutex | ||
35 | } | ||
36 | |||
37 | type muxBrokerPending struct { | ||
38 | ch chan net.Conn | ||
39 | doneCh chan struct{} | ||
40 | } | ||
41 | |||
42 | func newMuxBroker(s *yamux.Session) *MuxBroker { | ||
43 | return &MuxBroker{ | ||
44 | session: s, | ||
45 | streams: make(map[uint32]*muxBrokerPending), | ||
46 | } | ||
47 | } | ||
48 | |||
49 | // Accept accepts a connection by ID. | ||
50 | // | ||
51 | // This should not be called multiple times with the same ID at one time. | ||
52 | func (m *MuxBroker) Accept(id uint32) (net.Conn, error) { | ||
53 | var c net.Conn | ||
54 | p := m.getStream(id) | ||
55 | select { | ||
56 | case c = <-p.ch: | ||
57 | close(p.doneCh) | ||
58 | case <-time.After(5 * time.Second): | ||
59 | m.Lock() | ||
60 | defer m.Unlock() | ||
61 | delete(m.streams, id) | ||
62 | |||
63 | return nil, fmt.Errorf("timeout waiting for accept") | ||
64 | } | ||
65 | |||
66 | // Ack our connection | ||
67 | if err := binary.Write(c, binary.LittleEndian, id); err != nil { | ||
68 | c.Close() | ||
69 | return nil, err | ||
70 | } | ||
71 | |||
72 | return c, nil | ||
73 | } | ||
74 | |||
75 | // AcceptAndServe is used to accept a specific stream ID and immediately | ||
76 | // serve an RPC server on that stream ID. This is used to easily serve | ||
77 | // complex arguments. | ||
78 | // | ||
79 | // The served interface is always registered to the "Plugin" name. | ||
80 | func (m *MuxBroker) AcceptAndServe(id uint32, v interface{}) { | ||
81 | conn, err := m.Accept(id) | ||
82 | if err != nil { | ||
83 | log.Printf("[ERR] plugin: plugin acceptAndServe error: %s", err) | ||
84 | return | ||
85 | } | ||
86 | |||
87 | serve(conn, "Plugin", v) | ||
88 | } | ||
89 | |||
90 | // Close closes the connection and all sub-connections. | ||
91 | func (m *MuxBroker) Close() error { | ||
92 | return m.session.Close() | ||
93 | } | ||
94 | |||
95 | // Dial opens a connection by ID. | ||
96 | func (m *MuxBroker) Dial(id uint32) (net.Conn, error) { | ||
97 | // Open the stream | ||
98 | stream, err := m.session.OpenStream() | ||
99 | if err != nil { | ||
100 | return nil, err | ||
101 | } | ||
102 | |||
103 | // Write the stream ID onto the wire. | ||
104 | if err := binary.Write(stream, binary.LittleEndian, id); err != nil { | ||
105 | stream.Close() | ||
106 | return nil, err | ||
107 | } | ||
108 | |||
109 | // Read the ack that we connected. Then we're off! | ||
110 | var ack uint32 | ||
111 | if err := binary.Read(stream, binary.LittleEndian, &ack); err != nil { | ||
112 | stream.Close() | ||
113 | return nil, err | ||
114 | } | ||
115 | if ack != id { | ||
116 | stream.Close() | ||
117 | return nil, fmt.Errorf("bad ack: %d (expected %d)", ack, id) | ||
118 | } | ||
119 | |||
120 | return stream, nil | ||
121 | } | ||
122 | |||
123 | // NextId returns a unique ID to use next. | ||
124 | // | ||
125 | // It is possible for very long-running plugin hosts to wrap this value, | ||
126 | // though it would require a very large amount of RPC calls. In practice | ||
127 | // we've never seen it happen. | ||
128 | func (m *MuxBroker) NextId() uint32 { | ||
129 | return atomic.AddUint32(&m.nextId, 1) | ||
130 | } | ||
131 | |||
132 | // Run starts the brokering and should be executed in a goroutine, since it | ||
133 | // blocks forever, or until the session closes. | ||
134 | // | ||
135 | // Uses of MuxBroker never need to call this. It is called internally by | ||
136 | // the plugin host/client. | ||
137 | func (m *MuxBroker) Run() { | ||
138 | for { | ||
139 | stream, err := m.session.AcceptStream() | ||
140 | if err != nil { | ||
141 | // Once we receive an error, just exit | ||
142 | break | ||
143 | } | ||
144 | |||
145 | // Read the stream ID from the stream | ||
146 | var id uint32 | ||
147 | if err := binary.Read(stream, binary.LittleEndian, &id); err != nil { | ||
148 | stream.Close() | ||
149 | continue | ||
150 | } | ||
151 | |||
152 | // Initialize the waiter | ||
153 | p := m.getStream(id) | ||
154 | select { | ||
155 | case p.ch <- stream: | ||
156 | default: | ||
157 | } | ||
158 | |||
159 | // Wait for a timeout | ||
160 | go m.timeoutWait(id, p) | ||
161 | } | ||
162 | } | ||
163 | |||
164 | func (m *MuxBroker) getStream(id uint32) *muxBrokerPending { | ||
165 | m.Lock() | ||
166 | defer m.Unlock() | ||
167 | |||
168 | p, ok := m.streams[id] | ||
169 | if ok { | ||
170 | return p | ||
171 | } | ||
172 | |||
173 | m.streams[id] = &muxBrokerPending{ | ||
174 | ch: make(chan net.Conn, 1), | ||
175 | doneCh: make(chan struct{}), | ||
176 | } | ||
177 | return m.streams[id] | ||
178 | } | ||
179 | |||
180 | func (m *MuxBroker) timeoutWait(id uint32, p *muxBrokerPending) { | ||
181 | // Wait for the stream to either be picked up and connected, or | ||
182 | // for a timeout. | ||
183 | timeout := false | ||
184 | select { | ||
185 | case <-p.doneCh: | ||
186 | case <-time.After(5 * time.Second): | ||
187 | timeout = true | ||
188 | } | ||
189 | |||
190 | m.Lock() | ||
191 | defer m.Unlock() | ||
192 | |||
193 | // Delete the stream so no one else can grab it | ||
194 | delete(m.streams, id) | ||
195 | |||
196 | // If we timed out, then check if we have a channel in the buffer, | ||
197 | // and if so, close it. | ||
198 | if timeout { | ||
199 | select { | ||
200 | case s := <-p.ch: | ||
201 | s.Close() | ||
202 | } | ||
203 | } | ||
204 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/plugin.go b/vendor/github.com/hashicorp/go-plugin/plugin.go new file mode 100644 index 0000000..37c8fd6 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/plugin.go | |||
@@ -0,0 +1,25 @@ | |||
1 | // The plugin package exposes functions and helpers for communicating to | ||
2 | // plugins which are implemented as standalone binary applications. | ||
3 | // | ||
4 | // plugin.Client fully manages the lifecycle of executing the application, | ||
5 | // connecting to it, and returning the RPC client for dispensing plugins. | ||
6 | // | ||
7 | // plugin.Serve fully manages listeners to expose an RPC server from a binary | ||
8 | // that plugin.Client can connect to. | ||
9 | package plugin | ||
10 | |||
11 | import ( | ||
12 | "net/rpc" | ||
13 | ) | ||
14 | |||
15 | // Plugin is the interface that is implemented to serve/connect to an | ||
16 | // inteface implementation. | ||
17 | type Plugin interface { | ||
18 | // Server should return the RPC server compatible struct to serve | ||
19 | // the methods that the Client calls over net/rpc. | ||
20 | Server(*MuxBroker) (interface{}, error) | ||
21 | |||
22 | // Client returns an interface implementation for the plugin you're | ||
23 | // serving that communicates to the server end of the plugin. | ||
24 | Client(*MuxBroker, *rpc.Client) (interface{}, error) | ||
25 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/process.go b/vendor/github.com/hashicorp/go-plugin/process.go new file mode 100644 index 0000000..88c999a --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/process.go | |||
@@ -0,0 +1,24 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "time" | ||
5 | ) | ||
6 | |||
7 | // pidAlive checks whether a pid is alive. | ||
8 | func pidAlive(pid int) bool { | ||
9 | return _pidAlive(pid) | ||
10 | } | ||
11 | |||
12 | // pidWait blocks for a process to exit. | ||
13 | func pidWait(pid int) error { | ||
14 | ticker := time.NewTicker(1 * time.Second) | ||
15 | defer ticker.Stop() | ||
16 | |||
17 | for range ticker.C { | ||
18 | if !pidAlive(pid) { | ||
19 | break | ||
20 | } | ||
21 | } | ||
22 | |||
23 | return nil | ||
24 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/process_posix.go b/vendor/github.com/hashicorp/go-plugin/process_posix.go new file mode 100644 index 0000000..70ba546 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/process_posix.go | |||
@@ -0,0 +1,19 @@ | |||
1 | // +build !windows | ||
2 | |||
3 | package plugin | ||
4 | |||
5 | import ( | ||
6 | "os" | ||
7 | "syscall" | ||
8 | ) | ||
9 | |||
10 | // _pidAlive tests whether a process is alive or not by sending it Signal 0, | ||
11 | // since Go otherwise has no way to test this. | ||
12 | func _pidAlive(pid int) bool { | ||
13 | proc, err := os.FindProcess(pid) | ||
14 | if err == nil { | ||
15 | err = proc.Signal(syscall.Signal(0)) | ||
16 | } | ||
17 | |||
18 | return err == nil | ||
19 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/process_windows.go b/vendor/github.com/hashicorp/go-plugin/process_windows.go new file mode 100644 index 0000000..9f7b018 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/process_windows.go | |||
@@ -0,0 +1,29 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "syscall" | ||
5 | ) | ||
6 | |||
7 | const ( | ||
8 | // Weird name but matches the MSDN docs | ||
9 | exit_STILL_ACTIVE = 259 | ||
10 | |||
11 | processDesiredAccess = syscall.STANDARD_RIGHTS_READ | | ||
12 | syscall.PROCESS_QUERY_INFORMATION | | ||
13 | syscall.SYNCHRONIZE | ||
14 | ) | ||
15 | |||
16 | // _pidAlive tests whether a process is alive or not | ||
17 | func _pidAlive(pid int) bool { | ||
18 | h, err := syscall.OpenProcess(processDesiredAccess, false, uint32(pid)) | ||
19 | if err != nil { | ||
20 | return false | ||
21 | } | ||
22 | |||
23 | var ec uint32 | ||
24 | if e := syscall.GetExitCodeProcess(h, &ec); e != nil { | ||
25 | return false | ||
26 | } | ||
27 | |||
28 | return ec == exit_STILL_ACTIVE | ||
29 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/rpc_client.go b/vendor/github.com/hashicorp/go-plugin/rpc_client.go new file mode 100644 index 0000000..29f9bf0 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/rpc_client.go | |||
@@ -0,0 +1,123 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "io" | ||
6 | "net" | ||
7 | "net/rpc" | ||
8 | |||
9 | "github.com/hashicorp/yamux" | ||
10 | ) | ||
11 | |||
12 | // RPCClient connects to an RPCServer over net/rpc to dispense plugin types. | ||
13 | type RPCClient struct { | ||
14 | broker *MuxBroker | ||
15 | control *rpc.Client | ||
16 | plugins map[string]Plugin | ||
17 | |||
18 | // These are the streams used for the various stdout/err overrides | ||
19 | stdout, stderr net.Conn | ||
20 | } | ||
21 | |||
22 | // NewRPCClient creates a client from an already-open connection-like value. | ||
23 | // Dial is typically used instead. | ||
24 | func NewRPCClient(conn io.ReadWriteCloser, plugins map[string]Plugin) (*RPCClient, error) { | ||
25 | // Create the yamux client so we can multiplex | ||
26 | mux, err := yamux.Client(conn, nil) | ||
27 | if err != nil { | ||
28 | conn.Close() | ||
29 | return nil, err | ||
30 | } | ||
31 | |||
32 | // Connect to the control stream. | ||
33 | control, err := mux.Open() | ||
34 | if err != nil { | ||
35 | mux.Close() | ||
36 | return nil, err | ||
37 | } | ||
38 | |||
39 | // Connect stdout, stderr streams | ||
40 | stdstream := make([]net.Conn, 2) | ||
41 | for i, _ := range stdstream { | ||
42 | stdstream[i], err = mux.Open() | ||
43 | if err != nil { | ||
44 | mux.Close() | ||
45 | return nil, err | ||
46 | } | ||
47 | } | ||
48 | |||
49 | // Create the broker and start it up | ||
50 | broker := newMuxBroker(mux) | ||
51 | go broker.Run() | ||
52 | |||
53 | // Build the client using our broker and control channel. | ||
54 | return &RPCClient{ | ||
55 | broker: broker, | ||
56 | control: rpc.NewClient(control), | ||
57 | plugins: plugins, | ||
58 | stdout: stdstream[0], | ||
59 | stderr: stdstream[1], | ||
60 | }, nil | ||
61 | } | ||
62 | |||
63 | // SyncStreams should be called to enable syncing of stdout, | ||
64 | // stderr with the plugin. | ||
65 | // | ||
66 | // This will return immediately and the syncing will continue to happen | ||
67 | // in the background. You do not need to launch this in a goroutine itself. | ||
68 | // | ||
69 | // This should never be called multiple times. | ||
70 | func (c *RPCClient) SyncStreams(stdout io.Writer, stderr io.Writer) error { | ||
71 | go copyStream("stdout", stdout, c.stdout) | ||
72 | go copyStream("stderr", stderr, c.stderr) | ||
73 | return nil | ||
74 | } | ||
75 | |||
76 | // Close closes the connection. The client is no longer usable after this | ||
77 | // is called. | ||
78 | func (c *RPCClient) Close() error { | ||
79 | // Call the control channel and ask it to gracefully exit. If this | ||
80 | // errors, then we save it so that we always return an error but we | ||
81 | // want to try to close the other channels anyways. | ||
82 | var empty struct{} | ||
83 | returnErr := c.control.Call("Control.Quit", true, &empty) | ||
84 | |||
85 | // Close the other streams we have | ||
86 | if err := c.control.Close(); err != nil { | ||
87 | return err | ||
88 | } | ||
89 | if err := c.stdout.Close(); err != nil { | ||
90 | return err | ||
91 | } | ||
92 | if err := c.stderr.Close(); err != nil { | ||
93 | return err | ||
94 | } | ||
95 | if err := c.broker.Close(); err != nil { | ||
96 | return err | ||
97 | } | ||
98 | |||
99 | // Return back the error we got from Control.Quit. This is very important | ||
100 | // since we MUST return non-nil error if this fails so that Client.Kill | ||
101 | // will properly try a process.Kill. | ||
102 | return returnErr | ||
103 | } | ||
104 | |||
105 | func (c *RPCClient) Dispense(name string) (interface{}, error) { | ||
106 | p, ok := c.plugins[name] | ||
107 | if !ok { | ||
108 | return nil, fmt.Errorf("unknown plugin type: %s", name) | ||
109 | } | ||
110 | |||
111 | var id uint32 | ||
112 | if err := c.control.Call( | ||
113 | "Dispenser.Dispense", name, &id); err != nil { | ||
114 | return nil, err | ||
115 | } | ||
116 | |||
117 | conn, err := c.broker.Dial(id) | ||
118 | if err != nil { | ||
119 | return nil, err | ||
120 | } | ||
121 | |||
122 | return p.Client(c.broker, rpc.NewClient(conn)) | ||
123 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/rpc_server.go b/vendor/github.com/hashicorp/go-plugin/rpc_server.go new file mode 100644 index 0000000..3984dc8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/rpc_server.go | |||
@@ -0,0 +1,185 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | "io" | ||
7 | "log" | ||
8 | "net" | ||
9 | "net/rpc" | ||
10 | "sync" | ||
11 | |||
12 | "github.com/hashicorp/yamux" | ||
13 | ) | ||
14 | |||
15 | // RPCServer listens for network connections and then dispenses interface | ||
16 | // implementations over net/rpc. | ||
17 | // | ||
18 | // After setting the fields below, they shouldn't be read again directly | ||
19 | // from the structure which may be reading/writing them concurrently. | ||
20 | type RPCServer struct { | ||
21 | Plugins map[string]Plugin | ||
22 | |||
23 | // Stdout, Stderr are what this server will use instead of the | ||
24 | // normal stdin/out/err. This is because due to the multi-process nature | ||
25 | // of our plugin system, we can't use the normal process values so we | ||
26 | // make our own custom one we pipe across. | ||
27 | Stdout io.Reader | ||
28 | Stderr io.Reader | ||
29 | |||
30 | // DoneCh should be set to a non-nil channel that will be closed | ||
31 | // when the control requests the RPC server to end. | ||
32 | DoneCh chan<- struct{} | ||
33 | |||
34 | lock sync.Mutex | ||
35 | } | ||
36 | |||
37 | // Accept accepts connections on a listener and serves requests for | ||
38 | // each incoming connection. Accept blocks; the caller typically invokes | ||
39 | // it in a go statement. | ||
40 | func (s *RPCServer) Accept(lis net.Listener) { | ||
41 | for { | ||
42 | conn, err := lis.Accept() | ||
43 | if err != nil { | ||
44 | log.Printf("[ERR] plugin: plugin server: %s", err) | ||
45 | return | ||
46 | } | ||
47 | |||
48 | go s.ServeConn(conn) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | // ServeConn runs a single connection. | ||
53 | // | ||
54 | // ServeConn blocks, serving the connection until the client hangs up. | ||
55 | func (s *RPCServer) ServeConn(conn io.ReadWriteCloser) { | ||
56 | // First create the yamux server to wrap this connection | ||
57 | mux, err := yamux.Server(conn, nil) | ||
58 | if err != nil { | ||
59 | conn.Close() | ||
60 | log.Printf("[ERR] plugin: error creating yamux server: %s", err) | ||
61 | return | ||
62 | } | ||
63 | |||
64 | // Accept the control connection | ||
65 | control, err := mux.Accept() | ||
66 | if err != nil { | ||
67 | mux.Close() | ||
68 | if err != io.EOF { | ||
69 | log.Printf("[ERR] plugin: error accepting control connection: %s", err) | ||
70 | } | ||
71 | |||
72 | return | ||
73 | } | ||
74 | |||
75 | // Connect the stdstreams (in, out, err) | ||
76 | stdstream := make([]net.Conn, 2) | ||
77 | for i, _ := range stdstream { | ||
78 | stdstream[i], err = mux.Accept() | ||
79 | if err != nil { | ||
80 | mux.Close() | ||
81 | log.Printf("[ERR] plugin: accepting stream %d: %s", i, err) | ||
82 | return | ||
83 | } | ||
84 | } | ||
85 | |||
86 | // Copy std streams out to the proper place | ||
87 | go copyStream("stdout", stdstream[0], s.Stdout) | ||
88 | go copyStream("stderr", stdstream[1], s.Stderr) | ||
89 | |||
90 | // Create the broker and start it up | ||
91 | broker := newMuxBroker(mux) | ||
92 | go broker.Run() | ||
93 | |||
94 | // Use the control connection to build the dispenser and serve the | ||
95 | // connection. | ||
96 | server := rpc.NewServer() | ||
97 | server.RegisterName("Control", &controlServer{ | ||
98 | server: s, | ||
99 | }) | ||
100 | server.RegisterName("Dispenser", &dispenseServer{ | ||
101 | broker: broker, | ||
102 | plugins: s.Plugins, | ||
103 | }) | ||
104 | server.ServeConn(control) | ||
105 | } | ||
106 | |||
107 | // done is called internally by the control server to trigger the | ||
108 | // doneCh to close which is listened to by the main process to cleanly | ||
109 | // exit. | ||
110 | func (s *RPCServer) done() { | ||
111 | s.lock.Lock() | ||
112 | defer s.lock.Unlock() | ||
113 | |||
114 | if s.DoneCh != nil { | ||
115 | close(s.DoneCh) | ||
116 | s.DoneCh = nil | ||
117 | } | ||
118 | } | ||
119 | |||
120 | // dispenseServer dispenses variousinterface implementations for Terraform. | ||
121 | type controlServer struct { | ||
122 | server *RPCServer | ||
123 | } | ||
124 | |||
125 | func (c *controlServer) Quit( | ||
126 | null bool, response *struct{}) error { | ||
127 | // End the server | ||
128 | c.server.done() | ||
129 | |||
130 | // Always return true | ||
131 | *response = struct{}{} | ||
132 | |||
133 | return nil | ||
134 | } | ||
135 | |||
136 | // dispenseServer dispenses variousinterface implementations for Terraform. | ||
137 | type dispenseServer struct { | ||
138 | broker *MuxBroker | ||
139 | plugins map[string]Plugin | ||
140 | } | ||
141 | |||
142 | func (d *dispenseServer) Dispense( | ||
143 | name string, response *uint32) error { | ||
144 | // Find the function to create this implementation | ||
145 | p, ok := d.plugins[name] | ||
146 | if !ok { | ||
147 | return fmt.Errorf("unknown plugin type: %s", name) | ||
148 | } | ||
149 | |||
150 | // Create the implementation first so we know if there is an error. | ||
151 | impl, err := p.Server(d.broker) | ||
152 | if err != nil { | ||
153 | // We turn the error into an errors error so that it works across RPC | ||
154 | return errors.New(err.Error()) | ||
155 | } | ||
156 | |||
157 | // Reserve an ID for our implementation | ||
158 | id := d.broker.NextId() | ||
159 | *response = id | ||
160 | |||
161 | // Run the rest in a goroutine since it can only happen once this RPC | ||
162 | // call returns. We wait for a connection for the plugin implementation | ||
163 | // and serve it. | ||
164 | go func() { | ||
165 | conn, err := d.broker.Accept(id) | ||
166 | if err != nil { | ||
167 | log.Printf("[ERR] go-plugin: plugin dispense error: %s: %s", name, err) | ||
168 | return | ||
169 | } | ||
170 | |||
171 | serve(conn, "Plugin", impl) | ||
172 | }() | ||
173 | |||
174 | return nil | ||
175 | } | ||
176 | |||
177 | func serve(conn io.ReadWriteCloser, name string, v interface{}) { | ||
178 | server := rpc.NewServer() | ||
179 | if err := server.RegisterName(name, v); err != nil { | ||
180 | log.Printf("[ERR] go-plugin: plugin dispense error: %s", err) | ||
181 | return | ||
182 | } | ||
183 | |||
184 | server.ServeConn(conn) | ||
185 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/server.go b/vendor/github.com/hashicorp/go-plugin/server.go new file mode 100644 index 0000000..b5c5270 --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/server.go | |||
@@ -0,0 +1,222 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | "io/ioutil" | ||
7 | "log" | ||
8 | "net" | ||
9 | "os" | ||
10 | "os/signal" | ||
11 | "runtime" | ||
12 | "strconv" | ||
13 | "sync/atomic" | ||
14 | ) | ||
15 | |||
16 | // CoreProtocolVersion is the ProtocolVersion of the plugin system itself. | ||
17 | // We will increment this whenever we change any protocol behavior. This | ||
18 | // will invalidate any prior plugins but will at least allow us to iterate | ||
19 | // on the core in a safe way. We will do our best to do this very | ||
20 | // infrequently. | ||
21 | const CoreProtocolVersion = 1 | ||
22 | |||
23 | // HandshakeConfig is the configuration used by client and servers to | ||
24 | // handshake before starting a plugin connection. This is embedded by | ||
25 | // both ServeConfig and ClientConfig. | ||
26 | // | ||
27 | // In practice, the plugin host creates a HandshakeConfig that is exported | ||
28 | // and plugins then can easily consume it. | ||
29 | type HandshakeConfig struct { | ||
30 | // ProtocolVersion is the version that clients must match on to | ||
31 | // agree they can communicate. This should match the ProtocolVersion | ||
32 | // set on ClientConfig when using a plugin. | ||
33 | ProtocolVersion uint | ||
34 | |||
35 | // MagicCookieKey and value are used as a very basic verification | ||
36 | // that a plugin is intended to be launched. This is not a security | ||
37 | // measure, just a UX feature. If the magic cookie doesn't match, | ||
38 | // we show human-friendly output. | ||
39 | MagicCookieKey string | ||
40 | MagicCookieValue string | ||
41 | } | ||
42 | |||
43 | // ServeConfig configures what sorts of plugins are served. | ||
44 | type ServeConfig struct { | ||
45 | // HandshakeConfig is the configuration that must match clients. | ||
46 | HandshakeConfig | ||
47 | |||
48 | // Plugins are the plugins that are served. | ||
49 | Plugins map[string]Plugin | ||
50 | } | ||
51 | |||
52 | // Serve serves the plugins given by ServeConfig. | ||
53 | // | ||
54 | // Serve doesn't return until the plugin is done being executed. Any | ||
55 | // errors will be outputted to the log. | ||
56 | // | ||
57 | // This is the method that plugins should call in their main() functions. | ||
58 | func Serve(opts *ServeConfig) { | ||
59 | // Validate the handshake config | ||
60 | if opts.MagicCookieKey == "" || opts.MagicCookieValue == "" { | ||
61 | fmt.Fprintf(os.Stderr, | ||
62 | "Misconfigured ServeConfig given to serve this plugin: no magic cookie\n"+ | ||
63 | "key or value was set. Please notify the plugin author and report\n"+ | ||
64 | "this as a bug.\n") | ||
65 | os.Exit(1) | ||
66 | } | ||
67 | |||
68 | // First check the cookie | ||
69 | if os.Getenv(opts.MagicCookieKey) != opts.MagicCookieValue { | ||
70 | fmt.Fprintf(os.Stderr, | ||
71 | "This binary is a plugin. These are not meant to be executed directly.\n"+ | ||
72 | "Please execute the program that consumes these plugins, which will\n"+ | ||
73 | "load any plugins automatically\n") | ||
74 | os.Exit(1) | ||
75 | } | ||
76 | |||
77 | // Logging goes to the original stderr | ||
78 | log.SetOutput(os.Stderr) | ||
79 | |||
80 | // Create our new stdout, stderr files. These will override our built-in | ||
81 | // stdout/stderr so that it works across the stream boundary. | ||
82 | stdout_r, stdout_w, err := os.Pipe() | ||
83 | if err != nil { | ||
84 | fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err) | ||
85 | os.Exit(1) | ||
86 | } | ||
87 | stderr_r, stderr_w, err := os.Pipe() | ||
88 | if err != nil { | ||
89 | fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err) | ||
90 | os.Exit(1) | ||
91 | } | ||
92 | |||
93 | // Register a listener so we can accept a connection | ||
94 | listener, err := serverListener() | ||
95 | if err != nil { | ||
96 | log.Printf("[ERR] plugin: plugin init: %s", err) | ||
97 | return | ||
98 | } | ||
99 | defer listener.Close() | ||
100 | |||
101 | // Create the channel to tell us when we're done | ||
102 | doneCh := make(chan struct{}) | ||
103 | |||
104 | // Create the RPC server to dispense | ||
105 | server := &RPCServer{ | ||
106 | Plugins: opts.Plugins, | ||
107 | Stdout: stdout_r, | ||
108 | Stderr: stderr_r, | ||
109 | DoneCh: doneCh, | ||
110 | } | ||
111 | |||
112 | // Output the address and service name to stdout so that core can bring it up. | ||
113 | log.Printf("[DEBUG] plugin: plugin address: %s %s\n", | ||
114 | listener.Addr().Network(), listener.Addr().String()) | ||
115 | fmt.Printf("%d|%d|%s|%s\n", | ||
116 | CoreProtocolVersion, | ||
117 | opts.ProtocolVersion, | ||
118 | listener.Addr().Network(), | ||
119 | listener.Addr().String()) | ||
120 | os.Stdout.Sync() | ||
121 | |||
122 | // Eat the interrupts | ||
123 | ch := make(chan os.Signal, 1) | ||
124 | signal.Notify(ch, os.Interrupt) | ||
125 | go func() { | ||
126 | var count int32 = 0 | ||
127 | for { | ||
128 | <-ch | ||
129 | newCount := atomic.AddInt32(&count, 1) | ||
130 | log.Printf( | ||
131 | "[DEBUG] plugin: received interrupt signal (count: %d). Ignoring.", | ||
132 | newCount) | ||
133 | } | ||
134 | }() | ||
135 | |||
136 | // Set our new out, err | ||
137 | os.Stdout = stdout_w | ||
138 | os.Stderr = stderr_w | ||
139 | |||
140 | // Serve | ||
141 | go server.Accept(listener) | ||
142 | |||
143 | // Wait for the graceful exit | ||
144 | <-doneCh | ||
145 | } | ||
146 | |||
147 | func serverListener() (net.Listener, error) { | ||
148 | if runtime.GOOS == "windows" { | ||
149 | return serverListener_tcp() | ||
150 | } | ||
151 | |||
152 | return serverListener_unix() | ||
153 | } | ||
154 | |||
155 | func serverListener_tcp() (net.Listener, error) { | ||
156 | minPort, err := strconv.ParseInt(os.Getenv("PLUGIN_MIN_PORT"), 10, 32) | ||
157 | if err != nil { | ||
158 | return nil, err | ||
159 | } | ||
160 | |||
161 | maxPort, err := strconv.ParseInt(os.Getenv("PLUGIN_MAX_PORT"), 10, 32) | ||
162 | if err != nil { | ||
163 | return nil, err | ||
164 | } | ||
165 | |||
166 | for port := minPort; port <= maxPort; port++ { | ||
167 | address := fmt.Sprintf("127.0.0.1:%d", port) | ||
168 | listener, err := net.Listen("tcp", address) | ||
169 | if err == nil { | ||
170 | return listener, nil | ||
171 | } | ||
172 | } | ||
173 | |||
174 | return nil, errors.New("Couldn't bind plugin TCP listener") | ||
175 | } | ||
176 | |||
177 | func serverListener_unix() (net.Listener, error) { | ||
178 | tf, err := ioutil.TempFile("", "plugin") | ||
179 | if err != nil { | ||
180 | return nil, err | ||
181 | } | ||
182 | path := tf.Name() | ||
183 | |||
184 | // Close the file and remove it because it has to not exist for | ||
185 | // the domain socket. | ||
186 | if err := tf.Close(); err != nil { | ||
187 | return nil, err | ||
188 | } | ||
189 | if err := os.Remove(path); err != nil { | ||
190 | return nil, err | ||
191 | } | ||
192 | |||
193 | l, err := net.Listen("unix", path) | ||
194 | if err != nil { | ||
195 | return nil, err | ||
196 | } | ||
197 | |||
198 | // Wrap the listener in rmListener so that the Unix domain socket file | ||
199 | // is removed on close. | ||
200 | return &rmListener{ | ||
201 | Listener: l, | ||
202 | Path: path, | ||
203 | }, nil | ||
204 | } | ||
205 | |||
206 | // rmListener is an implementation of net.Listener that forwards most | ||
207 | // calls to the listener but also removes a file as part of the close. We | ||
208 | // use this to cleanup the unix domain socket on close. | ||
209 | type rmListener struct { | ||
210 | net.Listener | ||
211 | Path string | ||
212 | } | ||
213 | |||
214 | func (l *rmListener) Close() error { | ||
215 | // Close the listener itself | ||
216 | if err := l.Listener.Close(); err != nil { | ||
217 | return err | ||
218 | } | ||
219 | |||
220 | // Remove the file | ||
221 | return os.Remove(l.Path) | ||
222 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/server_mux.go b/vendor/github.com/hashicorp/go-plugin/server_mux.go new file mode 100644 index 0000000..033079e --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/server_mux.go | |||
@@ -0,0 +1,31 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "os" | ||
6 | ) | ||
7 | |||
8 | // ServeMuxMap is the type that is used to configure ServeMux | ||
9 | type ServeMuxMap map[string]*ServeConfig | ||
10 | |||
11 | // ServeMux is like Serve, but serves multiple types of plugins determined | ||
12 | // by the argument given on the command-line. | ||
13 | // | ||
14 | // This command doesn't return until the plugin is done being executed. Any | ||
15 | // errors are logged or output to stderr. | ||
16 | func ServeMux(m ServeMuxMap) { | ||
17 | if len(os.Args) != 2 { | ||
18 | fmt.Fprintf(os.Stderr, | ||
19 | "Invoked improperly. This is an internal command that shouldn't\n"+ | ||
20 | "be manually invoked.\n") | ||
21 | os.Exit(1) | ||
22 | } | ||
23 | |||
24 | opts, ok := m[os.Args[1]] | ||
25 | if !ok { | ||
26 | fmt.Fprintf(os.Stderr, "Unknown plugin: %s\n", os.Args[1]) | ||
27 | os.Exit(1) | ||
28 | } | ||
29 | |||
30 | Serve(opts) | ||
31 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/stream.go b/vendor/github.com/hashicorp/go-plugin/stream.go new file mode 100644 index 0000000..1d547aa --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/stream.go | |||
@@ -0,0 +1,18 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "io" | ||
5 | "log" | ||
6 | ) | ||
7 | |||
8 | func copyStream(name string, dst io.Writer, src io.Reader) { | ||
9 | if src == nil { | ||
10 | panic(name + ": src is nil") | ||
11 | } | ||
12 | if dst == nil { | ||
13 | panic(name + ": dst is nil") | ||
14 | } | ||
15 | if _, err := io.Copy(dst, src); err != nil && err != io.EOF { | ||
16 | log.Printf("[ERR] plugin: stream copy '%s' error: %s", name, err) | ||
17 | } | ||
18 | } | ||
diff --git a/vendor/github.com/hashicorp/go-plugin/testing.go b/vendor/github.com/hashicorp/go-plugin/testing.go new file mode 100644 index 0000000..9086a1b --- /dev/null +++ b/vendor/github.com/hashicorp/go-plugin/testing.go | |||
@@ -0,0 +1,76 @@ | |||
1 | package plugin | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "net" | ||
6 | "net/rpc" | ||
7 | "testing" | ||
8 | ) | ||
9 | |||
10 | // The testing file contains test helpers that you can use outside of | ||
11 | // this package for making it easier to test plugins themselves. | ||
12 | |||
13 | // TestConn is a helper function for returning a client and server | ||
14 | // net.Conn connected to each other. | ||
15 | func TestConn(t *testing.T) (net.Conn, net.Conn) { | ||
16 | // Listen to any local port. This listener will be closed | ||
17 | // after a single connection is established. | ||
18 | l, err := net.Listen("tcp", "127.0.0.1:0") | ||
19 | if err != nil { | ||
20 | t.Fatalf("err: %s", err) | ||
21 | } | ||
22 | |||
23 | // Start a goroutine to accept our client connection | ||
24 | var serverConn net.Conn | ||
25 | doneCh := make(chan struct{}) | ||
26 | go func() { | ||
27 | defer close(doneCh) | ||
28 | defer l.Close() | ||
29 | var err error | ||
30 | serverConn, err = l.Accept() | ||
31 | if err != nil { | ||
32 | t.Fatalf("err: %s", err) | ||
33 | } | ||
34 | }() | ||
35 | |||
36 | // Connect to the server | ||
37 | clientConn, err := net.Dial("tcp", l.Addr().String()) | ||
38 | if err != nil { | ||
39 | t.Fatalf("err: %s", err) | ||
40 | } | ||
41 | |||
42 | // Wait for the server side to acknowledge it has connected | ||
43 | <-doneCh | ||
44 | |||
45 | return clientConn, serverConn | ||
46 | } | ||
47 | |||
48 | // TestRPCConn returns a rpc client and server connected to each other. | ||
49 | func TestRPCConn(t *testing.T) (*rpc.Client, *rpc.Server) { | ||
50 | clientConn, serverConn := TestConn(t) | ||
51 | |||
52 | server := rpc.NewServer() | ||
53 | go server.ServeConn(serverConn) | ||
54 | |||
55 | client := rpc.NewClient(clientConn) | ||
56 | return client, server | ||
57 | } | ||
58 | |||
59 | // TestPluginRPCConn returns a plugin RPC client and server that are connected | ||
60 | // together and configured. | ||
61 | func TestPluginRPCConn(t *testing.T, ps map[string]Plugin) (*RPCClient, *RPCServer) { | ||
62 | // Create two net.Conns we can use to shuttle our control connection | ||
63 | clientConn, serverConn := TestConn(t) | ||
64 | |||
65 | // Start up the server | ||
66 | server := &RPCServer{Plugins: ps, Stdout: new(bytes.Buffer), Stderr: new(bytes.Buffer)} | ||
67 | go server.ServeConn(serverConn) | ||
68 | |||
69 | // Connect the client to the server | ||
70 | client, err := NewRPCClient(clientConn, ps) | ||
71 | if err != nil { | ||
72 | t.Fatalf("err: %s", err) | ||
73 | } | ||
74 | |||
75 | return client, server | ||
76 | } | ||