From f8d399490e967be3afd2e52767c9cdaedf8f5d2c Mon Sep 17 00:00:00 2001 From: Patrik Cyvoct Date: Thu, 14 Jun 2018 11:06:48 +0200 Subject: [PATCH] Add new dependencies Signed-off-by: Patrik Cyvoct --- src/cmd/linuxkit/vendor.conf | 8 + .../github.com/containerd/console/LICENSE | 201 ++ .../github.com/containerd/console/README.md | 17 + .../github.com/containerd/console/console.go | 78 + .../containerd/console/console_linux.go | 271 +++ .../containerd/console/console_unix.go | 158 ++ .../containerd/console/console_windows.go | 216 ++ .../containerd/console/tc_darwin.go | 53 + .../containerd/console/tc_freebsd.go | 45 + .../github.com/containerd/console/tc_linux.go | 49 + .../containerd/console/tc_openbsd_cgo.go | 51 + .../containerd/console/tc_openbsd_nocgo.go | 47 + .../containerd/console/tc_solaris_cgo.go | 51 + .../containerd/console/tc_solaris_nocgo.go | 47 + .../github.com/containerd/console/tc_unix.go | 91 + .../vendor/github.com/creack/goselect/LICENSE | 22 + .../github.com/creack/goselect/README.md | 30 + .../github.com/creack/goselect/fdset.go | 33 + .../github.com/creack/goselect/fdset_32.go | 10 + .../github.com/creack/goselect/fdset_64.go | 11 + .../github.com/creack/goselect/fdset_doc.go | 93 + .../creack/goselect/fdset_freebsd.go | 33 + .../creack/goselect/fdset_unsupported.go | 20 + .../creack/goselect/fdset_windows.go | 57 + .../github.com/creack/goselect/select.go | 28 + .../creack/goselect/select_linux.go | 10 + .../creack/goselect/select_other.go | 9 + .../creack/goselect/select_unsupported.go | 16 + .../creack/goselect/select_windows.go | 13 + .../creack/goselect/zselect_windows.go | 39 + .../github.com/gorilla/websocket/LICENSE | 22 + .../github.com/gorilla/websocket/README.md | 64 + .../github.com/gorilla/websocket/client.go | 330 +++ .../gorilla/websocket/client_clone.go | 16 + .../gorilla/websocket/client_clone_legacy.go | 38 + .../gorilla/websocket/compression.go | 148 ++ .../github.com/gorilla/websocket/conn.go | 1157 ++++++++++ .../github.com/gorilla/websocket/conn_read.go | 18 + .../gorilla/websocket/conn_read_legacy.go | 21 + .../gorilla/websocket/conn_write.go | 15 + .../gorilla/websocket/conn_write_legacy.go | 18 + .../github.com/gorilla/websocket/doc.go | 180 ++ .../github.com/gorilla/websocket/json.go | 60 + .../github.com/gorilla/websocket/mask.go | 54 + .../github.com/gorilla/websocket/mask_safe.go | 15 + .../github.com/gorilla/websocket/prepared.go | 103 + .../github.com/gorilla/websocket/proxy.go | 77 + .../github.com/gorilla/websocket/server.go | 298 +++ .../github.com/gorilla/websocket/util.go | 237 ++ .../gorilla/websocket/x_net_proxy.go | 473 ++++ .../vendor/github.com/moul/anonuuid/LICENSE | 22 + .../vendor/github.com/moul/anonuuid/README.md | 171 ++ .../github.com/moul/anonuuid/anonuuid.go | 229 ++ .../github.com/moul/gotty-client/LICENSE | 22 + .../moul/gotty-client/LICENSE.apache | 192 ++ .../github.com/moul/gotty-client/README.md | 217 ++ .../github.com/moul/gotty-client/arch.go | 33 + .../moul/gotty-client/arch_windows.go | 16 + .../github.com/moul/gotty-client/ascii.go | 66 + .../moul/gotty-client/gotty-client.go | 560 +++++ .../github.com/moul/gotty-client/proxy.go | 93 + .../github.com/renstrom/fuzzysearch/LICENSE | 21 + .../github.com/renstrom/fuzzysearch/README.md | 43 + .../renstrom/fuzzysearch/fuzzy/fuzzy.go | 167 ++ .../renstrom/fuzzysearch/fuzzy/levenshtein.go | 43 + .../github.com/scaleway/go-scaleway/LICENSE | 21 + .../github.com/scaleway/go-scaleway/README.md | 27 + .../github.com/scaleway/go-scaleway/api.go | 2003 +++++++++++++++++ .../scaleway/go-scaleway/cache/cache.go | 693 ++++++ .../scaleway/go-scaleway/logger/logger.go | 77 + .../scaleway/go-scaleway/types/types.go | 983 ++++++++ .../linuxkit/vendor/golang.org/x/sync/LICENSE | 27 + .../linuxkit/vendor/golang.org/x/sync/PATENTS | 22 + .../vendor/golang.org/x/sync/README.md | 18 + .../golang.org/x/sync/errgroup/errgroup.go | 67 + 75 files changed, 10984 insertions(+) create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/console.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/console_linux.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/console_unix.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/console_windows.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/tc_darwin.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/tc_freebsd.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/tc_linux.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/tc_openbsd_cgo.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/tc_openbsd_nocgo.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/tc_solaris_cgo.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/tc_solaris_nocgo.go create mode 100644 src/cmd/linuxkit/vendor/github.com/containerd/console/tc_unix.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_32.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_64.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_doc.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_freebsd.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_unsupported.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_windows.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/select.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/select_linux.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/select_other.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/select_unsupported.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/select_windows.go create mode 100644 src/cmd/linuxkit/vendor/github.com/creack/goselect/zselect_windows.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client_clone.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client_clone_legacy.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/compression.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_read.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_read_legacy.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_write.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_write_legacy.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/doc.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/json.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/mask.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/mask_safe.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/prepared.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/proxy.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/server.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/util.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gorilla/websocket/x_net_proxy.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/anonuuid/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/anonuuid/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/anonuuid/anonuuid.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/gotty-client/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/gotty-client/LICENSE.apache create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/gotty-client/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/gotty-client/arch.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/gotty-client/arch_windows.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/gotty-client/ascii.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/gotty-client/gotty-client.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moul/gotty-client/proxy.go create mode 100644 src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go create mode 100644 src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go create mode 100644 src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/api.go create mode 100644 src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/cache/cache.go create mode 100644 src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/logger/logger.go create mode 100644 src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/types/types.go create mode 100644 src/cmd/linuxkit/vendor/golang.org/x/sync/LICENSE create mode 100644 src/cmd/linuxkit/vendor/golang.org/x/sync/PATENTS create mode 100644 src/cmd/linuxkit/vendor/golang.org/x/sync/README.md create mode 100644 src/cmd/linuxkit/vendor/golang.org/x/sync/errgroup/errgroup.go diff --git a/src/cmd/linuxkit/vendor.conf b/src/cmd/linuxkit/vendor.conf index 88333f1f5..8e87a8bc3 100644 --- a/src/cmd/linuxkit/vendor.conf +++ b/src/cmd/linuxkit/vendor.conf @@ -53,3 +53,11 @@ golang.org/x/text c4d099d611ac3ded35360abf03581e13d91c828f google.golang.org/api 373a4c220f5c90e5b7ff7101779c5be385d171be google.golang.org/grpc 0713829b980f4ddd276689a36235c5fcc82a21bf gopkg.in/yaml.v2 a3f3340b5840cee44f372bddb5880fcbc419b46a +github.com/scaleway/go-scaleway f8c3b31c65867b4cb96b3bd41e574c33fd1d69ae +github.com/moul/anonuuid c6987e46f8a0231504bcd402962749f8dd9970b0 +golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca +github.com/renstrom/fuzzysearch 7a8f9a1c4bed53899ecd512daeaf8207cc454156 +github.com/moul/gotty-client e5589f6df35953284b091b8394daa6be6c453469 +github.com/gorilla/websocket 21ab95fa12b9bdd8fecf5fa3586aad941cc98785 +github.com/creack/goselect 58854f77ee8d858ce751b0a9bcc5533fef7bfa9e +github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925 diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/LICENSE b/src/cmd/linuxkit/vendor/github.com/containerd/console/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/README.md b/src/cmd/linuxkit/vendor/github.com/containerd/console/README.md new file mode 100644 index 000000000..4c56d9d13 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/README.md @@ -0,0 +1,17 @@ +# console + +[![Build Status](https://travis-ci.org/containerd/console.svg?branch=master)](https://travis-ci.org/containerd/console) + +Golang package for dealing with consoles. Light on deps and a simple API. + +## Modifying the current process + +```go +current := console.Current() +defer current.Reset() + +if err := current.SetRaw(); err != nil { +} +ws, err := current.Size() +current.Resize(ws) +``` diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/console.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/console.go new file mode 100644 index 000000000..c187a9b41 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/console.go @@ -0,0 +1,78 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "errors" + "io" + "os" +) + +var ErrNotAConsole = errors.New("provided file is not a console") + +type Console interface { + io.Reader + io.Writer + io.Closer + + // Resize resizes the console to the provided window size + Resize(WinSize) error + // ResizeFrom resizes the calling console to the size of the + // provided console + ResizeFrom(Console) error + // SetRaw sets the console in raw mode + SetRaw() error + // DisableEcho disables echo on the console + DisableEcho() error + // Reset restores the console to its orignal state + Reset() error + // Size returns the window size of the console + Size() (WinSize, error) + // Fd returns the console's file descriptor + Fd() uintptr + // Name returns the console's file name + Name() string +} + +// WinSize specifies the window size of the console +type WinSize struct { + // Height of the console + Height uint16 + // Width of the console + Width uint16 + x uint16 + y uint16 +} + +// Current returns the current processes console +func Current() Console { + c, err := ConsoleFromFile(os.Stdin) + if err != nil { + // stdin should always be a console for the design + // of this function + panic(err) + } + return c +} + +// ConsoleFromFile returns a console using the provided file +func ConsoleFromFile(f *os.File) (Console, error) { + if err := checkConsole(f); err != nil { + return nil, err + } + return newMaster(f) +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/console_linux.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/console_linux.go new file mode 100644 index 000000000..312bce17d --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/console_linux.go @@ -0,0 +1,271 @@ +// +build linux + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "io" + "os" + "sync" + + "golang.org/x/sys/unix" +) + +const ( + maxEvents = 128 +) + +// Epoller manages multiple epoll consoles using edge-triggered epoll api so we +// dont have to deal with repeated wake-up of EPOLLER or EPOLLHUP. +// For more details, see: +// - https://github.com/systemd/systemd/pull/4262 +// - https://github.com/moby/moby/issues/27202 +// +// Example usage of Epoller and EpollConsole can be as follow: +// +// epoller, _ := NewEpoller() +// epollConsole, _ := epoller.Add(console) +// go epoller.Wait() +// var ( +// b bytes.Buffer +// wg sync.WaitGroup +// ) +// wg.Add(1) +// go func() { +// io.Copy(&b, epollConsole) +// wg.Done() +// }() +// // perform I/O on the console +// epollConsole.Shutdown(epoller.CloseConsole) +// wg.Wait() +// epollConsole.Close() +type Epoller struct { + efd int + mu sync.Mutex + fdMapping map[int]*EpollConsole +} + +// NewEpoller returns an instance of epoller with a valid epoll fd. +func NewEpoller() (*Epoller, error) { + efd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) + if err != nil { + return nil, err + } + return &Epoller{ + efd: efd, + fdMapping: make(map[int]*EpollConsole), + }, nil +} + +// Add creates a epoll console based on the provided console. The console will +// be registered with EPOLLET (i.e. using edge-triggered notification) and its +// file descriptor will be set to non-blocking mode. After this, user should use +// the return console to perform I/O. +func (e *Epoller) Add(console Console) (*EpollConsole, error) { + sysfd := int(console.Fd()) + // Set sysfd to non-blocking mode + if err := unix.SetNonblock(sysfd, true); err != nil { + return nil, err + } + + ev := unix.EpollEvent{ + Events: unix.EPOLLIN | unix.EPOLLOUT | unix.EPOLLRDHUP | unix.EPOLLET, + Fd: int32(sysfd), + } + if err := unix.EpollCtl(e.efd, unix.EPOLL_CTL_ADD, sysfd, &ev); err != nil { + return nil, err + } + ef := &EpollConsole{ + Console: console, + sysfd: sysfd, + readc: sync.NewCond(&sync.Mutex{}), + writec: sync.NewCond(&sync.Mutex{}), + } + e.mu.Lock() + e.fdMapping[sysfd] = ef + e.mu.Unlock() + return ef, nil +} + +// Wait starts the loop to wait for its consoles' notifications and signal +// appropriate console that it can perform I/O. +func (e *Epoller) Wait() error { + events := make([]unix.EpollEvent, maxEvents) + for { + n, err := unix.EpollWait(e.efd, events, -1) + if err != nil { + // EINTR: The call was interrupted by a signal handler before either + // any of the requested events occurred or the timeout expired + if err == unix.EINTR { + continue + } + return err + } + for i := 0; i < n; i++ { + ev := &events[i] + // the console is ready to be read from + if ev.Events&(unix.EPOLLIN|unix.EPOLLHUP|unix.EPOLLERR) != 0 { + if epfile := e.getConsole(int(ev.Fd)); epfile != nil { + epfile.signalRead() + } + } + // the console is ready to be written to + if ev.Events&(unix.EPOLLOUT|unix.EPOLLHUP|unix.EPOLLERR) != 0 { + if epfile := e.getConsole(int(ev.Fd)); epfile != nil { + epfile.signalWrite() + } + } + } + } +} + +// Close unregister the console's file descriptor from epoll interface +func (e *Epoller) CloseConsole(fd int) error { + e.mu.Lock() + defer e.mu.Unlock() + delete(e.fdMapping, fd) + return unix.EpollCtl(e.efd, unix.EPOLL_CTL_DEL, fd, &unix.EpollEvent{}) +} + +func (e *Epoller) getConsole(sysfd int) *EpollConsole { + e.mu.Lock() + f := e.fdMapping[sysfd] + e.mu.Unlock() + return f +} + +// Close the epoll fd +func (e *Epoller) Close() error { + return unix.Close(e.efd) +} + +// EpollConsole acts like a console but register its file descriptor with a +// epoll fd and uses epoll API to perform I/O. +type EpollConsole struct { + Console + readc *sync.Cond + writec *sync.Cond + sysfd int + closed bool +} + +// Read reads up to len(p) bytes into p. It returns the number of bytes read +// (0 <= n <= len(p)) and any error encountered. +// +// If the console's read returns EAGAIN or EIO, we assumes that its a +// temporary error because the other side went away and wait for the signal +// generated by epoll event to continue. +func (ec *EpollConsole) Read(p []byte) (n int, err error) { + var read int + ec.readc.L.Lock() + defer ec.readc.L.Unlock() + for { + read, err = ec.Console.Read(p[n:]) + n += read + if err != nil { + var hangup bool + if perr, ok := err.(*os.PathError); ok { + hangup = (perr.Err == unix.EAGAIN || perr.Err == unix.EIO) + } else { + hangup = (err == unix.EAGAIN || err == unix.EIO) + } + // if the other end disappear, assume this is temporary and wait for the + // signal to continue again. Unless we didnt read anything and the + // console is already marked as closed then we should exit + if hangup && !(n == 0 && len(p) > 0 && ec.closed) { + ec.readc.Wait() + continue + } + } + break + } + // if we didnt read anything then return io.EOF to end gracefully + if n == 0 && len(p) > 0 && err == nil { + err = io.EOF + } + // signal for others that we finished the read + ec.readc.Signal() + return n, err +} + +// Writes len(p) bytes from p to the console. It returns the number of bytes +// written from p (0 <= n <= len(p)) and any error encountered that caused +// the write to stop early. +// +// If writes to the console returns EAGAIN or EIO, we assumes that its a +// temporary error because the other side went away and wait for the signal +// generated by epoll event to continue. +func (ec *EpollConsole) Write(p []byte) (n int, err error) { + var written int + ec.writec.L.Lock() + defer ec.writec.L.Unlock() + for { + written, err = ec.Console.Write(p[n:]) + n += written + if err != nil { + var hangup bool + if perr, ok := err.(*os.PathError); ok { + hangup = (perr.Err == unix.EAGAIN || perr.Err == unix.EIO) + } else { + hangup = (err == unix.EAGAIN || err == unix.EIO) + } + // if the other end disappear, assume this is temporary and wait for the + // signal to continue again. + if hangup { + ec.writec.Wait() + continue + } + } + // unrecoverable error, break the loop and return the error + break + } + if n < len(p) && err == nil { + err = io.ErrShortWrite + } + // signal for others that we finished the write + ec.writec.Signal() + return n, err +} + +// Close closed the file descriptor and signal call waiters for this fd. +// It accepts a callback which will be called with the console's fd. The +// callback typically will be used to do further cleanup such as unregister the +// console's fd from the epoll interface. +// User should call Shutdown and wait for all I/O operation to be finished +// before closing the console. +func (ec *EpollConsole) Shutdown(close func(int) error) error { + ec.readc.L.Lock() + defer ec.readc.L.Unlock() + ec.writec.L.Lock() + defer ec.writec.L.Unlock() + + ec.readc.Broadcast() + ec.writec.Broadcast() + ec.closed = true + return close(ec.sysfd) +} + +// signalRead signals that the console is readable. +func (ec *EpollConsole) signalRead() { + ec.readc.Signal() +} + +// signalWrite signals that the console is writable. +func (ec *EpollConsole) signalWrite() { + ec.writec.Signal() +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/console_unix.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/console_unix.go new file mode 100644 index 000000000..a4a8d1267 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/console_unix.go @@ -0,0 +1,158 @@ +// +build darwin freebsd linux openbsd solaris + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// NewPty creates a new pty pair +// The master is returned as the first console and a string +// with the path to the pty slave is returned as the second +func NewPty() (Console, string, error) { + f, err := os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0) + if err != nil { + return nil, "", err + } + slave, err := ptsname(f) + if err != nil { + return nil, "", err + } + if err := unlockpt(f); err != nil { + return nil, "", err + } + m, err := newMaster(f) + if err != nil { + return nil, "", err + } + return m, slave, nil +} + +type master struct { + f *os.File + original *unix.Termios +} + +func (m *master) Read(b []byte) (int, error) { + return m.f.Read(b) +} + +func (m *master) Write(b []byte) (int, error) { + return m.f.Write(b) +} + +func (m *master) Close() error { + return m.f.Close() +} + +func (m *master) Resize(ws WinSize) error { + return tcswinsz(m.f.Fd(), ws) +} + +func (m *master) ResizeFrom(c Console) error { + ws, err := c.Size() + if err != nil { + return err + } + return m.Resize(ws) +} + +func (m *master) Reset() error { + if m.original == nil { + return nil + } + return tcset(m.f.Fd(), m.original) +} + +func (m *master) getCurrent() (unix.Termios, error) { + var termios unix.Termios + if err := tcget(m.f.Fd(), &termios); err != nil { + return unix.Termios{}, err + } + return termios, nil +} + +func (m *master) SetRaw() error { + rawState, err := m.getCurrent() + if err != nil { + return err + } + rawState = cfmakeraw(rawState) + rawState.Oflag = rawState.Oflag | unix.OPOST + return tcset(m.f.Fd(), &rawState) +} + +func (m *master) DisableEcho() error { + rawState, err := m.getCurrent() + if err != nil { + return err + } + rawState.Lflag = rawState.Lflag &^ unix.ECHO + return tcset(m.f.Fd(), &rawState) +} + +func (m *master) Size() (WinSize, error) { + return tcgwinsz(m.f.Fd()) +} + +func (m *master) Fd() uintptr { + return m.f.Fd() +} + +func (m *master) Name() string { + return m.f.Name() +} + +// checkConsole checks if the provided file is a console +func checkConsole(f *os.File) error { + var termios unix.Termios + if tcget(f.Fd(), &termios) != nil { + return ErrNotAConsole + } + return nil +} + +func newMaster(f *os.File) (Console, error) { + m := &master{ + f: f, + } + t, err := m.getCurrent() + if err != nil { + return nil, err + } + m.original = &t + return m, nil +} + +// ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair +// created by us acts normally. In particular, a not-very-well-known default of +// Linux unix98 ptys is that they have +onlcr by default. While this isn't a +// problem for terminal emulators, because we relay data from the terminal we +// also relay that funky line discipline. +func ClearONLCR(fd uintptr) error { + return setONLCR(fd, false) +} + +// SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair +// created by us acts as intended for a terminal emulator. +func SetONLCR(fd uintptr) error { + return setONLCR(fd, true) +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/console_windows.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/console_windows.go new file mode 100644 index 000000000..7aa726f99 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/console_windows.go @@ -0,0 +1,216 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "fmt" + "os" + + "github.com/pkg/errors" + "golang.org/x/sys/windows" +) + +var ( + vtInputSupported bool + ErrNotImplemented = errors.New("not implemented") +) + +func (m *master) initStdios() { + m.in = windows.Handle(os.Stdin.Fd()) + if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil { + // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. + if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil { + vtInputSupported = true + } + // Unconditionally set the console mode back even on failure because SetConsoleMode + // remembers invalid bits on input handles. + windows.SetConsoleMode(m.in, m.inMode) + } else { + fmt.Printf("failed to get console mode for stdin: %v\n", err) + } + + m.out = windows.Handle(os.Stdout.Fd()) + if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil { + if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { + m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + windows.SetConsoleMode(m.out, m.outMode) + } + } else { + fmt.Printf("failed to get console mode for stdout: %v\n", err) + } + + m.err = windows.Handle(os.Stderr.Fd()) + if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil { + if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { + m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + windows.SetConsoleMode(m.err, m.errMode) + } + } else { + fmt.Printf("failed to get console mode for stderr: %v\n", err) + } +} + +type master struct { + in windows.Handle + inMode uint32 + + out windows.Handle + outMode uint32 + + err windows.Handle + errMode uint32 +} + +func (m *master) SetRaw() error { + if err := makeInputRaw(m.in, m.inMode); err != nil { + return err + } + + // Set StdOut and StdErr to raw mode, we ignore failures since + // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of + // Windows. + + windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN) + + windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN) + + return nil +} + +func (m *master) Reset() error { + for _, s := range []struct { + fd windows.Handle + mode uint32 + }{ + {m.in, m.inMode}, + {m.out, m.outMode}, + {m.err, m.errMode}, + } { + if err := windows.SetConsoleMode(s.fd, s.mode); err != nil { + return errors.Wrap(err, "unable to restore console mode") + } + } + + return nil +} + +func (m *master) Size() (WinSize, error) { + var info windows.ConsoleScreenBufferInfo + err := windows.GetConsoleScreenBufferInfo(m.out, &info) + if err != nil { + return WinSize{}, errors.Wrap(err, "unable to get console info") + } + + winsize := WinSize{ + Width: uint16(info.Window.Right - info.Window.Left + 1), + Height: uint16(info.Window.Bottom - info.Window.Top + 1), + } + + return winsize, nil +} + +func (m *master) Resize(ws WinSize) error { + return ErrNotImplemented +} + +func (m *master) ResizeFrom(c Console) error { + return ErrNotImplemented +} + +func (m *master) DisableEcho() error { + mode := m.inMode &^ windows.ENABLE_ECHO_INPUT + mode |= windows.ENABLE_PROCESSED_INPUT + mode |= windows.ENABLE_LINE_INPUT + + if err := windows.SetConsoleMode(m.in, mode); err != nil { + return errors.Wrap(err, "unable to set console to disable echo") + } + + return nil +} + +func (m *master) Close() error { + return nil +} + +func (m *master) Read(b []byte) (int, error) { + panic("not implemented on windows") +} + +func (m *master) Write(b []byte) (int, error) { + panic("not implemented on windows") +} + +func (m *master) Fd() uintptr { + return uintptr(m.in) +} + +// on windows, console can only be made from os.Std{in,out,err}, hence there +// isnt a single name here we can use. Return a dummy "console" value in this +// case should be sufficient. +func (m *master) Name() string { + return "console" +} + +// makeInputRaw puts the terminal (Windows Console) connected to the given +// file descriptor into raw mode +func makeInputRaw(fd windows.Handle, mode uint32) error { + // See + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx + // -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx + + // Disable these modes + mode &^= windows.ENABLE_ECHO_INPUT + mode &^= windows.ENABLE_LINE_INPUT + mode &^= windows.ENABLE_MOUSE_INPUT + mode &^= windows.ENABLE_WINDOW_INPUT + mode &^= windows.ENABLE_PROCESSED_INPUT + + // Enable these modes + mode |= windows.ENABLE_EXTENDED_FLAGS + mode |= windows.ENABLE_INSERT_MODE + mode |= windows.ENABLE_QUICK_EDIT_MODE + + if vtInputSupported { + mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT + } + + if err := windows.SetConsoleMode(fd, mode); err != nil { + return errors.Wrap(err, "unable to set console to raw mode") + } + + return nil +} + +func checkConsole(f *os.File) error { + var mode uint32 + if err := windows.GetConsoleMode(windows.Handle(f.Fd()), &mode); err != nil { + return err + } + return nil +} + +func newMaster(f *os.File) (Console, error) { + if f != os.Stdin && f != os.Stdout && f != os.Stderr { + return nil, errors.New("creating a console from a file is not supported on windows") + } + m := &master{} + m.initStdios() + return m, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_darwin.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_darwin.go new file mode 100644 index 000000000..b0128abb0 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_darwin.go @@ -0,0 +1,53 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "fmt" + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +func ioctl(fd, flag, data uintptr) error { + if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, flag, data); err != 0 { + return err + } + return nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + var u int32 + return ioctl(f.Fd(), unix.TIOCPTYUNLK, uintptr(unsafe.Pointer(&u))) +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCPTYGNAME) + if err != nil { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", n), nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_freebsd.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_freebsd.go new file mode 100644 index 000000000..04583a615 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_freebsd.go @@ -0,0 +1,45 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "fmt" + "os" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +// This does not exist on FreeBSD, it does not allocate controlling terminals on open +func unlockpt(f *os.File) error { + return nil +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN) + if err != nil { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", n), nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_linux.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_linux.go new file mode 100644 index 000000000..1bdd68e6d --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_linux.go @@ -0,0 +1,49 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "fmt" + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TCGETS + cmdTcSet = unix.TCSETS +) + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + var u int32 + if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))); err != 0 { + return err + } + return nil +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + var u uint32 + if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&u))); err != 0 { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", u), nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_openbsd_cgo.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_openbsd_cgo.go new file mode 100644 index 000000000..f0cec06a7 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_openbsd_cgo.go @@ -0,0 +1,51 @@ +// +build openbsd,cgo + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +//#include +import "C" + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + ptspath, err := C.ptsname(C.int(f.Fd())) + if err != nil { + return "", err + } + return C.GoString(ptspath), nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + if _, err := C.grantpt(C.int(f.Fd())); err != nil { + return err + } + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_openbsd_nocgo.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_openbsd_nocgo.go new file mode 100644 index 000000000..daccce205 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_openbsd_nocgo.go @@ -0,0 +1,47 @@ +// +build openbsd,!cgo + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// +// Implementing the functions below requires cgo support. Non-cgo stubs +// versions are defined below to enable cross-compilation of source code +// that depends on these functions, but the resultant cross-compiled +// binaries cannot actually be used. If the stub function(s) below are +// actually invoked they will display an error message and cause the +// calling process to exit. +// + +package console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TIOCGETA + cmdTcSet = unix.TIOCSETA +) + +func ptsname(f *os.File) (string, error) { + panic("ptsname() support requires cgo.") +} + +func unlockpt(f *os.File) error { + panic("unlockpt() support requires cgo.") +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_solaris_cgo.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_solaris_cgo.go new file mode 100644 index 000000000..e36a68edd --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_solaris_cgo.go @@ -0,0 +1,51 @@ +// +build solaris,cgo + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +//#include +import "C" + +const ( + cmdTcGet = unix.TCGETS + cmdTcSet = unix.TCSETS +) + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + ptspath, err := C.ptsname(C.int(f.Fd())) + if err != nil { + return "", err + } + return C.GoString(ptspath), nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + if _, err := C.grantpt(C.int(f.Fd())); err != nil { + return err + } + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_solaris_nocgo.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_solaris_nocgo.go new file mode 100644 index 000000000..eb0bd2c36 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_solaris_nocgo.go @@ -0,0 +1,47 @@ +// +build solaris,!cgo + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// +// Implementing the functions below requires cgo support. Non-cgo stubs +// versions are defined below to enable cross-compilation of source code +// that depends on these functions, but the resultant cross-compiled +// binaries cannot actually be used. If the stub function(s) below are +// actually invoked they will display an error message and cause the +// calling process to exit. +// + +package console + +import ( + "os" + + "golang.org/x/sys/unix" +) + +const ( + cmdTcGet = unix.TCGETS + cmdTcSet = unix.TCSETS +) + +func ptsname(f *os.File) (string, error) { + panic("ptsname() support requires cgo.") +} + +func unlockpt(f *os.File) error { + panic("unlockpt() support requires cgo.") +} diff --git a/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_unix.go b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_unix.go new file mode 100644 index 000000000..7ae773c53 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/containerd/console/tc_unix.go @@ -0,0 +1,91 @@ +// +build darwin freebsd linux openbsd solaris + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package console + +import ( + "golang.org/x/sys/unix" +) + +func tcget(fd uintptr, p *unix.Termios) error { + termios, err := unix.IoctlGetTermios(int(fd), cmdTcGet) + if err != nil { + return err + } + *p = *termios + return nil +} + +func tcset(fd uintptr, p *unix.Termios) error { + return unix.IoctlSetTermios(int(fd), cmdTcSet, p) +} + +func tcgwinsz(fd uintptr) (WinSize, error) { + var ws WinSize + + uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ) + if err != nil { + return ws, err + } + + // Translate from unix.Winsize to console.WinSize + ws.Height = uws.Row + ws.Width = uws.Col + ws.x = uws.Xpixel + ws.y = uws.Ypixel + return ws, nil +} + +func tcswinsz(fd uintptr, ws WinSize) error { + // Translate from console.WinSize to unix.Winsize + + var uws unix.Winsize + uws.Row = ws.Height + uws.Col = ws.Width + uws.Xpixel = ws.x + uws.Ypixel = ws.y + + return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, &uws) +} + +func setONLCR(fd uintptr, enable bool) error { + var termios unix.Termios + if err := tcget(fd, &termios); err != nil { + return err + } + if enable { + // Set +onlcr so we can act like a real terminal + termios.Oflag |= unix.ONLCR + } else { + // Set -onlcr so we don't have to deal with \r. + termios.Oflag &^= unix.ONLCR + } + return tcset(fd, &termios) +} + +func cfmakeraw(t unix.Termios) unix.Termios { + t.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON) + t.Oflag &^= unix.OPOST + t.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN) + t.Cflag &^= (unix.CSIZE | unix.PARENB) + t.Cflag &^= unix.CS8 + t.Cc[unix.VMIN] = 1 + t.Cc[unix.VTIME] = 0 + + return t +} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/LICENSE b/src/cmd/linuxkit/vendor/github.com/creack/goselect/LICENSE new file mode 100644 index 000000000..95c600af1 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Guillaume J. Charmes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/README.md b/src/cmd/linuxkit/vendor/github.com/creack/goselect/README.md new file mode 100644 index 000000000..d5d06510e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/README.md @@ -0,0 +1,30 @@ +# go-select + +select(2) implementation in Go + +## Supported platforms + +| | 386 | amd64 | arm | arm64 | +|---------------|-----|-------|-----|-------| +| **linux** | yes | yes | yes | yes | +| **darwin** | yes | yes | n/a | ?? | +| **freebsd** | yes | yes | yes | ?? | +| **openbsd** | yes | yes | yes | ?? | +| **netbsd** | yes | yes | yes | ?? | +| **dragonfly** | n/a | yes | n/a | ?? | +| **solaris** | n/a | no | n/a | ?? | +| **plan9** | no | no | n/a | ?? | +| **windows** | yes | yes | n/a | ?? | +| **android** | n/a | n/a | no | ?? | + +*n/a: platform not supported by Go + +Go on `plan9` and `solaris` do not implement `syscall.Select` not `syscall.SYS_SELECT`. + +## Cross compile + +Using davecheney's https://github.com/davecheney/golang-crosscompile + +``` +export PLATFORMS="darwin/386 darwin/amd64 freebsd/386 freebsd/amd64 freebsd/arm linux/386 linux/amd64 linux/arm windows/386 windows/amd64 openbsd/386 openbsd/amd64 netbsd/386 netbsd/amd64 dragonfly/amd64 plan9/386 plan9/amd64 solaris/amd64" +``` diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset.go new file mode 100644 index 000000000..2ff3f6c7a --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset.go @@ -0,0 +1,33 @@ +// +build !freebsd,!windows,!plan9 + +package goselect + +import "syscall" + +const FD_SETSIZE = syscall.FD_SETSIZE + +// FDSet wraps syscall.FdSet with convenience methods +type FDSet syscall.FdSet + +// Set adds the fd to the set +func (fds *FDSet) Set(fd uintptr) { + fds.Bits[fd/NFDBITS] |= (1 << (fd % NFDBITS)) +} + +// Clear remove the fd from the set +func (fds *FDSet) Clear(fd uintptr) { + fds.Bits[fd/NFDBITS] &^= (1 << (fd % NFDBITS)) +} + +// IsSet check if the given fd is set +func (fds *FDSet) IsSet(fd uintptr) bool { + return fds.Bits[fd/NFDBITS]&(1<<(fd%NFDBITS)) != 0 +} + +// Keep a null set to avoid reinstatiation +var nullFdSet = &FDSet{} + +// Zero empties the Set +func (fds *FDSet) Zero() { + copy(fds.Bits[:], (nullFdSet).Bits[:]) +} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_32.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_32.go new file mode 100644 index 000000000..a346d3f1c --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_32.go @@ -0,0 +1,10 @@ +// +build darwin openbsd netbsd 386 arm mips mipsle + +package goselect + +// darwin, netbsd and openbsd uses uint32 on both amd64 and 386 + +const ( + // NFDBITS is the amount of bits per mask + NFDBITS = 4 * 8 +) diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_64.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_64.go new file mode 100644 index 000000000..0e70213d8 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_64.go @@ -0,0 +1,11 @@ +// +build !darwin,!netbsd,!openbsd +// +build amd64 arm64 ppc64le mips64 mips64le s390x + +package goselect + +// darwin, netbsd and openbsd uses uint32 on both amd64 and 386 + +const ( + // NFDBITS is the amount of bits per mask + NFDBITS = 8 * 8 +) diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_doc.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_doc.go new file mode 100644 index 000000000..d9f15a1ce --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_doc.go @@ -0,0 +1,93 @@ +package goselect + +/** +From: XCode's MacOSX10.10.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/sys/select.h +-- +// darwin/amd64 / 386 +sizeof(__int32_t) == 4 +-- + +typedef __int32_t __fd_mask; + +#define FD_SETSIZE 1024 +#define __NFDBITS (sizeof(__fd_mask) * 8) +#define __howmany(x, y) ((((x) % (y)) == 0) ? ((x) / (y)) : (((x) / (y)) + 1)) + +typedef struct fd_set { + __fd_mask fds_bits[__howmany(__FD_SETSIZE, __NFDBITS)]; +} fd_set; + +#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) +#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) +#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) +*/ + +/** +From: /usr/include/i386-linux-gnu/sys/select.h +-- +// linux/i686 +sizeof(long int) == 4 +-- + +typedef long int __fd_mask; + +#define FD_SETSIZE 1024 +#define __NFDBITS (sizeof(__fd_mask) * 8) + + +typedef struct fd_set { + __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; +} fd_set; + +#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) +#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) +#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) +*/ + +/** +From: /usr/include/x86_64-linux-gnu/sys/select.h +-- +// linux/amd64 +sizeof(long int) == 8 +-- + +typedef long int __fd_mask; + +#define FD_SETSIZE 1024 +#define __NFDBITS (sizeof(__fd_mask) * 8) + + +typedef struct fd_set { + __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; +} fd_set; + +#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) +#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) +#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) +*/ + +/** +From: /usr/include/sys/select.h +-- +// freebsd/amd64 +sizeof(unsigned long) == 8 +-- + +typedef unsigned long __fd_mask; + +#define FD_SETSIZE 1024U +#define __NFDBITS (sizeof(__fd_mask) * 8) +#define _howmany(x, y) (((x) + ((y) - 1)) / (y)) + +typedef struct fd_set { + __fd_mask fds_bits[_howmany(FD_SETSIZE, __NFDBITS)]; +} fd_set; + +#define __FD_MASK(n) ((__fd_mask)1 << ((n) % __NFDBITS)) +#define FD_SET(n, p) ((p)->fds_bits[(n)/__NFDBITS] |= __FD_MASK(n)) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/__NFDBITS] &= ~__FD_MASK(n)) +#define FD_ISSET(n, p) (((p)->fds_bits[(n)/__NFDBITS] & __FD_MASK(n)) != 0) +*/ diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_freebsd.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_freebsd.go new file mode 100644 index 000000000..03f7b9127 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_freebsd.go @@ -0,0 +1,33 @@ +// +build freebsd + +package goselect + +import "syscall" + +const FD_SETSIZE = syscall.FD_SETSIZE + +// FDSet wraps syscall.FdSet with convenience methods +type FDSet syscall.FdSet + +// Set adds the fd to the set +func (fds *FDSet) Set(fd uintptr) { + fds.X__fds_bits[fd/NFDBITS] |= (1 << (fd % NFDBITS)) +} + +// Clear remove the fd from the set +func (fds *FDSet) Clear(fd uintptr) { + fds.X__fds_bits[fd/NFDBITS] &^= (1 << (fd % NFDBITS)) +} + +// IsSet check if the given fd is set +func (fds *FDSet) IsSet(fd uintptr) bool { + return fds.X__fds_bits[fd/NFDBITS]&(1<<(fd%NFDBITS)) != 0 +} + +// Keep a null set to avoid reinstatiation +var nullFdSet = &FDSet{} + +// Zero empties the Set +func (fds *FDSet) Zero() { + copy(fds.X__fds_bits[:], (nullFdSet).X__fds_bits[:]) +} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_unsupported.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_unsupported.go new file mode 100644 index 000000000..cbd8d5880 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_unsupported.go @@ -0,0 +1,20 @@ +// +build plan9 + +package goselect + +const FD_SETSIZE = 0 + +// FDSet wraps syscall.FdSet with convenience methods +type FDSet struct{} + +// Set adds the fd to the set +func (fds *FDSet) Set(fd uintptr) {} + +// Clear remove the fd from the set +func (fds *FDSet) Clear(fd uintptr) {} + +// IsSet check if the given fd is set +func (fds *FDSet) IsSet(fd uintptr) bool { return false } + +// Zero empties the Set +func (fds *FDSet) Zero() {} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_windows.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_windows.go new file mode 100644 index 000000000..ee3491975 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/fdset_windows.go @@ -0,0 +1,57 @@ +// +build windows + +package goselect + +import "syscall" + +const FD_SETSIZE = 64 + +// FDSet extracted from mingw libs source code +type FDSet struct { + fd_count uint + fd_array [FD_SETSIZE]uintptr +} + +// Set adds the fd to the set +func (fds *FDSet) Set(fd uintptr) { + var i uint + for i = 0; i < fds.fd_count; i++ { + if fds.fd_array[i] == fd { + break + } + } + if i == fds.fd_count { + if fds.fd_count < FD_SETSIZE { + fds.fd_array[i] = fd + fds.fd_count++ + } + } +} + +// Clear remove the fd from the set +func (fds *FDSet) Clear(fd uintptr) { + var i uint + for i = 0; i < fds.fd_count; i++ { + if fds.fd_array[i] == fd { + for i < fds.fd_count-1 { + fds.fd_array[i] = fds.fd_array[i+1] + i++ + } + fds.fd_count-- + break + } + } +} + +// IsSet check if the given fd is set +func (fds *FDSet) IsSet(fd uintptr) bool { + if isset, err := __WSAFDIsSet(syscall.Handle(fd), fds); err == nil && isset != 0 { + return true + } + return false +} + +// Zero empties the Set +func (fds *FDSet) Zero() { + fds.fd_count = 0 +} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/select.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select.go new file mode 100644 index 000000000..7f2875e73 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select.go @@ -0,0 +1,28 @@ +package goselect + +import ( + "syscall" + "time" +) + +// Select wraps syscall.Select with Go types +func Select(n int, r, w, e *FDSet, timeout time.Duration) error { + var timeval *syscall.Timeval + if timeout >= 0 { + t := syscall.NsecToTimeval(timeout.Nanoseconds()) + timeval = &t + } + + return sysSelect(n, r, w, e, timeval) +} + +// RetrySelect wraps syscall.Select with Go types, and retries a number of times, with a given retryDelay. +func RetrySelect(n int, r, w, e *FDSet, timeout time.Duration, retries int, retryDelay time.Duration) (err error) { + for i := 0; i < retries; i++ { + if err = Select(n, r, w, e, timeout); err != syscall.EINTR { + return err + } + time.Sleep(retryDelay) + } + return err +} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_linux.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_linux.go new file mode 100644 index 000000000..acd569e9d --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_linux.go @@ -0,0 +1,10 @@ +// +build linux + +package goselect + +import "syscall" + +func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { + _, err := syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), timeout) + return err +} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_other.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_other.go new file mode 100644 index 000000000..6c8208147 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_other.go @@ -0,0 +1,9 @@ +// +build !linux,!windows,!plan9,!solaris + +package goselect + +import "syscall" + +func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { + return syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), timeout) +} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_unsupported.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_unsupported.go new file mode 100644 index 000000000..bea0ad94a --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_unsupported.go @@ -0,0 +1,16 @@ +// +build plan9 solaris + +package goselect + +import ( + "fmt" + "runtime" + "syscall" +) + +// ErrUnsupported . +var ErrUnsupported = fmt.Errorf("Platofrm %s/%s unsupported", runtime.GOOS, runtime.GOARCH) + +func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { + return ErrUnsupported +} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_windows.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_windows.go new file mode 100644 index 000000000..0fb70d5d2 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/select_windows.go @@ -0,0 +1,13 @@ +// +build windows + +package goselect + +import "syscall" + +//sys _select(nfds int, readfds *FDSet, writefds *FDSet, exceptfds *FDSet, timeout *syscall.Timeval) (total int, err error) = ws2_32.select +//sys __WSAFDIsSet(handle syscall.Handle, fdset *FDSet) (isset int, err error) = ws2_32.__WSAFDIsSet + +func sysSelect(n int, r, w, e *FDSet, timeout *syscall.Timeval) error { + _, err := _select(n, r, w, e, timeout) + return err +} diff --git a/src/cmd/linuxkit/vendor/github.com/creack/goselect/zselect_windows.go b/src/cmd/linuxkit/vendor/github.com/creack/goselect/zselect_windows.go new file mode 100644 index 000000000..e01502c07 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/creack/goselect/zselect_windows.go @@ -0,0 +1,39 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package goselect + +import "unsafe" +import "syscall" + +var _ unsafe.Pointer + +var ( + modws2_32 = syscall.NewLazyDLL("ws2_32.dll") + + procselect = modws2_32.NewProc("select") + proc__WSAFDIsSet = modws2_32.NewProc("__WSAFDIsSet") +) + +func _select(nfds int, readfds *FDSet, writefds *FDSet, exceptfds *FDSet, timeout *syscall.Timeval) (total int, err error) { + r0, _, e1 := syscall.Syscall6(procselect.Addr(), 5, uintptr(nfds), uintptr(unsafe.Pointer(readfds)), uintptr(unsafe.Pointer(writefds)), uintptr(unsafe.Pointer(exceptfds)), uintptr(unsafe.Pointer(timeout)), 0) + total = int(r0) + if total == 0 { + if e1 != 0 { + err = error(e1) + } + } + return +} + +func __WSAFDIsSet(handle syscall.Handle, fdset *FDSet) (isset int, err error) { + r0, _, e1 := syscall.Syscall(proc__WSAFDIsSet.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(fdset)), 0) + isset = int(r0) + if isset == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/LICENSE b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/LICENSE new file mode 100644 index 000000000..9171c9722 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/README.md b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/README.md new file mode 100644 index 000000000..20e391f86 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/README.md @@ -0,0 +1,64 @@ +# Gorilla WebSocket + +Gorilla WebSocket is a [Go](http://golang.org/) implementation of the +[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. + +[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket) +[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) + +### Documentation + +* [API Reference](http://godoc.org/github.com/gorilla/websocket) +* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) +* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) +* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) +* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) + +### Status + +The Gorilla WebSocket package provides a complete and tested implementation of +the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The +package API is stable. + +### Installation + + go get github.com/gorilla/websocket + +### Protocol Compliance + +The Gorilla WebSocket package passes the server tests in the [Autobahn Test +Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn +subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). + +### Gorilla WebSocket compared with other packages + + + + + + + + + + + + + + + + + + +
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
+ +Notes: + +1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). +2. The application can get the type of a received data message by implementing + a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) + function. +3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. + Read returns when the input buffer is full or a frame boundary is + encountered. Each call to Write sends a single frame message. The Gorilla + io.Reader and io.WriteCloser operate on a single WebSocket message. + diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client.go new file mode 100644 index 000000000..41f8ed53d --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client.go @@ -0,0 +1,330 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "crypto/tls" + "errors" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "strings" + "time" +) + +// ErrBadHandshake is returned when the server response to opening handshake is +// invalid. +var ErrBadHandshake = errors.New("websocket: bad handshake") + +var errInvalidCompression = errors.New("websocket: invalid compression negotiation") + +// NewClient creates a new client connection using the given net connection. +// The URL u specifies the host and request URI. Use requestHeader to specify +// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies +// (Cookie). Use the response.Header to get the selected subprotocol +// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +// +// If the WebSocket handshake fails, ErrBadHandshake is returned along with a +// non-nil *http.Response so that callers can handle redirects, authentication, +// etc. +// +// Deprecated: Use Dialer instead. +func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { + d := Dialer{ + ReadBufferSize: readBufSize, + WriteBufferSize: writeBufSize, + NetDial: func(net, addr string) (net.Conn, error) { + return netConn, nil + }, + } + return d.Dial(u.String(), requestHeader) +} + +// A Dialer contains options for connecting to WebSocket server. +type Dialer struct { + // NetDial specifies the dial function for creating TCP connections. If + // NetDial is nil, net.Dial is used. + NetDial func(network, addr string) (net.Conn, error) + + // Proxy specifies a function to return a proxy for a given + // Request. If the function returns a non-nil error, the + // request is aborted with the provided error. + // If Proxy is nil or returns a nil *URL, no proxy is used. + Proxy func(*http.Request) (*url.URL, error) + + // TLSClientConfig specifies the TLS configuration to use with tls.Client. + // If nil, the default configuration is used. + TLSClientConfig *tls.Config + + // HandshakeTimeout specifies the duration for the handshake to complete. + HandshakeTimeout time.Duration + + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer + // size is zero, then a useful default size is used. The I/O buffer sizes + // do not limit the size of the messages that can be sent or received. + ReadBufferSize, WriteBufferSize int + + // Subprotocols specifies the client's requested subprotocols. + Subprotocols []string + + // EnableCompression specifies if the client should attempt to negotiate + // per message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently only "no context + // takeover" modes are supported. + EnableCompression bool + + // Jar specifies the cookie jar. + // If Jar is nil, cookies are not sent in requests and ignored + // in responses. + Jar http.CookieJar +} + +var errMalformedURL = errors.New("malformed ws or wss URL") + +func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { + hostPort = u.Host + hostNoPort = u.Host + if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { + hostNoPort = hostNoPort[:i] + } else { + switch u.Scheme { + case "wss": + hostPort += ":443" + case "https": + hostPort += ":443" + default: + hostPort += ":80" + } + } + return hostPort, hostNoPort +} + +// DefaultDialer is a dialer with all fields set to the default values. +var DefaultDialer = &Dialer{ + Proxy: http.ProxyFromEnvironment, + HandshakeTimeout: 45 * time.Second, +} + +// nilDialer is dialer to use when receiver is nil. +var nilDialer Dialer = *DefaultDialer + +// Dial creates a new client connection. Use requestHeader to specify the +// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). +// Use the response.Header to get the selected subprotocol +// (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +// +// If the WebSocket handshake fails, ErrBadHandshake is returned along with a +// non-nil *http.Response so that callers can handle redirects, authentication, +// etcetera. The response body may not contain the entire response and does not +// need to be closed by the application. +func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { + + if d == nil { + d = &nilDialer + } + + challengeKey, err := generateChallengeKey() + if err != nil { + return nil, nil, err + } + + u, err := url.Parse(urlStr) + if err != nil { + return nil, nil, err + } + + switch u.Scheme { + case "ws": + u.Scheme = "http" + case "wss": + u.Scheme = "https" + default: + return nil, nil, errMalformedURL + } + + if u.User != nil { + // User name and password are not allowed in websocket URIs. + return nil, nil, errMalformedURL + } + + req := &http.Request{ + Method: "GET", + URL: u, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + Host: u.Host, + } + + // Set the cookies present in the cookie jar of the dialer + if d.Jar != nil { + for _, cookie := range d.Jar.Cookies(u) { + req.AddCookie(cookie) + } + } + + // Set the request headers using the capitalization for names and values in + // RFC examples. Although the capitalization shouldn't matter, there are + // servers that depend on it. The Header.Set method is not used because the + // method canonicalizes the header names. + req.Header["Upgrade"] = []string{"websocket"} + req.Header["Connection"] = []string{"Upgrade"} + req.Header["Sec-WebSocket-Key"] = []string{challengeKey} + req.Header["Sec-WebSocket-Version"] = []string{"13"} + if len(d.Subprotocols) > 0 { + req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} + } + for k, vs := range requestHeader { + switch { + case k == "Host": + if len(vs) > 0 { + req.Host = vs[0] + } + case k == "Upgrade" || + k == "Connection" || + k == "Sec-Websocket-Key" || + k == "Sec-Websocket-Version" || + k == "Sec-Websocket-Extensions" || + (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): + return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) + case k == "Sec-Websocket-Protocol": + req.Header["Sec-WebSocket-Protocol"] = vs + default: + req.Header[k] = vs + } + } + + if d.EnableCompression { + req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"} + } + + var deadline time.Time + if d.HandshakeTimeout != 0 { + deadline = time.Now().Add(d.HandshakeTimeout) + } + + // Get network dial function. + netDial := d.NetDial + if netDial == nil { + netDialer := &net.Dialer{Deadline: deadline} + netDial = netDialer.Dial + } + + // If needed, wrap the dial function to set the connection deadline. + if !deadline.Equal(time.Time{}) { + forwardDial := netDial + netDial = func(network, addr string) (net.Conn, error) { + c, err := forwardDial(network, addr) + if err != nil { + return nil, err + } + err = c.SetDeadline(deadline) + if err != nil { + c.Close() + return nil, err + } + return c, nil + } + } + + // If needed, wrap the dial function to connect through a proxy. + if d.Proxy != nil { + proxyURL, err := d.Proxy(req) + if err != nil { + return nil, nil, err + } + if proxyURL != nil { + dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial)) + if err != nil { + return nil, nil, err + } + netDial = dialer.Dial + } + } + + hostPort, hostNoPort := hostPortNoPort(u) + netConn, err := netDial("tcp", hostPort) + if err != nil { + return nil, nil, err + } + + defer func() { + if netConn != nil { + netConn.Close() + } + }() + + if u.Scheme == "https" { + cfg := cloneTLSConfig(d.TLSClientConfig) + if cfg.ServerName == "" { + cfg.ServerName = hostNoPort + } + tlsConn := tls.Client(netConn, cfg) + netConn = tlsConn + if err := tlsConn.Handshake(); err != nil { + return nil, nil, err + } + if !cfg.InsecureSkipVerify { + if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { + return nil, nil, err + } + } + } + + conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize) + + if err := req.Write(netConn); err != nil { + return nil, nil, err + } + + resp, err := http.ReadResponse(conn.br, req) + if err != nil { + return nil, nil, err + } + + if d.Jar != nil { + if rc := resp.Cookies(); len(rc) > 0 { + d.Jar.SetCookies(u, rc) + } + } + + if resp.StatusCode != 101 || + !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || + !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || + resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { + // Before closing the network connection on return from this + // function, slurp up some of the response to aid application + // debugging. + buf := make([]byte, 1024) + n, _ := io.ReadFull(resp.Body, buf) + resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) + return nil, resp, ErrBadHandshake + } + + for _, ext := range parseExtensions(resp.Header) { + if ext[""] != "permessage-deflate" { + continue + } + _, snct := ext["server_no_context_takeover"] + _, cnct := ext["client_no_context_takeover"] + if !snct || !cnct { + return nil, resp, errInvalidCompression + } + conn.newCompressionWriter = compressNoContextTakeover + conn.newDecompressionReader = decompressNoContextTakeover + break + } + + resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) + conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") + + netConn.SetDeadline(time.Time{}) + netConn = nil // to avoid close in defer. + return conn, resp, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client_clone.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client_clone.go new file mode 100644 index 000000000..4f0d94372 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client_clone.go @@ -0,0 +1,16 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.8 + +package websocket + +import "crypto/tls" + +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + if cfg == nil { + return &tls.Config{} + } + return cfg.Clone() +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client_clone_legacy.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client_clone_legacy.go new file mode 100644 index 000000000..babb007fb --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/client_clone_legacy.go @@ -0,0 +1,38 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.8 + +package websocket + +import "crypto/tls" + +// cloneTLSConfig clones all public fields except the fields +// SessionTicketsDisabled and SessionTicketKey. This avoids copying the +// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a +// config in active use. +func cloneTLSConfig(cfg *tls.Config) *tls.Config { + if cfg == nil { + return &tls.Config{} + } + return &tls.Config{ + Rand: cfg.Rand, + Time: cfg.Time, + Certificates: cfg.Certificates, + NameToCertificate: cfg.NameToCertificate, + GetCertificate: cfg.GetCertificate, + RootCAs: cfg.RootCAs, + NextProtos: cfg.NextProtos, + ServerName: cfg.ServerName, + ClientAuth: cfg.ClientAuth, + ClientCAs: cfg.ClientCAs, + InsecureSkipVerify: cfg.InsecureSkipVerify, + CipherSuites: cfg.CipherSuites, + PreferServerCipherSuites: cfg.PreferServerCipherSuites, + ClientSessionCache: cfg.ClientSessionCache, + MinVersion: cfg.MinVersion, + MaxVersion: cfg.MaxVersion, + CurvePreferences: cfg.CurvePreferences, + } +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/compression.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/compression.go new file mode 100644 index 000000000..813ffb1e8 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/compression.go @@ -0,0 +1,148 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "compress/flate" + "errors" + "io" + "strings" + "sync" +) + +const ( + minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 + maxCompressionLevel = flate.BestCompression + defaultCompressionLevel = 1 +) + +var ( + flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool + flateReaderPool = sync.Pool{New: func() interface{} { + return flate.NewReader(nil) + }} +) + +func decompressNoContextTakeover(r io.Reader) io.ReadCloser { + const tail = + // Add four bytes as specified in RFC + "\x00\x00\xff\xff" + + // Add final block to squelch unexpected EOF error from flate reader. + "\x01\x00\x00\xff\xff" + + fr, _ := flateReaderPool.Get().(io.ReadCloser) + fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) + return &flateReadWrapper{fr} +} + +func isValidCompressionLevel(level int) bool { + return minCompressionLevel <= level && level <= maxCompressionLevel +} + +func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { + p := &flateWriterPools[level-minCompressionLevel] + tw := &truncWriter{w: w} + fw, _ := p.Get().(*flate.Writer) + if fw == nil { + fw, _ = flate.NewWriter(tw, level) + } else { + fw.Reset(tw) + } + return &flateWriteWrapper{fw: fw, tw: tw, p: p} +} + +// truncWriter is an io.Writer that writes all but the last four bytes of the +// stream to another io.Writer. +type truncWriter struct { + w io.WriteCloser + n int + p [4]byte +} + +func (w *truncWriter) Write(p []byte) (int, error) { + n := 0 + + // fill buffer first for simplicity. + if w.n < len(w.p) { + n = copy(w.p[w.n:], p) + p = p[n:] + w.n += n + if len(p) == 0 { + return n, nil + } + } + + m := len(p) + if m > len(w.p) { + m = len(w.p) + } + + if nn, err := w.w.Write(w.p[:m]); err != nil { + return n + nn, err + } + + copy(w.p[:], w.p[m:]) + copy(w.p[len(w.p)-m:], p[len(p)-m:]) + nn, err := w.w.Write(p[:len(p)-m]) + return n + nn, err +} + +type flateWriteWrapper struct { + fw *flate.Writer + tw *truncWriter + p *sync.Pool +} + +func (w *flateWriteWrapper) Write(p []byte) (int, error) { + if w.fw == nil { + return 0, errWriteClosed + } + return w.fw.Write(p) +} + +func (w *flateWriteWrapper) Close() error { + if w.fw == nil { + return errWriteClosed + } + err1 := w.fw.Flush() + w.p.Put(w.fw) + w.fw = nil + if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { + return errors.New("websocket: internal error, unexpected bytes at end of flate stream") + } + err2 := w.tw.w.Close() + if err1 != nil { + return err1 + } + return err2 +} + +type flateReadWrapper struct { + fr io.ReadCloser +} + +func (r *flateReadWrapper) Read(p []byte) (int, error) { + if r.fr == nil { + return 0, io.ErrClosedPipe + } + n, err := r.fr.Read(p) + if err == io.EOF { + // Preemptively place the reader back in the pool. This helps with + // scenarios where the application does not call NextReader() soon after + // this final read. + r.Close() + } + return n, err +} + +func (r *flateReadWrapper) Close() error { + if r.fr == nil { + return io.ErrClosedPipe + } + err := r.fr.Close() + flateReaderPool.Put(r.fr) + r.fr = nil + return err +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn.go new file mode 100644 index 000000000..5f46bf4a5 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn.go @@ -0,0 +1,1157 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "encoding/binary" + "errors" + "io" + "io/ioutil" + "math/rand" + "net" + "strconv" + "sync" + "time" + "unicode/utf8" +) + +const ( + // Frame header byte 0 bits from Section 5.2 of RFC 6455 + finalBit = 1 << 7 + rsv1Bit = 1 << 6 + rsv2Bit = 1 << 5 + rsv3Bit = 1 << 4 + + // Frame header byte 1 bits from Section 5.2 of RFC 6455 + maskBit = 1 << 7 + + maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask + maxControlFramePayloadSize = 125 + + writeWait = time.Second + + defaultReadBufferSize = 4096 + defaultWriteBufferSize = 4096 + + continuationFrame = 0 + noFrame = -1 +) + +// Close codes defined in RFC 6455, section 11.7. +const ( + CloseNormalClosure = 1000 + CloseGoingAway = 1001 + CloseProtocolError = 1002 + CloseUnsupportedData = 1003 + CloseNoStatusReceived = 1005 + CloseAbnormalClosure = 1006 + CloseInvalidFramePayloadData = 1007 + ClosePolicyViolation = 1008 + CloseMessageTooBig = 1009 + CloseMandatoryExtension = 1010 + CloseInternalServerErr = 1011 + CloseServiceRestart = 1012 + CloseTryAgainLater = 1013 + CloseTLSHandshake = 1015 +) + +// The message types are defined in RFC 6455, section 11.8. +const ( + // TextMessage denotes a text data message. The text message payload is + // interpreted as UTF-8 encoded text data. + TextMessage = 1 + + // BinaryMessage denotes a binary data message. + BinaryMessage = 2 + + // CloseMessage denotes a close control message. The optional message + // payload contains a numeric code and text. Use the FormatCloseMessage + // function to format a close message payload. + CloseMessage = 8 + + // PingMessage denotes a ping control message. The optional message payload + // is UTF-8 encoded text. + PingMessage = 9 + + // PongMessage denotes a pong control message. The optional message payload + // is UTF-8 encoded text. + PongMessage = 10 +) + +// ErrCloseSent is returned when the application writes a message to the +// connection after sending a close message. +var ErrCloseSent = errors.New("websocket: close sent") + +// ErrReadLimit is returned when reading a message that is larger than the +// read limit set for the connection. +var ErrReadLimit = errors.New("websocket: read limit exceeded") + +// netError satisfies the net Error interface. +type netError struct { + msg string + temporary bool + timeout bool +} + +func (e *netError) Error() string { return e.msg } +func (e *netError) Temporary() bool { return e.temporary } +func (e *netError) Timeout() bool { return e.timeout } + +// CloseError represents a close message. +type CloseError struct { + // Code is defined in RFC 6455, section 11.7. + Code int + + // Text is the optional text payload. + Text string +} + +func (e *CloseError) Error() string { + s := []byte("websocket: close ") + s = strconv.AppendInt(s, int64(e.Code), 10) + switch e.Code { + case CloseNormalClosure: + s = append(s, " (normal)"...) + case CloseGoingAway: + s = append(s, " (going away)"...) + case CloseProtocolError: + s = append(s, " (protocol error)"...) + case CloseUnsupportedData: + s = append(s, " (unsupported data)"...) + case CloseNoStatusReceived: + s = append(s, " (no status)"...) + case CloseAbnormalClosure: + s = append(s, " (abnormal closure)"...) + case CloseInvalidFramePayloadData: + s = append(s, " (invalid payload data)"...) + case ClosePolicyViolation: + s = append(s, " (policy violation)"...) + case CloseMessageTooBig: + s = append(s, " (message too big)"...) + case CloseMandatoryExtension: + s = append(s, " (mandatory extension missing)"...) + case CloseInternalServerErr: + s = append(s, " (internal server error)"...) + case CloseTLSHandshake: + s = append(s, " (TLS handshake error)"...) + } + if e.Text != "" { + s = append(s, ": "...) + s = append(s, e.Text...) + } + return string(s) +} + +// IsCloseError returns boolean indicating whether the error is a *CloseError +// with one of the specified codes. +func IsCloseError(err error, codes ...int) bool { + if e, ok := err.(*CloseError); ok { + for _, code := range codes { + if e.Code == code { + return true + } + } + } + return false +} + +// IsUnexpectedCloseError returns boolean indicating whether the error is a +// *CloseError with a code not in the list of expected codes. +func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { + if e, ok := err.(*CloseError); ok { + for _, code := range expectedCodes { + if e.Code == code { + return false + } + } + return true + } + return false +} + +var ( + errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} + errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} + errBadWriteOpCode = errors.New("websocket: bad write message type") + errWriteClosed = errors.New("websocket: write closed") + errInvalidControlFrame = errors.New("websocket: invalid control frame") +) + +func newMaskKey() [4]byte { + n := rand.Uint32() + return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} +} + +func hideTempErr(err error) error { + if e, ok := err.(net.Error); ok && e.Temporary() { + err = &netError{msg: e.Error(), timeout: e.Timeout()} + } + return err +} + +func isControl(frameType int) bool { + return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage +} + +func isData(frameType int) bool { + return frameType == TextMessage || frameType == BinaryMessage +} + +var validReceivedCloseCodes = map[int]bool{ + // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number + + CloseNormalClosure: true, + CloseGoingAway: true, + CloseProtocolError: true, + CloseUnsupportedData: true, + CloseNoStatusReceived: false, + CloseAbnormalClosure: false, + CloseInvalidFramePayloadData: true, + ClosePolicyViolation: true, + CloseMessageTooBig: true, + CloseMandatoryExtension: true, + CloseInternalServerErr: true, + CloseServiceRestart: true, + CloseTryAgainLater: true, + CloseTLSHandshake: false, +} + +func isValidReceivedCloseCode(code int) bool { + return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) +} + +// The Conn type represents a WebSocket connection. +type Conn struct { + conn net.Conn + isServer bool + subprotocol string + + // Write fields + mu chan bool // used as mutex to protect write to conn + writeBuf []byte // frame is constructed in this buffer. + writeDeadline time.Time + writer io.WriteCloser // the current writer returned to the application + isWriting bool // for best-effort concurrent write detection + + writeErrMu sync.Mutex + writeErr error + + enableWriteCompression bool + compressionLevel int + newCompressionWriter func(io.WriteCloser, int) io.WriteCloser + + // Read fields + reader io.ReadCloser // the current reader returned to the application + readErr error + br *bufio.Reader + readRemaining int64 // bytes remaining in current frame. + readFinal bool // true the current message has more frames. + readLength int64 // Message size. + readLimit int64 // Maximum message size. + readMaskPos int + readMaskKey [4]byte + handlePong func(string) error + handlePing func(string) error + handleClose func(int, string) error + readErrCount int + messageReader *messageReader // the current low-level reader + + readDecompress bool // whether last read frame had RSV1 set + newDecompressionReader func(io.Reader) io.ReadCloser +} + +func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn { + return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil) +} + +type writeHook struct { + p []byte +} + +func (wh *writeHook) Write(p []byte) (int, error) { + wh.p = p + return len(p), nil +} + +func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn { + mu := make(chan bool, 1) + mu <- true + + var br *bufio.Reader + if readBufferSize == 0 && brw != nil && brw.Reader != nil { + // Reuse the supplied bufio.Reader if the buffer has a useful size. + // This code assumes that peek on a reader returns + // bufio.Reader.buf[:0]. + brw.Reader.Reset(conn) + if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 { + br = brw.Reader + } + } + if br == nil { + if readBufferSize == 0 { + readBufferSize = defaultReadBufferSize + } + if readBufferSize < maxControlFramePayloadSize { + readBufferSize = maxControlFramePayloadSize + } + br = bufio.NewReaderSize(conn, readBufferSize) + } + + var writeBuf []byte + if writeBufferSize == 0 && brw != nil && brw.Writer != nil { + // Use the bufio.Writer's buffer if the buffer has a useful size. This + // code assumes that bufio.Writer.buf[:1] is passed to the + // bufio.Writer's underlying writer. + var wh writeHook + brw.Writer.Reset(&wh) + brw.Writer.WriteByte(0) + brw.Flush() + if cap(wh.p) >= maxFrameHeaderSize+256 { + writeBuf = wh.p[:cap(wh.p)] + } + } + + if writeBuf == nil { + if writeBufferSize == 0 { + writeBufferSize = defaultWriteBufferSize + } + writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize) + } + + c := &Conn{ + isServer: isServer, + br: br, + conn: conn, + mu: mu, + readFinal: true, + writeBuf: writeBuf, + enableWriteCompression: true, + compressionLevel: defaultCompressionLevel, + } + c.SetCloseHandler(nil) + c.SetPingHandler(nil) + c.SetPongHandler(nil) + return c +} + +// Subprotocol returns the negotiated protocol for the connection. +func (c *Conn) Subprotocol() string { + return c.subprotocol +} + +// Close closes the underlying network connection without sending or waiting +// for a close message. +func (c *Conn) Close() error { + return c.conn.Close() +} + +// LocalAddr returns the local network address. +func (c *Conn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +// RemoteAddr returns the remote network address. +func (c *Conn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +// Write methods + +func (c *Conn) writeFatal(err error) error { + err = hideTempErr(err) + c.writeErrMu.Lock() + if c.writeErr == nil { + c.writeErr = err + } + c.writeErrMu.Unlock() + return err +} + +func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { + <-c.mu + defer func() { c.mu <- true }() + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + if err != nil { + return err + } + + c.conn.SetWriteDeadline(deadline) + if len(buf1) == 0 { + _, err = c.conn.Write(buf0) + } else { + err = c.writeBufs(buf0, buf1) + } + if err != nil { + return c.writeFatal(err) + } + if frameType == CloseMessage { + c.writeFatal(ErrCloseSent) + } + return nil +} + +// WriteControl writes a control message with the given deadline. The allowed +// message types are CloseMessage, PingMessage and PongMessage. +func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { + if !isControl(messageType) { + return errBadWriteOpCode + } + if len(data) > maxControlFramePayloadSize { + return errInvalidControlFrame + } + + b0 := byte(messageType) | finalBit + b1 := byte(len(data)) + if !c.isServer { + b1 |= maskBit + } + + buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) + buf = append(buf, b0, b1) + + if c.isServer { + buf = append(buf, data...) + } else { + key := newMaskKey() + buf = append(buf, key[:]...) + buf = append(buf, data...) + maskBytes(key, 0, buf[6:]) + } + + d := time.Hour * 1000 + if !deadline.IsZero() { + d = deadline.Sub(time.Now()) + if d < 0 { + return errWriteTimeout + } + } + + timer := time.NewTimer(d) + select { + case <-c.mu: + timer.Stop() + case <-timer.C: + return errWriteTimeout + } + defer func() { c.mu <- true }() + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + if err != nil { + return err + } + + c.conn.SetWriteDeadline(deadline) + _, err = c.conn.Write(buf) + if err != nil { + return c.writeFatal(err) + } + if messageType == CloseMessage { + c.writeFatal(ErrCloseSent) + } + return err +} + +func (c *Conn) prepWrite(messageType int) error { + // Close previous writer if not already closed by the application. It's + // probably better to return an error in this situation, but we cannot + // change this without breaking existing applications. + if c.writer != nil { + c.writer.Close() + c.writer = nil + } + + if !isControl(messageType) && !isData(messageType) { + return errBadWriteOpCode + } + + c.writeErrMu.Lock() + err := c.writeErr + c.writeErrMu.Unlock() + return err +} + +// NextWriter returns a writer for the next message to send. The writer's Close +// method flushes the complete message to the network. +// +// There can be at most one open writer on a connection. NextWriter closes the +// previous writer if the application has not already done so. +// +// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and +// PongMessage) are supported. +func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { + if err := c.prepWrite(messageType); err != nil { + return nil, err + } + + mw := &messageWriter{ + c: c, + frameType: messageType, + pos: maxFrameHeaderSize, + } + c.writer = mw + if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { + w := c.newCompressionWriter(c.writer, c.compressionLevel) + mw.compress = true + c.writer = w + } + return c.writer, nil +} + +type messageWriter struct { + c *Conn + compress bool // whether next call to flushFrame should set RSV1 + pos int // end of data in writeBuf. + frameType int // type of the current frame. + err error +} + +func (w *messageWriter) fatal(err error) error { + if w.err != nil { + w.err = err + w.c.writer = nil + } + return err +} + +// flushFrame writes buffered data and extra as a frame to the network. The +// final argument indicates that this is the last frame in the message. +func (w *messageWriter) flushFrame(final bool, extra []byte) error { + c := w.c + length := w.pos - maxFrameHeaderSize + len(extra) + + // Check for invalid control frames. + if isControl(w.frameType) && + (!final || length > maxControlFramePayloadSize) { + return w.fatal(errInvalidControlFrame) + } + + b0 := byte(w.frameType) + if final { + b0 |= finalBit + } + if w.compress { + b0 |= rsv1Bit + } + w.compress = false + + b1 := byte(0) + if !c.isServer { + b1 |= maskBit + } + + // Assume that the frame starts at beginning of c.writeBuf. + framePos := 0 + if c.isServer { + // Adjust up if mask not included in the header. + framePos = 4 + } + + switch { + case length >= 65536: + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | 127 + binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) + case length > 125: + framePos += 6 + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | 126 + binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) + default: + framePos += 8 + c.writeBuf[framePos] = b0 + c.writeBuf[framePos+1] = b1 | byte(length) + } + + if !c.isServer { + key := newMaskKey() + copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) + maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) + if len(extra) > 0 { + return c.writeFatal(errors.New("websocket: internal error, extra used in client mode")) + } + } + + // Write the buffers to the connection with best-effort detection of + // concurrent writes. See the concurrency section in the package + // documentation for more info. + + if c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = true + + err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra) + + if !c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = false + + if err != nil { + return w.fatal(err) + } + + if final { + c.writer = nil + return nil + } + + // Setup for next frame. + w.pos = maxFrameHeaderSize + w.frameType = continuationFrame + return nil +} + +func (w *messageWriter) ncopy(max int) (int, error) { + n := len(w.c.writeBuf) - w.pos + if n <= 0 { + if err := w.flushFrame(false, nil); err != nil { + return 0, err + } + n = len(w.c.writeBuf) - w.pos + } + if n > max { + n = max + } + return n, nil +} + +func (w *messageWriter) Write(p []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + + if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { + // Don't buffer large messages. + err := w.flushFrame(false, p) + if err != nil { + return 0, err + } + return len(p), nil + } + + nn := len(p) + for len(p) > 0 { + n, err := w.ncopy(len(p)) + if err != nil { + return 0, err + } + copy(w.c.writeBuf[w.pos:], p[:n]) + w.pos += n + p = p[n:] + } + return nn, nil +} + +func (w *messageWriter) WriteString(p string) (int, error) { + if w.err != nil { + return 0, w.err + } + + nn := len(p) + for len(p) > 0 { + n, err := w.ncopy(len(p)) + if err != nil { + return 0, err + } + copy(w.c.writeBuf[w.pos:], p[:n]) + w.pos += n + p = p[n:] + } + return nn, nil +} + +func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { + if w.err != nil { + return 0, w.err + } + for { + if w.pos == len(w.c.writeBuf) { + err = w.flushFrame(false, nil) + if err != nil { + break + } + } + var n int + n, err = r.Read(w.c.writeBuf[w.pos:]) + w.pos += n + nn += int64(n) + if err != nil { + if err == io.EOF { + err = nil + } + break + } + } + return nn, err +} + +func (w *messageWriter) Close() error { + if w.err != nil { + return w.err + } + if err := w.flushFrame(true, nil); err != nil { + return err + } + w.err = errWriteClosed + return nil +} + +// WritePreparedMessage writes prepared message into connection. +func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { + frameType, frameData, err := pm.frame(prepareKey{ + isServer: c.isServer, + compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), + compressionLevel: c.compressionLevel, + }) + if err != nil { + return err + } + if c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = true + err = c.write(frameType, c.writeDeadline, frameData, nil) + if !c.isWriting { + panic("concurrent write to websocket connection") + } + c.isWriting = false + return err +} + +// WriteMessage is a helper method for getting a writer using NextWriter, +// writing the message and closing the writer. +func (c *Conn) WriteMessage(messageType int, data []byte) error { + + if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { + // Fast path with no allocations and single frame. + + if err := c.prepWrite(messageType); err != nil { + return err + } + mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} + n := copy(c.writeBuf[mw.pos:], data) + mw.pos += n + data = data[n:] + return mw.flushFrame(true, data) + } + + w, err := c.NextWriter(messageType) + if err != nil { + return err + } + if _, err = w.Write(data); err != nil { + return err + } + return w.Close() +} + +// SetWriteDeadline sets the write deadline on the underlying network +// connection. After a write has timed out, the websocket state is corrupt and +// all future writes will return an error. A zero value for t means writes will +// not time out. +func (c *Conn) SetWriteDeadline(t time.Time) error { + c.writeDeadline = t + return nil +} + +// Read methods + +func (c *Conn) advanceFrame() (int, error) { + // 1. Skip remainder of previous frame. + + if c.readRemaining > 0 { + if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil { + return noFrame, err + } + } + + // 2. Read and parse first two bytes of frame header. + + p, err := c.read(2) + if err != nil { + return noFrame, err + } + + final := p[0]&finalBit != 0 + frameType := int(p[0] & 0xf) + mask := p[1]&maskBit != 0 + c.readRemaining = int64(p[1] & 0x7f) + + c.readDecompress = false + if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { + c.readDecompress = true + p[0] &^= rsv1Bit + } + + if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 { + return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16)) + } + + switch frameType { + case CloseMessage, PingMessage, PongMessage: + if c.readRemaining > maxControlFramePayloadSize { + return noFrame, c.handleProtocolError("control frame length > 125") + } + if !final { + return noFrame, c.handleProtocolError("control frame not final") + } + case TextMessage, BinaryMessage: + if !c.readFinal { + return noFrame, c.handleProtocolError("message start before final message frame") + } + c.readFinal = final + case continuationFrame: + if c.readFinal { + return noFrame, c.handleProtocolError("continuation after final message frame") + } + c.readFinal = final + default: + return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) + } + + // 3. Read and parse frame length. + + switch c.readRemaining { + case 126: + p, err := c.read(2) + if err != nil { + return noFrame, err + } + c.readRemaining = int64(binary.BigEndian.Uint16(p)) + case 127: + p, err := c.read(8) + if err != nil { + return noFrame, err + } + c.readRemaining = int64(binary.BigEndian.Uint64(p)) + } + + // 4. Handle frame masking. + + if mask != c.isServer { + return noFrame, c.handleProtocolError("incorrect mask flag") + } + + if mask { + c.readMaskPos = 0 + p, err := c.read(len(c.readMaskKey)) + if err != nil { + return noFrame, err + } + copy(c.readMaskKey[:], p) + } + + // 5. For text and binary messages, enforce read limit and return. + + if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { + + c.readLength += c.readRemaining + if c.readLimit > 0 && c.readLength > c.readLimit { + c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) + return noFrame, ErrReadLimit + } + + return frameType, nil + } + + // 6. Read control frame payload. + + var payload []byte + if c.readRemaining > 0 { + payload, err = c.read(int(c.readRemaining)) + c.readRemaining = 0 + if err != nil { + return noFrame, err + } + if c.isServer { + maskBytes(c.readMaskKey, 0, payload) + } + } + + // 7. Process control frame payload. + + switch frameType { + case PongMessage: + if err := c.handlePong(string(payload)); err != nil { + return noFrame, err + } + case PingMessage: + if err := c.handlePing(string(payload)); err != nil { + return noFrame, err + } + case CloseMessage: + closeCode := CloseNoStatusReceived + closeText := "" + if len(payload) >= 2 { + closeCode = int(binary.BigEndian.Uint16(payload)) + if !isValidReceivedCloseCode(closeCode) { + return noFrame, c.handleProtocolError("invalid close code") + } + closeText = string(payload[2:]) + if !utf8.ValidString(closeText) { + return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") + } + } + if err := c.handleClose(closeCode, closeText); err != nil { + return noFrame, err + } + return noFrame, &CloseError{Code: closeCode, Text: closeText} + } + + return frameType, nil +} + +func (c *Conn) handleProtocolError(message string) error { + c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait)) + return errors.New("websocket: " + message) +} + +// NextReader returns the next data message received from the peer. The +// returned messageType is either TextMessage or BinaryMessage. +// +// There can be at most one open reader on a connection. NextReader discards +// the previous message if the application has not already consumed it. +// +// Applications must break out of the application's read loop when this method +// returns a non-nil error value. Errors returned from this method are +// permanent. Once this method returns a non-nil error, all subsequent calls to +// this method return the same error. +func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { + // Close previous reader, only relevant for decompression. + if c.reader != nil { + c.reader.Close() + c.reader = nil + } + + c.messageReader = nil + c.readLength = 0 + + for c.readErr == nil { + frameType, err := c.advanceFrame() + if err != nil { + c.readErr = hideTempErr(err) + break + } + if frameType == TextMessage || frameType == BinaryMessage { + c.messageReader = &messageReader{c} + c.reader = c.messageReader + if c.readDecompress { + c.reader = c.newDecompressionReader(c.reader) + } + return frameType, c.reader, nil + } + } + + // Applications that do handle the error returned from this method spin in + // tight loop on connection failure. To help application developers detect + // this error, panic on repeated reads to the failed connection. + c.readErrCount++ + if c.readErrCount >= 1000 { + panic("repeated read on failed websocket connection") + } + + return noFrame, nil, c.readErr +} + +type messageReader struct{ c *Conn } + +func (r *messageReader) Read(b []byte) (int, error) { + c := r.c + if c.messageReader != r { + return 0, io.EOF + } + + for c.readErr == nil { + + if c.readRemaining > 0 { + if int64(len(b)) > c.readRemaining { + b = b[:c.readRemaining] + } + n, err := c.br.Read(b) + c.readErr = hideTempErr(err) + if c.isServer { + c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) + } + c.readRemaining -= int64(n) + if c.readRemaining > 0 && c.readErr == io.EOF { + c.readErr = errUnexpectedEOF + } + return n, c.readErr + } + + if c.readFinal { + c.messageReader = nil + return 0, io.EOF + } + + frameType, err := c.advanceFrame() + switch { + case err != nil: + c.readErr = hideTempErr(err) + case frameType == TextMessage || frameType == BinaryMessage: + c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") + } + } + + err := c.readErr + if err == io.EOF && c.messageReader == r { + err = errUnexpectedEOF + } + return 0, err +} + +func (r *messageReader) Close() error { + return nil +} + +// ReadMessage is a helper method for getting a reader using NextReader and +// reading from that reader to a buffer. +func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { + var r io.Reader + messageType, r, err = c.NextReader() + if err != nil { + return messageType, nil, err + } + p, err = ioutil.ReadAll(r) + return messageType, p, err +} + +// SetReadDeadline sets the read deadline on the underlying network connection. +// After a read has timed out, the websocket connection state is corrupt and +// all future reads will return an error. A zero value for t means reads will +// not time out. +func (c *Conn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +// SetReadLimit sets the maximum size for a message read from the peer. If a +// message exceeds the limit, the connection sends a close message to the peer +// and returns ErrReadLimit to the application. +func (c *Conn) SetReadLimit(limit int64) { + c.readLimit = limit +} + +// CloseHandler returns the current close handler +func (c *Conn) CloseHandler() func(code int, text string) error { + return c.handleClose +} + +// SetCloseHandler sets the handler for close messages received from the peer. +// The code argument to h is the received close code or CloseNoStatusReceived +// if the close message is empty. The default close handler sends a close +// message back to the peer. +// +// The handler function is called from the NextReader, ReadMessage and message +// reader Read methods. The application must read the connection to process +// close messages as described in the section on Control Messages above. +// +// The connection read methods return a CloseError when a close message is +// received. Most applications should handle close messages as part of their +// normal error handling. Applications should only set a close handler when the +// application must perform some action before sending a close message back to +// the peer. +func (c *Conn) SetCloseHandler(h func(code int, text string) error) { + if h == nil { + h = func(code int, text string) error { + message := FormatCloseMessage(code, "") + c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)) + return nil + } + } + c.handleClose = h +} + +// PingHandler returns the current ping handler +func (c *Conn) PingHandler() func(appData string) error { + return c.handlePing +} + +// SetPingHandler sets the handler for ping messages received from the peer. +// The appData argument to h is the PING message application data. The default +// ping handler sends a pong to the peer. +// +// The handler function is called from the NextReader, ReadMessage and message +// reader Read methods. The application must read the connection to process +// ping messages as described in the section on Control Messages above. +func (c *Conn) SetPingHandler(h func(appData string) error) { + if h == nil { + h = func(message string) error { + err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) + if err == ErrCloseSent { + return nil + } else if e, ok := err.(net.Error); ok && e.Temporary() { + return nil + } + return err + } + } + c.handlePing = h +} + +// PongHandler returns the current pong handler +func (c *Conn) PongHandler() func(appData string) error { + return c.handlePong +} + +// SetPongHandler sets the handler for pong messages received from the peer. +// The appData argument to h is the PONG message application data. The default +// pong handler does nothing. +// +// The handler function is called from the NextReader, ReadMessage and message +// reader Read methods. The application must read the connection to process +// pong messages as described in the section on Control Messages above. +func (c *Conn) SetPongHandler(h func(appData string) error) { + if h == nil { + h = func(string) error { return nil } + } + c.handlePong = h +} + +// UnderlyingConn returns the internal net.Conn. This can be used to further +// modifications to connection specific flags. +func (c *Conn) UnderlyingConn() net.Conn { + return c.conn +} + +// EnableWriteCompression enables and disables write compression of +// subsequent text and binary messages. This function is a noop if +// compression was not negotiated with the peer. +func (c *Conn) EnableWriteCompression(enable bool) { + c.enableWriteCompression = enable +} + +// SetCompressionLevel sets the flate compression level for subsequent text and +// binary messages. This function is a noop if compression was not negotiated +// with the peer. See the compress/flate package for a description of +// compression levels. +func (c *Conn) SetCompressionLevel(level int) error { + if !isValidCompressionLevel(level) { + return errors.New("websocket: invalid compression level") + } + c.compressionLevel = level + return nil +} + +// FormatCloseMessage formats closeCode and text as a WebSocket close message. +// An empty message is returned for code CloseNoStatusReceived. +func FormatCloseMessage(closeCode int, text string) []byte { + if closeCode == CloseNoStatusReceived { + // Return empty message because it's illegal to send + // CloseNoStatusReceived. Return non-nil value in case application + // checks for nil. + return []byte{} + } + buf := make([]byte, 2+len(text)) + binary.BigEndian.PutUint16(buf, uint16(closeCode)) + copy(buf[2:], text) + return buf +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_read.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_read.go new file mode 100644 index 000000000..1ea15059e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_read.go @@ -0,0 +1,18 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.5 + +package websocket + +import "io" + +func (c *Conn) read(n int) ([]byte, error) { + p, err := c.br.Peek(n) + if err == io.EOF { + err = errUnexpectedEOF + } + c.br.Discard(len(p)) + return p, err +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_read_legacy.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_read_legacy.go new file mode 100644 index 000000000..018541cf6 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_read_legacy.go @@ -0,0 +1,21 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +package websocket + +import "io" + +func (c *Conn) read(n int) ([]byte, error) { + p, err := c.br.Peek(n) + if err == io.EOF { + err = errUnexpectedEOF + } + if len(p) > 0 { + // advance over the bytes just read + io.ReadFull(c.br, p) + } + return p, err +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_write.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_write.go new file mode 100644 index 000000000..a509a21f8 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_write.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.8 + +package websocket + +import "net" + +func (c *Conn) writeBufs(bufs ...[]byte) error { + b := net.Buffers(bufs) + _, err := b.WriteTo(c.conn) + return err +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_write_legacy.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_write_legacy.go new file mode 100644 index 000000000..37edaff5a --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/conn_write_legacy.go @@ -0,0 +1,18 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.8 + +package websocket + +func (c *Conn) writeBufs(bufs ...[]byte) error { + for _, buf := range bufs { + if len(buf) > 0 { + if _, err := c.conn.Write(buf); err != nil { + return err + } + } + } + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/doc.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/doc.go new file mode 100644 index 000000000..dcce1a63c --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/doc.go @@ -0,0 +1,180 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package websocket implements the WebSocket protocol defined in RFC 6455. +// +// Overview +// +// The Conn type represents a WebSocket connection. A server application calls +// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn: +// +// var upgrader = websocket.Upgrader{ +// ReadBufferSize: 1024, +// WriteBufferSize: 1024, +// } +// +// func handler(w http.ResponseWriter, r *http.Request) { +// conn, err := upgrader.Upgrade(w, r, nil) +// if err != nil { +// log.Println(err) +// return +// } +// ... Use conn to send and receive messages. +// } +// +// Call the connection's WriteMessage and ReadMessage methods to send and +// receive messages as a slice of bytes. This snippet of code shows how to echo +// messages using these methods: +// +// for { +// messageType, p, err := conn.ReadMessage() +// if err != nil { +// log.Println(err) +// return +// } +// if err := conn.WriteMessage(messageType, p); err != nil { +// log.Println(err) +// return +// } +// } +// +// In above snippet of code, p is a []byte and messageType is an int with value +// websocket.BinaryMessage or websocket.TextMessage. +// +// An application can also send and receive messages using the io.WriteCloser +// and io.Reader interfaces. To send a message, call the connection NextWriter +// method to get an io.WriteCloser, write the message to the writer and close +// the writer when done. To receive a message, call the connection NextReader +// method to get an io.Reader and read until io.EOF is returned. This snippet +// shows how to echo messages using the NextWriter and NextReader methods: +// +// for { +// messageType, r, err := conn.NextReader() +// if err != nil { +// return +// } +// w, err := conn.NextWriter(messageType) +// if err != nil { +// return err +// } +// if _, err := io.Copy(w, r); err != nil { +// return err +// } +// if err := w.Close(); err != nil { +// return err +// } +// } +// +// Data Messages +// +// The WebSocket protocol distinguishes between text and binary data messages. +// Text messages are interpreted as UTF-8 encoded text. The interpretation of +// binary messages is left to the application. +// +// This package uses the TextMessage and BinaryMessage integer constants to +// identify the two data message types. The ReadMessage and NextReader methods +// return the type of the received message. The messageType argument to the +// WriteMessage and NextWriter methods specifies the type of a sent message. +// +// It is the application's responsibility to ensure that text messages are +// valid UTF-8 encoded text. +// +// Control Messages +// +// The WebSocket protocol defines three types of control messages: close, ping +// and pong. Call the connection WriteControl, WriteMessage or NextWriter +// methods to send a control message to the peer. +// +// Connections handle received close messages by calling the handler function +// set with the SetCloseHandler method and by returning a *CloseError from the +// NextReader, ReadMessage or the message Read method. The default close +// handler sends a close message to the peer. +// +// Connections handle received ping messages by calling the handler function +// set with the SetPingHandler method. The default ping handler sends a pong +// message to the peer. +// +// Connections handle received pong messages by calling the handler function +// set with the SetPongHandler method. The default pong handler does nothing. +// If an application sends ping messages, then the application should set a +// pong handler to receive the corresponding pong. +// +// The control message handler functions are called from the NextReader, +// ReadMessage and message reader Read methods. The default close and ping +// handlers can block these methods for a short time when the handler writes to +// the connection. +// +// The application must read the connection to process close, ping and pong +// messages sent from the peer. If the application is not otherwise interested +// in messages from the peer, then the application should start a goroutine to +// read and discard messages from the peer. A simple example is: +// +// func readLoop(c *websocket.Conn) { +// for { +// if _, _, err := c.NextReader(); err != nil { +// c.Close() +// break +// } +// } +// } +// +// Concurrency +// +// Connections support one concurrent reader and one concurrent writer. +// +// Applications are responsible for ensuring that no more than one goroutine +// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, +// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and +// that no more than one goroutine calls the read methods (NextReader, +// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) +// concurrently. +// +// The Close and WriteControl methods can be called concurrently with all other +// methods. +// +// Origin Considerations +// +// Web browsers allow Javascript applications to open a WebSocket connection to +// any host. It's up to the server to enforce an origin policy using the Origin +// request header sent by the browser. +// +// The Upgrader calls the function specified in the CheckOrigin field to check +// the origin. If the CheckOrigin function returns false, then the Upgrade +// method fails the WebSocket handshake with HTTP status 403. +// +// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail +// the handshake if the Origin request header is present and the Origin host is +// not equal to the Host request header. +// +// The deprecated package-level Upgrade function does not perform origin +// checking. The application is responsible for checking the Origin header +// before calling the Upgrade function. +// +// Compression EXPERIMENTAL +// +// Per message compression extensions (RFC 7692) are experimentally supported +// by this package in a limited capacity. Setting the EnableCompression option +// to true in Dialer or Upgrader will attempt to negotiate per message deflate +// support. +// +// var upgrader = websocket.Upgrader{ +// EnableCompression: true, +// } +// +// If compression was successfully negotiated with the connection's peer, any +// message received in compressed form will be automatically decompressed. +// All Read methods will return uncompressed bytes. +// +// Per message compression of messages written to a connection can be enabled +// or disabled by calling the corresponding Conn method: +// +// conn.EnableWriteCompression(false) +// +// Currently this package does not support compression with "context takeover". +// This means that messages must be compressed and decompressed in isolation, +// without retaining sliding window or dictionary state across messages. For +// more details refer to RFC 7692. +// +// Use of compression is experimental and may result in decreased performance. +package websocket diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/json.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/json.go new file mode 100644 index 000000000..dc2c1f641 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/json.go @@ -0,0 +1,60 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "encoding/json" + "io" +) + +// WriteJSON writes the JSON encoding of v as a message. +// +// Deprecated: Use c.WriteJSON instead. +func WriteJSON(c *Conn, v interface{}) error { + return c.WriteJSON(v) +} + +// WriteJSON writes the JSON encoding of v as a message. +// +// See the documentation for encoding/json Marshal for details about the +// conversion of Go values to JSON. +func (c *Conn) WriteJSON(v interface{}) error { + w, err := c.NextWriter(TextMessage) + if err != nil { + return err + } + err1 := json.NewEncoder(w).Encode(v) + err2 := w.Close() + if err1 != nil { + return err1 + } + return err2 +} + +// ReadJSON reads the next JSON-encoded message from the connection and stores +// it in the value pointed to by v. +// +// Deprecated: Use c.ReadJSON instead. +func ReadJSON(c *Conn, v interface{}) error { + return c.ReadJSON(v) +} + +// ReadJSON reads the next JSON-encoded message from the connection and stores +// it in the value pointed to by v. +// +// See the documentation for the encoding/json Unmarshal function for details +// about the conversion of JSON to a Go value. +func (c *Conn) ReadJSON(v interface{}) error { + _, r, err := c.NextReader() + if err != nil { + return err + } + err = json.NewDecoder(r).Decode(v) + if err == io.EOF { + // One value is expected in the message. + err = io.ErrUnexpectedEOF + } + return err +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/mask.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/mask.go new file mode 100644 index 000000000..577fce9ef --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/mask.go @@ -0,0 +1,54 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// +build !appengine + +package websocket + +import "unsafe" + +const wordSize = int(unsafe.Sizeof(uintptr(0))) + +func maskBytes(key [4]byte, pos int, b []byte) int { + // Mask one byte at a time for small buffers. + if len(b) < 2*wordSize { + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + return pos & 3 + } + + // Mask one byte at a time to word boundary. + if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { + n = wordSize - n + for i := range b[:n] { + b[i] ^= key[pos&3] + pos++ + } + b = b[n:] + } + + // Create aligned word size key. + var k [wordSize]byte + for i := range k { + k[i] = key[(pos+i)&3] + } + kw := *(*uintptr)(unsafe.Pointer(&k)) + + // Mask one word at a time. + n := (len(b) / wordSize) * wordSize + for i := 0; i < n; i += wordSize { + *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw + } + + // Mask one byte at a time for remaining bytes. + b = b[n:] + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + + return pos & 3 +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/mask_safe.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/mask_safe.go new file mode 100644 index 000000000..2aac060e5 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/mask_safe.go @@ -0,0 +1,15 @@ +// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// +build appengine + +package websocket + +func maskBytes(key [4]byte, pos int, b []byte) int { + for i := range b { + b[i] ^= key[pos&3] + pos++ + } + return pos & 3 +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/prepared.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/prepared.go new file mode 100644 index 000000000..1efffbd1e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/prepared.go @@ -0,0 +1,103 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bytes" + "net" + "sync" + "time" +) + +// PreparedMessage caches on the wire representations of a message payload. +// Use PreparedMessage to efficiently send a message payload to multiple +// connections. PreparedMessage is especially useful when compression is used +// because the CPU and memory expensive compression operation can be executed +// once for a given set of compression options. +type PreparedMessage struct { + messageType int + data []byte + err error + mu sync.Mutex + frames map[prepareKey]*preparedFrame +} + +// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. +type prepareKey struct { + isServer bool + compress bool + compressionLevel int +} + +// preparedFrame contains data in wire representation. +type preparedFrame struct { + once sync.Once + data []byte +} + +// NewPreparedMessage returns an initialized PreparedMessage. You can then send +// it to connection using WritePreparedMessage method. Valid wire +// representation will be calculated lazily only once for a set of current +// connection options. +func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { + pm := &PreparedMessage{ + messageType: messageType, + frames: make(map[prepareKey]*preparedFrame), + data: data, + } + + // Prepare a plain server frame. + _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) + if err != nil { + return nil, err + } + + // To protect against caller modifying the data argument, remember the data + // copied to the plain server frame. + pm.data = frameData[len(frameData)-len(data):] + return pm, nil +} + +func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { + pm.mu.Lock() + frame, ok := pm.frames[key] + if !ok { + frame = &preparedFrame{} + pm.frames[key] = frame + } + pm.mu.Unlock() + + var err error + frame.once.Do(func() { + // Prepare a frame using a 'fake' connection. + // TODO: Refactor code in conn.go to allow more direct construction of + // the frame. + mu := make(chan bool, 1) + mu <- true + var nc prepareConn + c := &Conn{ + conn: &nc, + mu: mu, + isServer: key.isServer, + compressionLevel: key.compressionLevel, + enableWriteCompression: true, + writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), + } + if key.compress { + c.newCompressionWriter = compressNoContextTakeover + } + err = c.WriteMessage(pm.messageType, pm.data) + frame.data = nc.buf.Bytes() + }) + return pm.messageType, frame.data, err +} + +type prepareConn struct { + buf bytes.Buffer + net.Conn +} + +func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } +func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/proxy.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/proxy.go new file mode 100644 index 000000000..bf2478e43 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/proxy.go @@ -0,0 +1,77 @@ +// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "encoding/base64" + "errors" + "net" + "net/http" + "net/url" + "strings" +) + +type netDialerFunc func(network, addr string) (net.Conn, error) + +func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { + return fn(network, addr) +} + +func init() { + proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { + return &httpProxyDialer{proxyURL: proxyURL, fowardDial: forwardDialer.Dial}, nil + }) +} + +type httpProxyDialer struct { + proxyURL *url.URL + fowardDial func(network, addr string) (net.Conn, error) +} + +func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) { + hostPort, _ := hostPortNoPort(hpd.proxyURL) + conn, err := hpd.fowardDial(network, hostPort) + if err != nil { + return nil, err + } + + connectHeader := make(http.Header) + if user := hpd.proxyURL.User; user != nil { + proxyUser := user.Username() + if proxyPassword, passwordSet := user.Password(); passwordSet { + credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) + connectHeader.Set("Proxy-Authorization", "Basic "+credential) + } + } + + connectReq := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Opaque: addr}, + Host: addr, + Header: connectHeader, + } + + if err := connectReq.Write(conn); err != nil { + conn.Close() + return nil, err + } + + // Read response. It's OK to use and discard buffered reader here becaue + // the remote server does not speak until spoken to. + br := bufio.NewReader(conn) + resp, err := http.ReadResponse(br, connectReq) + if err != nil { + conn.Close() + return nil, err + } + + if resp.StatusCode != 200 { + conn.Close() + f := strings.SplitN(resp.Status, " ", 2) + return nil, errors.New(f[1]) + } + return conn, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/server.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/server.go new file mode 100644 index 000000000..aee270528 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/server.go @@ -0,0 +1,298 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "errors" + "net" + "net/http" + "net/url" + "strings" + "time" +) + +// HandshakeError describes an error with the handshake from the peer. +type HandshakeError struct { + message string +} + +func (e HandshakeError) Error() string { return e.message } + +// Upgrader specifies parameters for upgrading an HTTP connection to a +// WebSocket connection. +type Upgrader struct { + // HandshakeTimeout specifies the duration for the handshake to complete. + HandshakeTimeout time.Duration + + // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer + // size is zero, then buffers allocated by the HTTP server are used. The + // I/O buffer sizes do not limit the size of the messages that can be sent + // or received. + ReadBufferSize, WriteBufferSize int + + // Subprotocols specifies the server's supported protocols in order of + // preference. If this field is set, then the Upgrade method negotiates a + // subprotocol by selecting the first match in this list with a protocol + // requested by the client. + Subprotocols []string + + // Error specifies the function for generating HTTP error responses. If Error + // is nil, then http.Error is used to generate the HTTP response. + Error func(w http.ResponseWriter, r *http.Request, status int, reason error) + + // CheckOrigin returns true if the request Origin header is acceptable. If + // CheckOrigin is nil, then a safe default is used: return false if the + // Origin request header is present and the origin host is not equal to + // request Host header. + // + // A CheckOrigin function should carefully validate the request origin to + // prevent cross-site request forgery. + CheckOrigin func(r *http.Request) bool + + // EnableCompression specify if the server should attempt to negotiate per + // message compression (RFC 7692). Setting this value to true does not + // guarantee that compression will be supported. Currently only "no context + // takeover" modes are supported. + EnableCompression bool +} + +func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { + err := HandshakeError{reason} + if u.Error != nil { + u.Error(w, r, status, err) + } else { + w.Header().Set("Sec-Websocket-Version", "13") + http.Error(w, http.StatusText(status), status) + } + return nil, err +} + +// checkSameOrigin returns true if the origin is not set or is equal to the request host. +func checkSameOrigin(r *http.Request) bool { + origin := r.Header["Origin"] + if len(origin) == 0 { + return true + } + u, err := url.Parse(origin[0]) + if err != nil { + return false + } + return equalASCIIFold(u.Host, r.Host) +} + +func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { + if u.Subprotocols != nil { + clientProtocols := Subprotocols(r) + for _, serverProtocol := range u.Subprotocols { + for _, clientProtocol := range clientProtocols { + if clientProtocol == serverProtocol { + return clientProtocol + } + } + } + } else if responseHeader != nil { + return responseHeader.Get("Sec-Websocket-Protocol") + } + return "" +} + +// Upgrade upgrades the HTTP server connection to the WebSocket protocol. +// +// The responseHeader is included in the response to the client's upgrade +// request. Use the responseHeader to specify cookies (Set-Cookie) and the +// application negotiated subprotocol (Sec-WebSocket-Protocol). +// +// If the upgrade fails, then Upgrade replies to the client with an HTTP error +// response. +func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { + const badHandshake = "websocket: the client is not using the websocket protocol: " + + if !tokenListContainsValue(r.Header, "Connection", "upgrade") { + return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header") + } + + if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { + return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header") + } + + if r.Method != "GET" { + return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET") + } + + if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { + return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") + } + + if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { + return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported") + } + + checkOrigin := u.CheckOrigin + if checkOrigin == nil { + checkOrigin = checkSameOrigin + } + if !checkOrigin(r) { + return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin") + } + + challengeKey := r.Header.Get("Sec-Websocket-Key") + if challengeKey == "" { + return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-WebSocket-Key' header is missing or blank") + } + + subprotocol := u.selectSubprotocol(r, responseHeader) + + // Negotiate PMCE + var compress bool + if u.EnableCompression { + for _, ext := range parseExtensions(r.Header) { + if ext[""] != "permessage-deflate" { + continue + } + compress = true + break + } + } + + var ( + netConn net.Conn + err error + ) + + h, ok := w.(http.Hijacker) + if !ok { + return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") + } + var brw *bufio.ReadWriter + netConn, brw, err = h.Hijack() + if err != nil { + return u.returnError(w, r, http.StatusInternalServerError, err.Error()) + } + + if brw.Reader.Buffered() > 0 { + netConn.Close() + return nil, errors.New("websocket: client sent data before handshake is complete") + } + + c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw) + c.subprotocol = subprotocol + + if compress { + c.newCompressionWriter = compressNoContextTakeover + c.newDecompressionReader = decompressNoContextTakeover + } + + p := c.writeBuf[:0] + p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) + p = append(p, computeAcceptKey(challengeKey)...) + p = append(p, "\r\n"...) + if c.subprotocol != "" { + p = append(p, "Sec-WebSocket-Protocol: "...) + p = append(p, c.subprotocol...) + p = append(p, "\r\n"...) + } + if compress { + p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) + } + for k, vs := range responseHeader { + if k == "Sec-Websocket-Protocol" { + continue + } + for _, v := range vs { + p = append(p, k...) + p = append(p, ": "...) + for i := 0; i < len(v); i++ { + b := v[i] + if b <= 31 { + // prevent response splitting. + b = ' ' + } + p = append(p, b) + } + p = append(p, "\r\n"...) + } + } + p = append(p, "\r\n"...) + + // Clear deadlines set by HTTP server. + netConn.SetDeadline(time.Time{}) + + if u.HandshakeTimeout > 0 { + netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) + } + if _, err = netConn.Write(p); err != nil { + netConn.Close() + return nil, err + } + if u.HandshakeTimeout > 0 { + netConn.SetWriteDeadline(time.Time{}) + } + + return c, nil +} + +// Upgrade upgrades the HTTP server connection to the WebSocket protocol. +// +// Deprecated: Use websocket.Upgrader instead. +// +// Upgrade does not perform origin checking. The application is responsible for +// checking the Origin header before calling Upgrade. An example implementation +// of the same origin policy check is: +// +// if req.Header.Get("Origin") != "http://"+req.Host { +// http.Error(w, "Origin not allowed", http.StatusForbidden) +// return +// } +// +// If the endpoint supports subprotocols, then the application is responsible +// for negotiating the protocol used on the connection. Use the Subprotocols() +// function to get the subprotocols requested by the client. Use the +// Sec-Websocket-Protocol response header to specify the subprotocol selected +// by the application. +// +// The responseHeader is included in the response to the client's upgrade +// request. Use the responseHeader to specify cookies (Set-Cookie) and the +// negotiated subprotocol (Sec-Websocket-Protocol). +// +// The connection buffers IO to the underlying network connection. The +// readBufSize and writeBufSize parameters specify the size of the buffers to +// use. Messages can be larger than the buffers. +// +// If the request is not a valid WebSocket handshake, then Upgrade returns an +// error of type HandshakeError. Applications should handle this error by +// replying to the client with an HTTP error response. +func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { + u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} + u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { + // don't return errors to maintain backwards compatibility + } + u.CheckOrigin = func(r *http.Request) bool { + // allow all connections by default + return true + } + return u.Upgrade(w, r, responseHeader) +} + +// Subprotocols returns the subprotocols requested by the client in the +// Sec-Websocket-Protocol header. +func Subprotocols(r *http.Request) []string { + h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) + if h == "" { + return nil + } + protocols := strings.Split(h, ",") + for i := range protocols { + protocols[i] = strings.TrimSpace(protocols[i]) + } + return protocols +} + +// IsWebSocketUpgrade returns true if the client requested upgrade to the +// WebSocket protocol. +func IsWebSocketUpgrade(r *http.Request) bool { + return tokenListContainsValue(r.Header, "Connection", "upgrade") && + tokenListContainsValue(r.Header, "Upgrade", "websocket") +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/util.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/util.go new file mode 100644 index 000000000..385fa01be --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/util.go @@ -0,0 +1,237 @@ +// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "crypto/rand" + "crypto/sha1" + "encoding/base64" + "io" + "net/http" + "strings" + "unicode/utf8" +) + +var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") + +func computeAcceptKey(challengeKey string) string { + h := sha1.New() + h.Write([]byte(challengeKey)) + h.Write(keyGUID) + return base64.StdEncoding.EncodeToString(h.Sum(nil)) +} + +func generateChallengeKey() (string, error) { + p := make([]byte, 16) + if _, err := io.ReadFull(rand.Reader, p); err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(p), nil +} + +// Octet types from RFC 2616. +var octetTypes [256]byte + +const ( + isTokenOctet = 1 << iota + isSpaceOctet +) + +func init() { + // From RFC 2616 + // + // OCTET = + // CHAR = + // CTL = + // CR = + // LF = + // SP = + // HT = + // <"> = + // CRLF = CR LF + // LWS = [CRLF] 1*( SP | HT ) + // TEXT = + // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> + // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT + // token = 1* + // qdtext = > + + for c := 0; c < 256; c++ { + var t byte + isCtl := c <= 31 || c == 127 + isChar := 0 <= c && c <= 127 + isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 + if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { + t |= isSpaceOctet + } + if isChar && !isCtl && !isSeparator { + t |= isTokenOctet + } + octetTypes[c] = t + } +} + +func skipSpace(s string) (rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isSpaceOctet == 0 { + break + } + } + return s[i:] +} + +func nextToken(s string) (token, rest string) { + i := 0 + for ; i < len(s); i++ { + if octetTypes[s[i]]&isTokenOctet == 0 { + break + } + } + return s[:i], s[i:] +} + +func nextTokenOrQuoted(s string) (value string, rest string) { + if !strings.HasPrefix(s, "\"") { + return nextToken(s) + } + s = s[1:] + for i := 0; i < len(s); i++ { + switch s[i] { + case '"': + return s[:i], s[i+1:] + case '\\': + p := make([]byte, len(s)-1) + j := copy(p, s[:i]) + escape := true + for i = i + 1; i < len(s); i++ { + b := s[i] + switch { + case escape: + escape = false + p[j] = b + j++ + case b == '\\': + escape = true + case b == '"': + return string(p[:j]), s[i+1:] + default: + p[j] = b + j++ + } + } + return "", "" + } + } + return "", "" +} + +// equalASCIIFold returns true if s is equal to t with ASCII case folding. +func equalASCIIFold(s, t string) bool { + for s != "" && t != "" { + sr, size := utf8.DecodeRuneInString(s) + s = s[size:] + tr, size := utf8.DecodeRuneInString(t) + t = t[size:] + if sr == tr { + continue + } + if 'A' <= sr && sr <= 'Z' { + sr = sr + 'a' - 'A' + } + if 'A' <= tr && tr <= 'Z' { + tr = tr + 'a' - 'A' + } + if sr != tr { + return false + } + } + return s == t +} + +// tokenListContainsValue returns true if the 1#token header with the given +// name contains a token equal to value with ASCII case folding. +func tokenListContainsValue(header http.Header, name string, value string) bool { +headers: + for _, s := range header[name] { + for { + var t string + t, s = nextToken(skipSpace(s)) + if t == "" { + continue headers + } + s = skipSpace(s) + if s != "" && s[0] != ',' { + continue headers + } + if equalASCIIFold(t, value) { + return true + } + if s == "" { + continue headers + } + s = s[1:] + } + } + return false +} + +// parseExtensiosn parses WebSocket extensions from a header. +func parseExtensions(header http.Header) []map[string]string { + // From RFC 6455: + // + // Sec-WebSocket-Extensions = extension-list + // extension-list = 1#extension + // extension = extension-token *( ";" extension-param ) + // extension-token = registered-token + // registered-token = token + // extension-param = token [ "=" (token | quoted-string) ] + // ;When using the quoted-string syntax variant, the value + // ;after quoted-string unescaping MUST conform to the + // ;'token' ABNF. + + var result []map[string]string +headers: + for _, s := range header["Sec-Websocket-Extensions"] { + for { + var t string + t, s = nextToken(skipSpace(s)) + if t == "" { + continue headers + } + ext := map[string]string{"": t} + for { + s = skipSpace(s) + if !strings.HasPrefix(s, ";") { + break + } + var k string + k, s = nextToken(skipSpace(s[1:])) + if k == "" { + continue headers + } + s = skipSpace(s) + var v string + if strings.HasPrefix(s, "=") { + v, s = nextTokenOrQuoted(skipSpace(s[1:])) + s = skipSpace(s) + } + if s != "" && s[0] != ',' && s[0] != ';' { + continue headers + } + ext[k] = v + } + if s != "" && s[0] != ',' { + continue headers + } + result = append(result, ext) + if s == "" { + continue headers + } + s = s[1:] + } + } + return result +} diff --git a/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/x_net_proxy.go b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/x_net_proxy.go new file mode 100644 index 000000000..2e668f6b8 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gorilla/websocket/x_net_proxy.go @@ -0,0 +1,473 @@ +// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. +//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy + +// Package proxy provides support for a variety of protocols to proxy network +// data. +// + +package websocket + +import ( + "errors" + "io" + "net" + "net/url" + "os" + "strconv" + "strings" + "sync" +) + +type proxy_direct struct{} + +// Direct is a direct proxy: one that makes network connections directly. +var proxy_Direct = proxy_direct{} + +func (proxy_direct) Dial(network, addr string) (net.Conn, error) { + return net.Dial(network, addr) +} + +// A PerHost directs connections to a default Dialer unless the host name +// requested matches one of a number of exceptions. +type proxy_PerHost struct { + def, bypass proxy_Dialer + + bypassNetworks []*net.IPNet + bypassIPs []net.IP + bypassZones []string + bypassHosts []string +} + +// NewPerHost returns a PerHost Dialer that directs connections to either +// defaultDialer or bypass, depending on whether the connection matches one of +// the configured rules. +func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost { + return &proxy_PerHost{ + def: defaultDialer, + bypass: bypass, + } +} + +// Dial connects to the address addr on the given network through either +// defaultDialer or bypass. +func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) { + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + + return p.dialerForRequest(host).Dial(network, addr) +} + +func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer { + if ip := net.ParseIP(host); ip != nil { + for _, net := range p.bypassNetworks { + if net.Contains(ip) { + return p.bypass + } + } + for _, bypassIP := range p.bypassIPs { + if bypassIP.Equal(ip) { + return p.bypass + } + } + return p.def + } + + for _, zone := range p.bypassZones { + if strings.HasSuffix(host, zone) { + return p.bypass + } + if host == zone[1:] { + // For a zone ".example.com", we match "example.com" + // too. + return p.bypass + } + } + for _, bypassHost := range p.bypassHosts { + if bypassHost == host { + return p.bypass + } + } + return p.def +} + +// AddFromString parses a string that contains comma-separated values +// specifying hosts that should use the bypass proxy. Each value is either an +// IP address, a CIDR range, a zone (*.example.com) or a host name +// (localhost). A best effort is made to parse the string and errors are +// ignored. +func (p *proxy_PerHost) AddFromString(s string) { + hosts := strings.Split(s, ",") + for _, host := range hosts { + host = strings.TrimSpace(host) + if len(host) == 0 { + continue + } + if strings.Contains(host, "/") { + // We assume that it's a CIDR address like 127.0.0.0/8 + if _, net, err := net.ParseCIDR(host); err == nil { + p.AddNetwork(net) + } + continue + } + if ip := net.ParseIP(host); ip != nil { + p.AddIP(ip) + continue + } + if strings.HasPrefix(host, "*.") { + p.AddZone(host[1:]) + continue + } + p.AddHost(host) + } +} + +// AddIP specifies an IP address that will use the bypass proxy. Note that +// this will only take effect if a literal IP address is dialed. A connection +// to a named host will never match an IP. +func (p *proxy_PerHost) AddIP(ip net.IP) { + p.bypassIPs = append(p.bypassIPs, ip) +} + +// AddNetwork specifies an IP range that will use the bypass proxy. Note that +// this will only take effect if a literal IP address is dialed. A connection +// to a named host will never match. +func (p *proxy_PerHost) AddNetwork(net *net.IPNet) { + p.bypassNetworks = append(p.bypassNetworks, net) +} + +// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of +// "example.com" matches "example.com" and all of its subdomains. +func (p *proxy_PerHost) AddZone(zone string) { + if strings.HasSuffix(zone, ".") { + zone = zone[:len(zone)-1] + } + if !strings.HasPrefix(zone, ".") { + zone = "." + zone + } + p.bypassZones = append(p.bypassZones, zone) +} + +// AddHost specifies a host name that will use the bypass proxy. +func (p *proxy_PerHost) AddHost(host string) { + if strings.HasSuffix(host, ".") { + host = host[:len(host)-1] + } + p.bypassHosts = append(p.bypassHosts, host) +} + +// A Dialer is a means to establish a connection. +type proxy_Dialer interface { + // Dial connects to the given address via the proxy. + Dial(network, addr string) (c net.Conn, err error) +} + +// Auth contains authentication parameters that specific Dialers may require. +type proxy_Auth struct { + User, Password string +} + +// FromEnvironment returns the dialer specified by the proxy related variables in +// the environment. +func proxy_FromEnvironment() proxy_Dialer { + allProxy := proxy_allProxyEnv.Get() + if len(allProxy) == 0 { + return proxy_Direct + } + + proxyURL, err := url.Parse(allProxy) + if err != nil { + return proxy_Direct + } + proxy, err := proxy_FromURL(proxyURL, proxy_Direct) + if err != nil { + return proxy_Direct + } + + noProxy := proxy_noProxyEnv.Get() + if len(noProxy) == 0 { + return proxy + } + + perHost := proxy_NewPerHost(proxy, proxy_Direct) + perHost.AddFromString(noProxy) + return perHost +} + +// proxySchemes is a map from URL schemes to a function that creates a Dialer +// from a URL with such a scheme. +var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error) + +// RegisterDialerType takes a URL scheme and a function to generate Dialers from +// a URL with that scheme and a forwarding Dialer. Registered schemes are used +// by FromURL. +func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) { + if proxy_proxySchemes == nil { + proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) + } + proxy_proxySchemes[scheme] = f +} + +// FromURL returns a Dialer given a URL specification and an underlying +// Dialer for it to make network requests. +func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) { + var auth *proxy_Auth + if u.User != nil { + auth = new(proxy_Auth) + auth.User = u.User.Username() + if p, ok := u.User.Password(); ok { + auth.Password = p + } + } + + switch u.Scheme { + case "socks5": + return proxy_SOCKS5("tcp", u.Host, auth, forward) + } + + // If the scheme doesn't match any of the built-in schemes, see if it + // was registered by another package. + if proxy_proxySchemes != nil { + if f, ok := proxy_proxySchemes[u.Scheme]; ok { + return f(u, forward) + } + } + + return nil, errors.New("proxy: unknown scheme: " + u.Scheme) +} + +var ( + proxy_allProxyEnv = &proxy_envOnce{ + names: []string{"ALL_PROXY", "all_proxy"}, + } + proxy_noProxyEnv = &proxy_envOnce{ + names: []string{"NO_PROXY", "no_proxy"}, + } +) + +// envOnce looks up an environment variable (optionally by multiple +// names) once. It mitigates expensive lookups on some platforms +// (e.g. Windows). +// (Borrowed from net/http/transport.go) +type proxy_envOnce struct { + names []string + once sync.Once + val string +} + +func (e *proxy_envOnce) Get() string { + e.once.Do(e.init) + return e.val +} + +func (e *proxy_envOnce) init() { + for _, n := range e.names { + e.val = os.Getenv(n) + if e.val != "" { + return + } + } +} + +// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address +// with an optional username and password. See RFC 1928 and RFC 1929. +func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) { + s := &proxy_socks5{ + network: network, + addr: addr, + forward: forward, + } + if auth != nil { + s.user = auth.User + s.password = auth.Password + } + + return s, nil +} + +type proxy_socks5 struct { + user, password string + network, addr string + forward proxy_Dialer +} + +const proxy_socks5Version = 5 + +const ( + proxy_socks5AuthNone = 0 + proxy_socks5AuthPassword = 2 +) + +const proxy_socks5Connect = 1 + +const ( + proxy_socks5IP4 = 1 + proxy_socks5Domain = 3 + proxy_socks5IP6 = 4 +) + +var proxy_socks5Errors = []string{ + "", + "general failure", + "connection forbidden", + "network unreachable", + "host unreachable", + "connection refused", + "TTL expired", + "command not supported", + "address type not supported", +} + +// Dial connects to the address addr on the given network via the SOCKS5 proxy. +func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) { + switch network { + case "tcp", "tcp6", "tcp4": + default: + return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network) + } + + conn, err := s.forward.Dial(s.network, s.addr) + if err != nil { + return nil, err + } + if err := s.connect(conn, addr); err != nil { + conn.Close() + return nil, err + } + return conn, nil +} + +// connect takes an existing connection to a socks5 proxy server, +// and commands the server to extend that connection to target, +// which must be a canonical address with a host and port. +func (s *proxy_socks5) connect(conn net.Conn, target string) error { + host, portStr, err := net.SplitHostPort(target) + if err != nil { + return err + } + + port, err := strconv.Atoi(portStr) + if err != nil { + return errors.New("proxy: failed to parse port number: " + portStr) + } + if port < 1 || port > 0xffff { + return errors.New("proxy: port number out of range: " + portStr) + } + + // the size here is just an estimate + buf := make([]byte, 0, 6+len(host)) + + buf = append(buf, proxy_socks5Version) + if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { + buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword) + } else { + buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone) + } + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + if buf[0] != 5 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) + } + if buf[1] == 0xff { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") + } + + // See RFC 1929 + if buf[1] == proxy_socks5AuthPassword { + buf = buf[:0] + buf = append(buf, 1 /* password protocol version */) + buf = append(buf, uint8(len(s.user))) + buf = append(buf, s.user...) + buf = append(buf, uint8(len(s.password))) + buf = append(buf, s.password...) + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if buf[1] != 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") + } + } + + buf = buf[:0] + buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */) + + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + buf = append(buf, proxy_socks5IP4) + ip = ip4 + } else { + buf = append(buf, proxy_socks5IP6) + } + buf = append(buf, ip...) + } else { + if len(host) > 255 { + return errors.New("proxy: destination host name too long: " + host) + } + buf = append(buf, proxy_socks5Domain) + buf = append(buf, byte(len(host))) + buf = append(buf, host...) + } + buf = append(buf, byte(port>>8), byte(port)) + + if _, err := conn.Write(buf); err != nil { + return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + if _, err := io.ReadFull(conn, buf[:4]); err != nil { + return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + failure := "unknown error" + if int(buf[1]) < len(proxy_socks5Errors) { + failure = proxy_socks5Errors[buf[1]] + } + + if len(failure) > 0 { + return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) + } + + bytesToDiscard := 0 + switch buf[3] { + case proxy_socks5IP4: + bytesToDiscard = net.IPv4len + case proxy_socks5IP6: + bytesToDiscard = net.IPv6len + case proxy_socks5Domain: + _, err := io.ReadFull(conn, buf[:1]) + if err != nil { + return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + bytesToDiscard = int(buf[0]) + default: + return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) + } + + if cap(buf) < bytesToDiscard { + buf = make([]byte, bytesToDiscard) + } else { + buf = buf[:bytesToDiscard] + } + if _, err := io.ReadFull(conn, buf); err != nil { + return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + // Also need to discard the port number + if _, err := io.ReadFull(conn, buf[:2]); err != nil { + return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) + } + + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/LICENSE b/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/LICENSE new file mode 100644 index 000000000..492e2c629 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Manfred Touron + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/README.md b/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/README.md new file mode 100644 index 000000000..1012f4372 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/anonuuid/README.md @@ -0,0 +1,171 @@ +# AnonUUID + +[![Build Status](https://travis-ci.org/moul/anonuuid.svg)](https://travis-ci.org/moul/anonuuid) +[![GoDoc](https://godoc.org/github.com/moul/anonuuid?status.svg)](https://godoc.org/github.com/moul/anonuuid) +[![Coverage Status](https://coveralls.io/repos/moul/anonuuid/badge.svg?branch=master&service=github)](https://coveralls.io/github/moul/anonuuid?branch=master) + +:wrench: Anonymize UUIDs outputs (written in Golang) + +![AnonUUID Logo](https://raw.githubusercontent.com/moul/anonuuid/master/assets/anonuuid.png) + +**anonuuid** anonymize an input string by replacing all UUIDs by an anonymized +new one. + +The fake UUIDs are cached, so if AnonUUID encounter the same real UUIDs multiple +times, the translation will be the same. + +## Usage + +```console +$ anonuuid --help +NAME: + anonuuid - Anonymize UUIDs outputs + +USAGE: + anonuuid [global options] command [command options] [arguments...] + +VERSION: + 1.0.0-dev + +AUTHOR(S): + Manfred Touron + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --hexspeak Generate hexspeak style fake UUIDs + --random, -r Generate random fake UUIDs + --keep-beginning Keep first part of the UUID unchanged + --keep-end Keep last part of the UUID unchanged + --prefix, -p Prefix generated UUIDs + --suffix Suffix generated UUIDs + --help, -h show help + --version, -v print the version + ``` + +## Example + +Replace all UUIDs and cache the correspondance. + +```command +$ anonuuid git:(master) ✗ cat < 32 { + part = part[:32] + } + uuid := part[:8] + "-" + part[8:12] + "-1" + part[13:16] + "-" + part[16:20] + "-" + part[20:32] + + err := IsUUID(uuid) + if err != nil { + return "", err + } + + return uuid, nil +} + +// GenerateRandomUUID returns an UUID based on random strings +func GenerateRandomUUID(length int) (string, error) { + var letters = []rune("abcdef0123456789") + + b := make([]rune, length) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return FormatUUID(string(b)) +} + +// GenerateHexspeakUUID returns an UUID formatted string containing hexspeak words +func GenerateHexspeakUUID(i int) (string, error) { + if i < 0 { + i = -i + } + hexspeaks := []string{ + "0ff1ce", + "31337", + "4b1d", + "badc0de", + "badcafe", + "badf00d", + "deadbabe", + "deadbeef", + "deadc0de", + "deadfeed", + "fee1bad", + } + return FormatUUID(hexspeaks[i%len(hexspeaks)]) +} + +// GenerateLenUUID returns an UUID formatted string based on an index number +func GenerateLenUUID(i int) (string, error) { + if i < 0 { + i = 2<<29 + i + } + return FormatUUID(fmt.Sprintf("%x", i)) +} diff --git a/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/LICENSE b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/LICENSE new file mode 100644 index 000000000..492e2c629 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Manfred Touron + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/LICENSE.apache b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/LICENSE.apache new file mode 100644 index 000000000..6fe259dd5 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/LICENSE.apache @@ -0,0 +1,192 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2013-2017 Docker, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/README.md b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/README.md new file mode 100644 index 000000000..ba45cc2ea --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/README.md @@ -0,0 +1,217 @@ +# gotty-client +:wrench: Terminal client for [GoTTY](https://github.com/yudai/gotty). + +![](https://raw.githubusercontent.com/moul/gotty-client/master/resources/gotty-client.png) + +[![Build Status](https://travis-ci.org/moul/gotty-client.svg?branch=master)](https://travis-ci.org/moul/gotty-client) +[![GoDoc](https://godoc.org/github.com/moul/gotty-client?status.svg)](https://godoc.org/github.com/moul/gotty-client) +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoul%2Fgotty-client.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoul%2Fgotty-client?ref=badge_shield) + +```ruby + ┌─────────────────┐ + ┌──────▶│ /bin/bash │ + │ └─────────────────┘ + ┌──────────────┐ ┌──────────┐ + │ │ │ Gotty │ +┌───────┐ ┌──▶│ Browser │───────┐ │ │ +│ │ │ │ │ │ │ │ +│ │ │ └──────────────┘ │ │ │ ┌─────────────────┐ +│ Bob │───┤ websockets─▶│ │─▶│ emacs /var/www │ +│ │ │ ╔═ ══ ══ ══ ══ ╗ │ │ │ └─────────────────┘ +│ │ │ ║ ║ │ │ │ +└───────┘ └──▶║ gotty-client ───────┘ │ │ + ║ │ │ + ╚═ ══ ══ ══ ══ ╝ └──────────┘ + │ ┌─────────────────┐ + └──────▶│ tmux attach │ + └─────────────────┘ +``` + +## Example + +Server side ([GoTTY](https://github.com/yudai/gotty)) + +```console +$ gotty -p 9191 sh -c 'while true; do date; sleep 1; done' +2015/08/24 18:54:31 Server is starting with command: sh -c while true; do date; sleep 1; done +2015/08/24 18:54:31 URL: http://[::1]:9191/ +2015/08/24 18:54:34 GET /ws +2015/08/24 18:54:34 New client connected: 127.0.0.1:61811 +2015/08/24 18:54:34 Command is running for client 127.0.0.1:61811 with PID 64834 +2015/08/24 18:54:39 Command exited for: 127.0.0.1:61811 +2015/08/24 18:54:39 Connection closed: 127.0.0.1:61811 +... +``` + +**Client side** + +```console +$ gotty-client http://localhost:9191/ +INFO[0000] New title: GoTTY - sh -c while true; do date; sleep 1; done (jean-michel-van-damme.local) +WARN[0000] Unhandled protocol message: json pref: 2{} +Mon Aug 24 18:54:34 CEST 2015 +Mon Aug 24 18:54:35 CEST 2015 +Mon Aug 24 18:54:36 CEST 2015 +Mon Aug 24 18:54:37 CEST 2015 +Mon Aug 24 18:54:38 CEST 2015 +^C +``` + +## Usage + +```console +$ gotty-client -h +NAME: + gotty-client - GoTTY client for your terminal + +USAGE: + gotty-client [global options] command [command options] GOTTY_URL + +AUTHOR: + Manfred Touron + +COMMANDS: + help, h Shows a list of commands or help for one command + +GLOBAL OPTIONS: + --debug, -D Enable debug mode [$GOTTY_CLIENT_DEBUG] + --skip-tls-verify Skip TLS verify [$SKIP_TLS_VERIFY] + --use-proxy-from-env Use Proxy from environment [$USE_PROXY_FROM_ENV] + --detach-keys value Key sequence for detaching gotty-client (default: "ctrl-p,ctrl-q") + --v2 For Gotty 2.0 [$GOTTY_CLIENT_GOTTY2] + --ws-origin value, -w value WebSocket Origin URL [$GOTTY_CLIENT_WS_ORIGIN] + --help, -h show help + --version, -v print the version +``` + +## Install + +Install latest version using Golang (recommended) + +```console +$ go get github.com/moul/gotty-client/cmd/gotty-client +``` + +--- + +Install latest version using Homebrew (Mac OS X) + +```console +$ brew install https://raw.githubusercontent.com/moul/gotty-client/master/contrib/homebrew/gotty-client.rb --HEAD +``` + +or the latest released version + +```console +$ brew install https://raw.githubusercontent.com/moul/gotty-client/master/contrib/homebrew/gotty-client.rb +``` + +## Changelog + +### master (unreleased) + +* Fix TTY restoring by switching to [`github.com/moby/moby/pkg/term`](https://github.com/moby/moby/tree/master/pkg/term) ([#59](https://github.com/moul/gotty-client/pull/59) + ([#60](https://github.com/moul/gotty-client/pull/60) + ([#62](https://github.com/moul/gotty-client/pull/62) ([@Sh4d1](https://github.com/Sh4d1)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.7.0...master) + +### [v1.7.0](https://github.com/moul/gotty-client/releases/tag/v1.7.0) (2018-04-11) + +* Add `--detach-keys` option ([#52](https://github.com/moul/gotty-client/issues/52)) +* Cross build on Solaris ([#55](https://github.com/moul/gotty-client/pull/55) ([@dimtion](https://github.com/dimtion)) +* Fix terminal resizing issue ([#56](https://github.com/moul/gotty-client/pull/56) ([@byung2](https://github.com/byung2)) +* Support for gotty v2.0 ([#58](https://github.com/moul/gotty-client/pull/58) ([@byung2](https://github.com/byung2)) +* Support ws-origin (CORS) ([#58](https://github.com/moul/gotty-client/pull/58) ([@byung2](https://github.com/byung2)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.6.1...v1.7.0) + +### [v1.6.1](https://github.com/moul/gotty-client/releases/tag/v1.6.1) (2017-01-19) + +* Do not exit on EOF ([#45](https://github.com/moul/gotty-client/pull/45)) ([@gurjeet](https://github.com/gurjeet)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.6.0...v1.6.1) + +### [v1.6.0](https://github.com/moul/gotty-client/releases/tag/v1.6.0) (2016-05-23) + +* Support of `--use-proxy-from-env` (Add Proxy support) ([#36](https://github.com/moul/gotty-client/pull/36)) ([@byung2](https://github.com/byung2)) +* Add debug mode ([#18](https://github.com/moul/gotty-client/issues/18)) +* Fix argument passing ([#16](https://github.com/moul/gotty-client/issues/16)) +* Remove warnings + golang fixes and refactoring ([@QuentinPerez](https://github.com/QuentinPerez)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.5.0...v1.6.0) + +### [v1.5.0](https://github.com/moul/gotty-client/releases/tag/v1.5.0) (2016-02-18) + +* Add autocomplete support ([#19](https://github.com/moul/gotty-client/issues/19)) +* Switch from `Party` to `Godep` +* Fix terminal data being interpreted as format string ([#34](https://github.com/moul/gotty-client/pull/34)) ([@mickael9](https://github.com/mickael9)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.4.0...v1.5.0) + +### [v1.4.0](https://github.com/moul/gotty-client/releases/tag/v1.4.0) (2015-12-09) + +* Remove solaris,plan9,nacl for `.goxc.json` +* Add an error if the go version is lower than 1.5 +* Flexible parsing of the input URL +* Add tests +* Support of `--skip-tls-verify` + +[full commits list](https://github.com/moul/gotty-client/compare/v1.3.0...v1.4.0) + +### [v1.3.0](https://github.com/moul/gotty-client/releases/tag/v1.3.0) (2015-10-27) + +* Fix `connected` state when using `Connect()` + `Loop()` methods +* Add `ExitLoop` which allow to exit from `Loop` function + +[full commits list](https://github.com/moul/gotty-client/compare/v1.2.0...v1.3.0) + +### [v1.2.0](https://github.com/moul/gotty-client/releases/tag/v1.2.0) (2015-10-23) + +* Removed an annoying warning when exiting connection ([#22](https://github.com/moul/gotty-client/issues/22)) ([@QuentinPerez](https://github.com/QuentinPerez)) +* Add the ability to configure alternative stdout ([#21](https://github.com/moul/gotty-client/issues/21)) ([@QuentinPerez](https://github.com/QuentinPerez)) +* Refactored the goroutine system with select, improve speed and stability ([@QuentinPerez](https://github.com/QuentinPerez)) +* Add debug mode (`--debug`/`-D`) ([#18](https://github.com/moul/gotty-client/issues/18)) +* Improve error message when connecting by checking HTTP status code +* Fix arguments passing ([#16](https://github.com/moul/gotty-client/issues/16)) +* Dropped support for golang<1.5 +* Small fixes + +[full commits list](https://github.com/moul/gotty-client/compare/v1.1.0...v1.2.0) + +### [v1.1.0](https://github.com/moul/gotty-client/releases/tag/v1.1.0) (2015-10-10) + +* Handling arguments + using mutexes (thanks to [@QuentinPerez](https://github.com/QuentinPerez)) +* Add logo ([#9](https://github.com/moul/gotty-client/issues/9)) +* Using codegansta/cli for CLI parsing ([#3](https://github.com/moul/gotty-client/issues/3)) +* Fix panic when running on older GoTTY server ([#13](https://github.com/moul/gotty-client/issues/13)) +* Add 'homebrew support' ([#1](https://github.com/moul/gotty-client/issues/1)) +* Add Changelog ([#5](https://github.com/moul/gotty-client/issues/5)) +* Add GOXC configuration to build binaries for multiple architectures ([#2](https://github.com/moul/gotty-client/issues/2)) + +[full commits list](https://github.com/moul/gotty-client/compare/v1.0.1...v1.1.0) + +### [v1.0.1](https://github.com/moul/gotty-client/releases/tag/v1.0.1) (2015-09-27) + +* Using party to manage dependencies + +[full commits list](https://github.com/moul/gotty-client/compare/v1.0.0...v1.0.1) + +### [v1.0.0](https://github.com/moul/gotty-client/releases/tag/v1.0.0) (2015-09-27) + +Compatible with [GoTTY](https://github.com/yudai/gotty) version: [v0.0.10](https://github.com/yudai/gotty/releases/tag/v0.0.10) + +#### Features + +* Support **basic-auth** +* Support **terminal-(re)size** +* Support **write** +* Support **title** +* Support **custom URI** + +[full commits list](https://github.com/moul/gotty-client/compare/cf0c1146c7ce20fe0bd65764c13253bc575cd43a...v1.0.0) + +## License + +MIT + + +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoul%2Fgotty-client.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoul%2Fgotty-client?ref=badge_large) diff --git a/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/arch.go b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/arch.go new file mode 100644 index 000000000..a237d7f59 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/arch.go @@ -0,0 +1,33 @@ +// +build !windows + +package gottyclient + +import ( + "encoding/json" + "fmt" + "golang.org/x/sys/unix" + "os" + "os/signal" + "syscall" +) + +func notifySignalSIGWINCH(c chan<- os.Signal) { + signal.Notify(c, syscall.SIGWINCH) +} + +func resetSignalSIGWINCH() { + signal.Reset(syscall.SIGWINCH) +} + +func syscallTIOCGWINSZ() ([]byte, error) { + ws, err := unix.IoctlGetWinsize(0, unix.TIOCGWINSZ) + if err != nil { + return nil, fmt.Errorf("ioctl error: %v", err) + } + tws := winsize{Rows: ws.Row, Columns: ws.Col} + b, err := json.Marshal(tws) + if err != nil { + return nil, fmt.Errorf("json.Marshal error: %v", err) + } + return b, err +} diff --git a/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/arch_windows.go b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/arch_windows.go new file mode 100644 index 000000000..53aaffc5f --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/arch_windows.go @@ -0,0 +1,16 @@ +package gottyclient + +import ( + "errors" + "os" +) + +func notifySignalSIGWINCH(c chan<- os.Signal) { +} + +func resetSignalSIGWINCH() { +} + +func syscallTIOCGWINSZ() ([]byte, error) { + return nil, errors.New("SIGWINCH isn't supported on this ARCH") +} diff --git a/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/ascii.go b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/ascii.go new file mode 100644 index 000000000..49f714533 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/ascii.go @@ -0,0 +1,66 @@ +package gottyclient // from github.com/moby/moby/pkg/term + +import ( + "fmt" + "strings" +) + +// ASCII list the possible supported ASCII key sequence +var ASCII = []string{ + "ctrl-@", + "ctrl-a", + "ctrl-b", + "ctrl-c", + "ctrl-d", + "ctrl-e", + "ctrl-f", + "ctrl-g", + "ctrl-h", + "ctrl-i", + "ctrl-j", + "ctrl-k", + "ctrl-l", + "ctrl-m", + "ctrl-n", + "ctrl-o", + "ctrl-p", + "ctrl-q", + "ctrl-r", + "ctrl-s", + "ctrl-t", + "ctrl-u", + "ctrl-v", + "ctrl-w", + "ctrl-x", + "ctrl-y", + "ctrl-z", + "ctrl-[", + "ctrl-\\", + "ctrl-]", + "ctrl-^", + "ctrl-_", +} + +// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code. +func ToBytes(keys string) ([]byte, error) { + codes := []byte{} +next: + for _, key := range strings.Split(keys, ",") { + if len(key) != 1 { + for code, ctrl := range ASCII { + if ctrl == key { + codes = append(codes, byte(code)) + continue next + } + } + if key == "DEL" { + codes = append(codes, 127) + } else { + return nil, fmt.Errorf("Unknown character: '%s'", key) + } + } else { + codes = append(codes, key[0]) + } + } + return codes, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/gotty-client.go b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/gotty-client.go new file mode 100644 index 000000000..8bcdf8bb1 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/gotty-client.go @@ -0,0 +1,560 @@ +package gottyclient + +import ( + "crypto/tls" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "regexp" + "strings" + "sync" + "time" + + "github.com/containerd/console" + "github.com/creack/goselect" + "github.com/gorilla/websocket" + "github.com/sirupsen/logrus" +) + +// message types for gotty +const ( + OutputV1 = '0' + PongV1 = '1' + SetWindowTitleV1 = '2' + SetPreferencesV1 = '3' + SetReconnectV1 = '4' + + InputV1 = '0' + PingV1 = '1' + ResizeTerminalV1 = '2' +) + +// message types for gotty v2.0 +const ( + // Unknown message type, maybe set by a bug + UnknownOutput = '0' + // Normal output to the terminal + Output = '1' + // Pong to the browser + Pong = '2' + // Set window title of the terminal + SetWindowTitle = '3' + // Set terminal preference + SetPreferences = '4' + // Make terminal to reconnect + SetReconnect = '5' + + // Unknown message type, maybe sent by a bug + UnknownInput = '0' + // User input typically from a keyboard + Input = '1' + // Ping to the server + Ping = '2' + // Notify that the browser size has been changed + ResizeTerminal = '3' +) + +type gottyMessageType struct { + output byte + pong byte + setWindowTitle byte + setPreferences byte + setReconnect byte + input byte + ping byte + resizeTerminal byte +} + +// GetAuthTokenURL transforms a GoTTY http URL to its AuthToken file URL +func GetAuthTokenURL(httpURL string) (*url.URL, *http.Header, error) { + header := http.Header{} + target, err := url.Parse(httpURL) + if err != nil { + return nil, nil, err + } + + target.Path = strings.TrimLeft(target.Path+"auth_token.js", "/") + + if target.User != nil { + header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(target.User.String()))) + target.User = nil + } + + return target, &header, nil +} + +// GetURLQuery returns url.query +func GetURLQuery(rawurl string) (url.Values, error) { + target, err := url.Parse(rawurl) + if err != nil { + return nil, err + } + return target.Query(), nil +} + +// GetWebsocketURL transforms a GoTTY http URL to its WebSocket URL +func GetWebsocketURL(httpURL string) (*url.URL, *http.Header, error) { + header := http.Header{} + target, err := url.Parse(httpURL) + if err != nil { + return nil, nil, err + } + + if target.Scheme == "https" { + target.Scheme = "wss" + } else { + target.Scheme = "ws" + } + + target.Path = strings.TrimLeft(target.Path+"ws", "/") + + if target.User != nil { + header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(target.User.String()))) + target.User = nil + } + + return target, &header, nil +} + +type Client struct { + Dialer *websocket.Dialer + Conn *websocket.Conn + URL string + WriteMutex *sync.Mutex + Output io.Writer + poison chan bool + SkipTLSVerify bool + UseProxyFromEnv bool + Connected bool + EscapeKeys []byte + V2 bool + message *gottyMessageType + WSOrigin string +} + +type querySingleType struct { + AuthToken string `json:"AuthToken"` + Arguments string `json:"Arguments"` +} + +func (c *Client) write(data []byte) error { + c.WriteMutex.Lock() + defer c.WriteMutex.Unlock() + return c.Conn.WriteMessage(websocket.TextMessage, data) +} + +// GetAuthToken retrieves an Auth Token from dynamic auth_token.js file +func (c *Client) GetAuthToken() (string, error) { + target, header, err := GetAuthTokenURL(c.URL) + if err != nil { + return "", err + } + + logrus.Debugf("Fetching auth token auth-token: %q", target.String()) + req, err := http.NewRequest("GET", target.String(), nil) + req.Header = *header + tr := &http.Transport{} + if c.SkipTLSVerify { + conf := &tls.Config{InsecureSkipVerify: true} + tr.TLSClientConfig = conf + } + if c.UseProxyFromEnv { + tr.Proxy = http.ProxyFromEnvironment + } + client := &http.Client{Transport: tr} + resp, err := client.Do(req) + if err != nil { + return "", err + } + + switch resp.StatusCode { + case 200: + // Everything is OK + default: + return "", fmt.Errorf("unknown status code: %d (%s)", resp.StatusCode, http.StatusText(resp.StatusCode)) + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + re := regexp.MustCompile("var gotty_auth_token = '(.*)'") + output := re.FindStringSubmatch(string(body)) + if len(output) == 0 { + return "", fmt.Errorf("Cannot fetch GoTTY auth-token, please upgrade your GoTTY server.") + } + + return output[1], nil +} + +// Connect tries to dial a websocket server +func (c *Client) Connect() error { + // Retrieve AuthToken + authToken, err := c.GetAuthToken() + if err != nil { + return err + } + logrus.Debugf("Auth-token: %q", authToken) + + // Open WebSocket connection + target, header, err := GetWebsocketURL(c.URL) + if err != nil { + return err + } + if c.WSOrigin != "" { + header.Add("Origin", c.WSOrigin) + } + logrus.Debugf("Connecting to websocket: %q", target.String()) + if c.SkipTLSVerify { + c.Dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + } + if c.UseProxyFromEnv { + c.Dialer.Proxy = http.ProxyFromEnvironment + } + conn, _, err := c.Dialer.Dial(target.String(), *header) + if err != nil { + return err + } + c.Conn = conn + c.Connected = true + + // Pass arguments and auth-token + query, err := GetURLQuery(c.URL) + if err != nil { + return err + } + querySingle := querySingleType{ + Arguments: "?" + query.Encode(), + AuthToken: authToken, + } + json, err := json.Marshal(querySingle) + if err != nil { + logrus.Errorf("Failed to parse init message %v", err) + return err + } + // Send Json + logrus.Debugf("Sending arguments and auth-token") + err = c.write(json) + if err != nil { + return err + } + + // Initialize message types for gotty + c.initMessageType() + + go c.pingLoop() + + return nil +} + +// initMessageType initialize message types for gotty +func (c *Client) initMessageType() { + if c.V2 { + c.message = &gottyMessageType{ + output: Output, + pong: Pong, + setWindowTitle: SetWindowTitle, + setPreferences: SetPreferences, + setReconnect: SetReconnect, + input: Input, + ping: Ping, + resizeTerminal: ResizeTerminal, + } + } else { + c.message = &gottyMessageType{ + output: OutputV1, + pong: PongV1, + setWindowTitle: SetWindowTitleV1, + setPreferences: SetPreferencesV1, + setReconnect: SetReconnectV1, + input: InputV1, + ping: PingV1, + resizeTerminal: ResizeTerminalV1, + } + } +} + +func (c *Client) pingLoop() { + for { + logrus.Debugf("Sending ping") + c.write([]byte{c.message.ping}) + time.Sleep(30 * time.Second) + } +} + +// Close will nicely close the dialer +func (c *Client) Close() { + c.Conn.Close() +} + +// ExitLoop will kill all goroutines launched by c.Loop() +// ExitLoop() -> wait Loop() -> Close() +func (c *Client) ExitLoop() { + fname := "ExitLoop" + openPoison(fname, c.poison) +} + +// Loop will look indefinitely for new messages +func (c *Client) Loop() error { + + if !c.Connected { + err := c.Connect() + if err != nil { + return err + } + } + term := console.Current() + err := term.SetRaw() + if err != nil { + return fmt.Errorf("Error setting raw terminal: %v", err) + } + defer term.Reset() + + wg := &sync.WaitGroup{} + + wg.Add(1) + go c.termsizeLoop(wg) + + wg.Add(1) + go c.readLoop(wg) + + wg.Add(1) + go c.writeLoop(wg) + + /* Wait for all of the above goroutines to finish */ + wg.Wait() + + logrus.Debug("Client.Loop() exiting") + return nil +} + +type winsize struct { + Rows uint16 `json:"rows"` + Columns uint16 `json:"columns"` + // unused + x uint16 + y uint16 +} + +type posionReason int + +const ( + committedSuicide = iota + killed +) + +func openPoison(fname string, poison chan bool) posionReason { + logrus.Debug(fname + " suicide") + + /* + * The close() may raise panic if multiple goroutines commit suicide at the + * same time. Prevent that panic from bubbling up. + */ + defer func() { + if r := recover(); r != nil { + logrus.Debug("Prevented panic() of simultaneous suicides", r) + } + }() + + /* Signal others to die */ + close(poison) + return committedSuicide +} + +func die(fname string, poison chan bool) posionReason { + logrus.Debug(fname + " died") + + wasOpen := <-poison + if wasOpen { + logrus.Error("ERROR: The channel was open when it wasn't suppoed to be") + } + + return killed +} + +func (c *Client) termsizeLoop(wg *sync.WaitGroup) posionReason { + defer wg.Done() + fname := "termsizeLoop" + + ch := make(chan os.Signal, 1) + notifySignalSIGWINCH(ch) + defer resetSignalSIGWINCH() + + for { + if b, err := syscallTIOCGWINSZ(); err != nil { + logrus.Warn(err) + } else { + if err = c.write(append([]byte{c.message.resizeTerminal}, b...)); err != nil { + logrus.Warnf("ws.WriteMessage failed: %v", err) + } + } + select { + case <-c.poison: + /* Somebody poisoned the well; die */ + return die(fname, c.poison) + case <-ch: + } + } +} + +type exposeFd interface { + Fd() uintptr +} + +func (c *Client) writeLoop(wg *sync.WaitGroup) posionReason { + defer wg.Done() + fname := "writeLoop" + + buff := make([]byte, 128) + + rdfs := &goselect.FDSet{} + reader := io.ReadCloser(os.Stdin) + + pr := NewEscapeProxy(reader, c.EscapeKeys) + defer reader.Close() + + for { + select { + case <-c.poison: + /* Somebody poisoned the well; die */ + return die(fname, c.poison) + default: + } + + rdfs.Zero() + rdfs.Set(reader.(exposeFd).Fd()) + err := goselect.Select(1, rdfs, nil, nil, 50*time.Millisecond) + if err != nil { + return openPoison(fname, c.poison) + } + if rdfs.IsSet(reader.(exposeFd).Fd()) { + size, err := pr.Read(buff) + + if err != nil { + if err == io.EOF { + // Send EOF to GoTTY + + // Send 'Input' marker, as defined in GoTTY::client_context.go, + // followed by EOT (a translation of Ctrl-D for terminals) + err = c.write(append([]byte{c.message.input}, byte(4))) + + if err != nil { + return openPoison(fname, c.poison) + } + continue + } else { + return openPoison(fname, c.poison) + } + } + + if size <= 0 { + continue + } + + data := buff[:size] + err = c.write(append([]byte{c.message.input}, data...)) + if err != nil { + return openPoison(fname, c.poison) + } + } + } + +} + +func (c *Client) readLoop(wg *sync.WaitGroup) posionReason { + defer wg.Done() + fname := "readLoop" + + type MessageNonBlocking struct { + Data []byte + Err error + } + msgChan := make(chan MessageNonBlocking) + + for { + go func() { + _, data, err := c.Conn.ReadMessage() + msgChan <- MessageNonBlocking{Data: data, Err: err} + }() + + select { + case <-c.poison: + /* Somebody poisoned the well; die */ + return die(fname, c.poison) + case msg := <-msgChan: + if msg.Err != nil { + + if _, ok := msg.Err.(*websocket.CloseError); !ok { + logrus.Warnf("c.Conn.ReadMessage: %v", msg.Err) + } + return openPoison(fname, c.poison) + } + if len(msg.Data) == 0 { + + logrus.Warnf("An error has occured") + return openPoison(fname, c.poison) + } + switch msg.Data[0] { + case c.message.output: // data + buf, err := base64.StdEncoding.DecodeString(string(msg.Data[1:])) + if err != nil { + logrus.Warnf("Invalid base64 content: %q", msg.Data[1:]) + break + } + c.Output.Write(buf) + case c.message.pong: // pong + case c.message.setWindowTitle: // new title + newTitle := string(msg.Data[1:]) + fmt.Fprintf(c.Output, "\033]0;%s\007", newTitle) + case c.message.setPreferences: // json prefs + logrus.Debugf("Unhandled protocol message: json pref: %s", string(msg.Data[1:])) + case c.message.setReconnect: // autoreconnect + logrus.Debugf("Unhandled protocol message: autoreconnect: %s", string(msg.Data)) + default: + logrus.Warnf("Unhandled protocol message: %s", string(msg.Data)) + } + } + } +} + +// SetOutput changes the output stream +func (c *Client) SetOutput(w io.Writer) { + c.Output = w +} + +// ParseURL parses an URL which may be incomplete and tries to standardize it +func ParseURL(input string) (string, error) { + parsed, err := url.Parse(input) + if err != nil { + return "", err + } + switch parsed.Scheme { + case "http", "https": + // everything is ok + default: + return ParseURL(fmt.Sprintf("http://%s", input)) + } + return parsed.String(), nil +} + +// NewClient returns a GoTTY client object +func NewClient(inputURL string) (*Client, error) { + url, err := ParseURL(inputURL) + if err != nil { + return nil, err + } + return &Client{ + Dialer: &websocket.Dialer{}, + URL: url, + WriteMutex: &sync.Mutex{}, + Output: os.Stdout, + poison: make(chan bool), + }, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/proxy.go b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/proxy.go new file mode 100644 index 000000000..212017d05 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moul/gotty-client/proxy.go @@ -0,0 +1,93 @@ +// Copyright 2013-2017 Docker, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +// CHANGES: +// - update package + +package gottyclient + +import ( + "io" +) + +// EscapeError is special error which returned by a TTY proxy reader's Read() +// method in case its detach escape sequence is read. +type EscapeError struct{} + +func (EscapeError) Error() string { + return "read escape sequence" +} + +// escapeProxy is used only for attaches with a TTY. It is used to proxy +// stdin keypresses from the underlying reader and look for the passed in +// escape key sequence to signal a detach. +type escapeProxy struct { + escapeKeys []byte + escapeKeyPos int + r io.Reader +} + +// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader +// and detects when the specified escape keys are read, in which case the Read +// method will return an error of type EscapeError. +func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader { + return &escapeProxy{ + escapeKeys: escapeKeys, + r: r, + } +} + +func (r *escapeProxy) Read(buf []byte) (int, error) { + nr, err := r.r.Read(buf) + + if len(r.escapeKeys) == 0 { + return nr, err + } + + preserve := func() { + // this preserves the original key presses in the passed in buffer + nr += r.escapeKeyPos + preserve := make([]byte, 0, r.escapeKeyPos+len(buf)) + preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...) + preserve = append(preserve, buf...) + r.escapeKeyPos = 0 + copy(buf[0:nr], preserve) + } + + if nr != 1 || err != nil { + if r.escapeKeyPos > 0 { + preserve() + } + return nr, err + } + + if buf[0] != r.escapeKeys[r.escapeKeyPos] { + if r.escapeKeyPos > 0 { + preserve() + } + return nr, nil + } + + if r.escapeKeyPos == len(r.escapeKeys)-1 { + return 0, EscapeError{} + } + + // Looks like we've got an escape key, but we need to match again on the next + // read. + // Store the current escape key we found so we can look for the next one on + // the next read. + // Since this is an escape key, make sure we don't let the caller read it + // If later on we find that this is not the escape sequence, we'll add the + // keys back + r.escapeKeyPos++ + return nr - r.escapeKeyPos, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/LICENSE b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/LICENSE new file mode 100644 index 000000000..9cc753370 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Peter Renström + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/README.md b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/README.md new file mode 100644 index 000000000..e8f0cd598 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/README.md @@ -0,0 +1,43 @@ +# Fuzzy Search + +[![Build Status](https://img.shields.io/travis/renstrom/fuzzysearch.svg?style=flat-square)](https://travis-ci.org/renstrom/fuzzysearch) +[![Godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/renstrom/fuzzysearch/fuzzy) + +Inspired by _[bevacqua/fuzzysearch][1]_, a fuzzy matching library written in JavaScript. But contains some extras like ranking using _[Levenshtein distance][2]_ (see [`RankMatch()`](https://godoc.org/github.com/renstrom/fuzzysearch/fuzzy#RankMatch)) and finding matches in a list of words (see [`Find()`](https://godoc.org/github.com/renstrom/fuzzysearch/fuzzy#Find)). + +Fuzzy searching allows for flexibly matching a string with partial input, useful for filtering data very quickly based on lightweight user input. + +The current implementation uses the algorithm suggested by Mr. Aleph, a russian compiler engineer working at V8. + +## Usage + +```go +fuzzy.Match("twl", "cartwheel") // true +fuzzy.Match("cart", "cartwheel") // true +fuzzy.Match("cw", "cartwheel") // true +fuzzy.Match("ee", "cartwheel") // true +fuzzy.Match("art", "cartwheel") // true +fuzzy.Match("eeel", "cartwheel") // false +fuzzy.Match("dog", "cartwheel") // false + +fuzzy.RankMatch("kitten", "sitting") // 3 + +words := []string{"cartwheel", "foobar", "wheel", "baz"} +fuzzy.Find("whl", words) // [cartwheel wheel] + +fuzzy.RankFind("whl", words) // [{whl cartwheel 6} {whl wheel 2}] +``` + +You can sort the result of a `fuzzy.RankFind()` call using the [`sort`](https://golang.org/pkg/sort/) package in the standard library: + +```go +matches := fuzzy.RankFind("whl", words) // [{whl cartwheel 6} {whl wheel 2}] +sort.Sort(matches) // [{whl wheel 2} {whl cartwheel 6}] +``` + +## License + +MIT + +[1]: https://github.com/bevacqua/fuzzysearch +[2]: http://en.wikipedia.org/wiki/Levenshtein_distance diff --git a/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go new file mode 100644 index 000000000..63277d51e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/fuzzy.go @@ -0,0 +1,167 @@ +// Fuzzy searching allows for flexibly matching a string with partial input, +// useful for filtering data very quickly based on lightweight user input. +package fuzzy + +import ( + "unicode" + "unicode/utf8" +) + +var noop = func(r rune) rune { return r } + +// Match returns true if source matches target using a fuzzy-searching +// algorithm. Note that it doesn't implement Levenshtein distance (see +// RankMatch instead), but rather a simplified version where there's no +// approximation. The method will return true only if each character in the +// source can be found in the target and occurs after the preceding matches. +func Match(source, target string) bool { + return match(source, target, noop) +} + +// MatchFold is a case-insensitive version of Match. +func MatchFold(source, target string) bool { + return match(source, target, unicode.ToLower) +} + +func match(source, target string, fn func(rune) rune) bool { + lenDiff := len(target) - len(source) + + if lenDiff < 0 { + return false + } + + if lenDiff == 0 && source == target { + return true + } + +Outer: + for _, r1 := range source { + for i, r2 := range target { + if fn(r1) == fn(r2) { + target = target[i+utf8.RuneLen(r2):] + continue Outer + } + } + return false + } + + return true +} + +// Find will return a list of strings in targets that fuzzy matches source. +func Find(source string, targets []string) []string { + return find(source, targets, noop) +} + +// FindFold is a case-insensitive version of Find. +func FindFold(source string, targets []string) []string { + return find(source, targets, unicode.ToLower) +} + +func find(source string, targets []string, fn func(rune) rune) []string { + var matches []string + + for _, target := range targets { + if match(source, target, fn) { + matches = append(matches, target) + } + } + + return matches +} + +// RankMatch is similar to Match except it will measure the Levenshtein +// distance between the source and the target and return its result. If there +// was no match, it will return -1. +// Given the requirements of match, RankMatch only needs to perform a subset of +// the Levenshtein calculation, only deletions need be considered, required +// additions and substitutions would fail the match test. +func RankMatch(source, target string) int { + return rank(source, target, noop) +} + +// RankMatchFold is a case-insensitive version of RankMatch. +func RankMatchFold(source, target string) int { + return rank(source, target, unicode.ToLower) +} + +func rank(source, target string, fn func(rune) rune) int { + lenDiff := len(target) - len(source) + + if lenDiff < 0 { + return -1 + } + + if lenDiff == 0 && source == target { + return 0 + } + + runeDiff := 0 + +Outer: + for _, r1 := range source { + for i, r2 := range target { + if fn(r1) == fn(r2) { + target = target[i+utf8.RuneLen(r2):] + continue Outer + } else { + runeDiff++ + } + } + return -1 + } + + // Count up remaining char + for len(target) > 0 { + target = target[utf8.RuneLen(rune(target[0])):] + runeDiff++ + } + + return runeDiff +} + +// RankFind is similar to Find, except it will also rank all matches using +// Levenshtein distance. +func RankFind(source string, targets []string) Ranks { + var r Ranks + for _, target := range find(source, targets, noop) { + distance := LevenshteinDistance(source, target) + r = append(r, Rank{source, target, distance}) + } + return r +} + +// RankFindFold is a case-insensitive version of RankFind. +func RankFindFold(source string, targets []string) Ranks { + var r Ranks + for _, target := range find(source, targets, unicode.ToLower) { + distance := LevenshteinDistance(source, target) + r = append(r, Rank{source, target, distance}) + } + return r +} + +type Rank struct { + // Source is used as the source for matching. + Source string + + // Target is the word matched against. + Target string + + // Distance is the Levenshtein distance between Source and Target. + Distance int +} + +type Ranks []Rank + +func (r Ranks) Len() int { + return len(r) +} + +func (r Ranks) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +func (r Ranks) Less(i, j int) bool { + return r[i].Distance < r[j].Distance +} diff --git a/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go new file mode 100644 index 000000000..237923d34 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/renstrom/fuzzysearch/fuzzy/levenshtein.go @@ -0,0 +1,43 @@ +package fuzzy + +// LevenshteinDistance measures the difference between two strings. +// The Levenshtein distance between two words is the minimum number of +// single-character edits (i.e. insertions, deletions or substitutions) +// required to change one word into the other. +// +// This implemention is optimized to use O(min(m,n)) space and is based on the +// optimized C version found here: +// http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance#C +func LevenshteinDistance(s, t string) int { + r1, r2 := []rune(s), []rune(t) + column := make([]int, len(r1)+1) + + for y := 1; y <= len(r1); y++ { + column[y] = y + } + + for x := 1; x <= len(r2); x++ { + column[0] = x + + for y, lastDiag := 1, x-1; y <= len(r1); y++ { + oldDiag := column[y] + cost := 0 + if r1[y-1] != r2[x-1] { + cost = 1 + } + column[y] = min(column[y]+1, column[y-1]+1, lastDiag+cost) + lastDiag = oldDiag + } + } + + return column[len(r1)] +} + +func min(a, b, c int) int { + if a < b && a < c { + return a + } else if b < c { + return b + } + return c +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/LICENSE b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/LICENSE new file mode 100644 index 000000000..d4a2ec8f0 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Scaleway + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/README.md b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/README.md new file mode 100644 index 000000000..cdf30e7a4 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/README.md @@ -0,0 +1,27 @@ +# Scaleway's API golang client + +[![GoDoc](https://godoc.org/github.com/scaleway/go-scaleway?status.svg)](https://godoc.org/github.com/scaleway/go-scaleway) + +This package contains facilities to play with the Scaleway API, it includes the following features: + +- dedicated configuration file containing credentials to deal with the API +- caching to resolve UUIDs without contacting the API + +## Links + +- [API documentation](https://developer.scaleway.com) +- [Official Python SDK](https://github.com/scaleway/python-scaleway) +- Projects using this SDK + - https://github.com/scaleway/scaleway-cli + - https://github.com/scaleway/devhub + - https://github.com/scaleway/docker-machine-driver-scaleway + - https://github.com/huseyin/docker-machine-driver-scaleway + - https://github.com/scaleway-community/scaleway-ubuntu-coreos/blob/master/overlay/usr/local/update-firewall/scw-api/cache.go + - https://github.com/pulcy/quark + - https://github.com/hex-sh/terraform-provider-scaleway + - https://github.com/tscolari/bosh-scaleway-cpi +- Other **golang** clients + - https://github.com/lalyos/onlabs + - https://github.com/meatballhat/packer-builder-onlinelabs + - https://github.com/nlamirault/go-scaleway + - https://github.com/golang/build/blob/master/cmd/scaleway/scaleway.go diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/api.go b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/api.go new file mode 100644 index 000000000..a48bd5a4b --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/api.go @@ -0,0 +1,2003 @@ +// Copyright (C) 2018 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +// Interact with Scaleway API + +// Package api contains client and functions to interact with Scaleway API +package api + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/http/httputil" + "net/url" + "os" + "sort" + "strconv" + "strings" + "text/tabwriter" + "time" + + "golang.org/x/sync/errgroup" + + "github.com/scaleway/go-scaleway/cache" + "github.com/scaleway/go-scaleway/logger" + "github.com/scaleway/go-scaleway/types" +) + +// Default values +var ( + AccountAPI = "https://account.scaleway.com/" + MetadataAPI = "http://169.254.42.42/" + MarketplaceAPI = "https://api-marketplace.scaleway.com" + ComputeAPIPar1 = "https://cp-par1.scaleway.com/" + ComputeAPIAms1 = "https://cp-ams1.scaleway.com" + + URLPublicDNS = ".pub.cloud.scaleway.com" + URLPrivateDNS = ".priv.cloud.scaleway.com" +) + +func init() { + if url := os.Getenv("SCW_ACCOUNT_API"); url != "" { + AccountAPI = url + } + if url := os.Getenv("SCW_METADATA_API"); url != "" { + MetadataAPI = url + } + if url := os.Getenv("SCW_MARKETPLACE_API"); url != "" { + MarketplaceAPI = url + } + if url := os.Getenv("SCW_COMPUTE_PAR1_API"); url != "" { + ComputeAPIPar1 = url + } + if url := os.Getenv("SCW_COMPUTE_AMS1_API"); url != "" { + ComputeAPIAms1 = url + } +} + +const ( + perPage = 50 +) + +// types.ScalewayAPI is the interface used to communicate with the Scaleway API +type ScalewayAPI struct { + // Organization is the identifier of the Scaleway organization + Organization string + + // Token is the authentication token for the Scaleway organization + Token string + + // Password is the authentication password + password string + + userAgent string + + // Cache is used to quickly resolve identifiers from names + Cache *cache.ScalewayCache + + client *http.Client + verbose bool + computeAPI string + + Region string + // + logger.Logger +} + +// Newtypes.ScalewayAPI creates a ready-to-use ScalewayAPI client +func NewScalewayAPI(organization, token, userAgent, region string, options ...func(*ScalewayAPI)) (*ScalewayAPI, error) { + s := &ScalewayAPI{ + // exposed + Organization: organization, + Token: token, + Logger: logger.NewDefaultLogger(), + + // internal + client: &http.Client{}, + verbose: os.Getenv("SCW_VERBOSE_API") != "", + password: "", + userAgent: userAgent, + } + for _, option := range options { + option(s) + } + cache, err := cache.NewScalewayCache(func() { s.Logger.Debugf("Writing cache file to disk") }) + if err != nil { + return nil, err + } + s.Cache = cache + if os.Getenv("SCW_TLSVERIFY") == "0" { + s.client.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + } + switch region { + case "par1", "": + s.computeAPI = ComputeAPIPar1 + case "ams1": + s.computeAPI = ComputeAPIAms1 + default: + return nil, fmt.Errorf("%s isn't a valid region", region) + } + s.Region = region + if url := os.Getenv("SCW_COMPUTE_API"); url != "" { + s.computeAPI = url + } + return s, nil +} + +// ClearCache clears the cache +func (s *ScalewayAPI) ClearCache() { + s.Cache.Clear() +} + +// Sync flushes out the cache to the disk +func (s *ScalewayAPI) Sync() { + s.Cache.Save() +} + +func (s *ScalewayAPI) response(method, uri string, content io.Reader) (resp *http.Response, err error) { + var ( + req *http.Request + ) + + req, err = http.NewRequest(method, uri, content) + if err != nil { + err = fmt.Errorf("response %s %s", method, uri) + return + } + req.Header.Set("X-Auth-Token", s.Token) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", s.userAgent) + s.LogHTTP(req) + if s.verbose { + dump, _ := httputil.DumpRequest(req, true) + s.Debugf("%v", string(dump)) + } else { + s.Debugf("[%s]: %v", method, uri) + } + resp, err = s.client.Do(req) + return +} + +// GetResponsePaginate fetchs all resources and returns an http.Response object for the requested resource +func (s *ScalewayAPI) GetResponsePaginate(apiURL, resource string, values url.Values) (*http.Response, error) { + resp, err := s.response("HEAD", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil) + if err != nil { + return nil, err + } + + count := resp.Header.Get("X-Total-Count") + var maxElem int + if count == "" { + maxElem = 0 + } else { + maxElem, err = strconv.Atoi(count) + if err != nil { + return nil, err + } + } + + get := maxElem / perPage + if (float32(maxElem) / perPage) > float32(get) { + get++ + } + + if get <= 1 { // If there is 0 or 1 page of result, the response is not paginated + if len(values) == 0 { + return s.response("GET", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), nil) + } + return s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil) + } + + fetchAll := !(values.Get("per_page") != "" || values.Get("page") != "") + if fetchAll { + var g errgroup.Group + + ch := make(chan *http.Response, get) + for i := 1; i <= get; i++ { + i := i // closure tricks + g.Go(func() (err error) { + var resp *http.Response + + val := url.Values{} + val.Set("per_page", fmt.Sprintf("%v", perPage)) + val.Set("page", fmt.Sprintf("%v", i)) + resp, err = s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, val.Encode()), nil) + ch <- resp + return + }) + } + if err = g.Wait(); err != nil { + return nil, err + } + newBody := make(map[string][]json.RawMessage) + body := make(map[string][]json.RawMessage) + key := "" + for i := 0; i < get; i++ { + res := <-ch + if res.StatusCode != http.StatusOK { + return res, nil + } + content, err := ioutil.ReadAll(res.Body) + res.Body.Close() + if err != nil { + return nil, err + } + if err := json.Unmarshal(content, &body); err != nil { + return nil, err + } + + if i == 0 { + resp = res + for k := range body { + key = k + break + } + } + newBody[key] = append(newBody[key], body[key]...) + } + payload := new(bytes.Buffer) + if err := json.NewEncoder(payload).Encode(newBody); err != nil { + return nil, err + } + resp.Body = ioutil.NopCloser(payload) + } else { + resp, err = s.response("GET", fmt.Sprintf("%s/%s?%s", strings.TrimRight(apiURL, "/"), resource, values.Encode()), nil) + } + return resp, err +} + +// PostResponse returns an http.Response object for the updated resource +func (s *ScalewayAPI) PostResponse(apiURL, resource string, data interface{}) (*http.Response, error) { + payload := new(bytes.Buffer) + if err := json.NewEncoder(payload).Encode(data); err != nil { + return nil, err + } + return s.response("POST", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload) +} + +// PatchResponse returns an http.Response object for the updated resource +func (s *ScalewayAPI) PatchResponse(apiURL, resource string, data interface{}) (*http.Response, error) { + payload := new(bytes.Buffer) + if err := json.NewEncoder(payload).Encode(data); err != nil { + return nil, err + } + return s.response("PATCH", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload) +} + +// PutResponse returns an http.Response object for the updated resource +func (s *ScalewayAPI) PutResponse(apiURL, resource string, data interface{}) (*http.Response, error) { + payload := new(bytes.Buffer) + if err := json.NewEncoder(payload).Encode(data); err != nil { + return nil, err + } + return s.response("PUT", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), payload) +} + +// DeleteResponse returns an http.Response object for the deleted resource +func (s *ScalewayAPI) DeleteResponse(apiURL, resource string) (*http.Response, error) { + return s.response("DELETE", fmt.Sprintf("%s/%s", strings.TrimRight(apiURL, "/"), resource), nil) +} + +// handleHTTPError checks the statusCode and displays the error +func (s *ScalewayAPI) handleHTTPError(goodStatusCode []int, resp *http.Response) ([]byte, error) { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if s.verbose { + resp.Body = ioutil.NopCloser(bytes.NewBuffer(body)) + dump, err := httputil.DumpResponse(resp, true) + if err == nil { + var js bytes.Buffer + + err = json.Indent(&js, body, "", " ") + if err != nil { + s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, string(dump)) + } else { + s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, js.String()) + } + } + } else { + s.Debugf("[Response]: [%v]\n%v", resp.StatusCode, string(body)) + } + + if resp.StatusCode >= http.StatusInternalServerError { + return nil, errors.New(string(body)) + } + good := false + for _, code := range goodStatusCode { + if code == resp.StatusCode { + good = true + } + } + if !good { + var scwError types.ScalewayAPIError + + if err := json.Unmarshal(body, &scwError); err != nil { + return nil, err + } + scwError.StatusCode = resp.StatusCode + s.Debugf("%s", scwError.Error()) + return nil, scwError + } + return body, nil +} + +func (s *ScalewayAPI) fetchServers(api string, query url.Values, out chan<- types.ScalewayServers) func() error { + return func() error { + resp, err := s.GetResponsePaginate(api, "servers", query) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return err + } + var servers types.ScalewayServers + + if err = json.Unmarshal(body, &servers); err != nil { + return err + } + out <- servers + return nil + } +} + +// GetServers gets the list of servers from the ScalewayAPI +func (s *ScalewayAPI) GetServers(all bool, limit int) (*[]types.ScalewayServer, error) { + query := url.Values{} + if !all { + query.Set("state", "running") + } + if limit > 0 { + // FIXME: wait for the API to be ready + // query.Set("per_page", strconv.Itoa(limit)) + panic("Not implemented yet") + } + if all && limit == 0 { + s.Cache.ClearServers() + } + var ( + g errgroup.Group + apis = []string{ + ComputeAPIPar1, + ComputeAPIAms1, + } + ) + + serverChan := make(chan types.ScalewayServers, 2) + for _, api := range apis { + g.Go(s.fetchServers(api, query, serverChan)) + } + + if err := g.Wait(); err != nil { + return nil, err + } + close(serverChan) + var servers types.ScalewayServers + + for server := range serverChan { + servers.Servers = append(servers.Servers, server.Servers...) + } + + for i, server := range servers.Servers { + servers.Servers[i].DNSPublic = server.Identifier + URLPublicDNS + servers.Servers[i].DNSPrivate = server.Identifier + URLPrivateDNS + s.Cache.InsertServer(server.Identifier, server.Location.ZoneID, server.Arch, server.Organization, server.Name) + } + return &servers.Servers, nil +} + +// GetServer gets a server from the ScalewayAPI +func (s *ScalewayAPI) GetServer(serverID string) (*types.ScalewayServer, error) { + if serverID == "" { + return nil, fmt.Errorf("cannot get server without serverID") + } + resp, err := s.GetResponsePaginate(s.computeAPI, "servers/"+serverID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + + var oneServer types.ScalewayOneServer + + if err = json.Unmarshal(body, &oneServer); err != nil { + return nil, err + } + // FIXME arch, owner, title + oneServer.Server.DNSPublic = oneServer.Server.Identifier + URLPublicDNS + oneServer.Server.DNSPrivate = oneServer.Server.Identifier + URLPrivateDNS + s.Cache.InsertServer(oneServer.Server.Identifier, oneServer.Server.Location.ZoneID, oneServer.Server.Arch, oneServer.Server.Organization, oneServer.Server.Name) + return &oneServer.Server, nil +} + +// PostServerAction posts an action on a server +func (s *ScalewayAPI) PostServerAction(serverID, action string) error { + data := types.ScalewayServerAction{ + Action: action, + } + resp, err := s.PostResponse(s.computeAPI, fmt.Sprintf("servers/%s/action", serverID), data) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) + return err +} + +// DeleteServer deletes a server +func (s *ScalewayAPI) DeleteServer(serverID string) error { + defer s.Cache.RemoveServer(serverID) + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("servers/%s", serverID)) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { + return err + } + return nil +} + +// PostServer creates a new server +func (s *ScalewayAPI) PostServer(definition types.ScalewayServerDefinition) (string, error) { + definition.Organization = s.Organization + + resp, err := s.PostResponse(s.computeAPI, "servers", definition) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return "", err + } + var server types.ScalewayOneServer + + if err = json.Unmarshal(body, &server); err != nil { + return "", err + } + // FIXME arch, owner, title + s.Cache.InsertServer(server.Server.Identifier, server.Server.Location.ZoneID, server.Server.Arch, server.Server.Organization, server.Server.Name) + return server.Server.Identifier, nil +} + +// PatchUserSSHKey updates a user +func (s *ScalewayAPI) PatchUserSSHKey(UserID string, definition types.ScalewayUserPatchSSHKeyDefinition) error { + resp, err := s.PatchResponse(AccountAPI, fmt.Sprintf("users/%s", UserID), definition) + if err != nil { + return err + } + + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusOK}, resp); err != nil { + return err + } + return nil +} + +// PatchServer updates a server +func (s *ScalewayAPI) PatchServer(serverID string, definition types.ScalewayServerPatchDefinition) error { + resp, err := s.PatchResponse(s.computeAPI, fmt.Sprintf("servers/%s", serverID), definition) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusOK}, resp); err != nil { + return err + } + return nil +} + +// PostSnapshot creates a new snapshot +func (s *ScalewayAPI) PostSnapshot(volumeID string, name string) (string, error) { + definition := types.ScalewaySnapshotDefinition{ + VolumeIDentifier: volumeID, + Name: name, + Organization: s.Organization, + } + resp, err := s.PostResponse(s.computeAPI, "snapshots", definition) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return "", err + } + var snapshot types.ScalewayOneSnapshot + + if err = json.Unmarshal(body, &snapshot); err != nil { + return "", err + } + // FIXME arch, owner, title + s.Cache.InsertSnapshot(snapshot.Snapshot.Identifier, "", "", snapshot.Snapshot.Organization, snapshot.Snapshot.Name) + return snapshot.Snapshot.Identifier, nil +} + +// PostImage creates a new image +func (s *ScalewayAPI) PostImage(volumeID string, name string, bootscript string, arch string) (string, error) { + definition := types.ScalewayImageDefinition{ + SnapshotIDentifier: volumeID, + Name: name, + Organization: s.Organization, + Arch: arch, + } + if bootscript != "" { + definition.DefaultBootscript = &bootscript + } + + resp, err := s.PostResponse(s.computeAPI, "images", definition) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return "", err + } + var image types.ScalewayOneImage + + if err = json.Unmarshal(body, &image); err != nil { + return "", err + } + // FIXME region, arch, owner, title + s.Cache.InsertImage(image.Image.Identifier, "", image.Image.Arch, image.Image.Organization, image.Image.Name, "") + return image.Image.Identifier, nil +} + +// PostVolume creates a new volume +func (s *ScalewayAPI) PostVolume(definition types.ScalewayVolumeDefinition) (string, error) { + definition.Organization = s.Organization + if definition.Type == "" { + definition.Type = "l_ssd" + } + + resp, err := s.PostResponse(s.computeAPI, "volumes", definition) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return "", err + } + var volume types.ScalewayOneVolume + + if err = json.Unmarshal(body, &volume); err != nil { + return "", err + } + // FIXME: s.Cache.InsertVolume(volume.Volume.Identifier, volume.Volume.Name) + return volume.Volume.Identifier, nil +} + +// PutVolume updates a volume +func (s *ScalewayAPI) PutVolume(volumeID string, definition types.ScalewayVolumePutDefinition) error { + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("volumes/%s", volumeID), definition) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// ResolveServer attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveServer(needle string) (types.ScalewayResolverResults, error) { + servers, err := s.Cache.LookUpServers(needle, true) + if err != nil { + return servers, err + } + if len(servers) == 0 { + if _, err = s.GetServers(true, 0); err != nil { + return nil, err + } + servers, err = s.Cache.LookUpServers(needle, true) + } + return servers, err +} + +// ResolveVolume attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveVolume(needle string) (types.ScalewayResolverResults, error) { + volumes, err := s.Cache.LookUpVolumes(needle, true) + if err != nil { + return volumes, err + } + if len(volumes) == 0 { + if _, err = s.GetVolumes(); err != nil { + return nil, err + } + volumes, err = s.Cache.LookUpVolumes(needle, true) + } + return volumes, err +} + +// ResolveSnapshot attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveSnapshot(needle string) (types.ScalewayResolverResults, error) { + snapshots, err := s.Cache.LookUpSnapshots(needle, true) + if err != nil { + return snapshots, err + } + if len(snapshots) == 0 { + if _, err = s.GetSnapshots(); err != nil { + return nil, err + } + snapshots, err = s.Cache.LookUpSnapshots(needle, true) + } + return snapshots, err +} + +// ResolveImage attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveImage(needle string) (types.ScalewayResolverResults, error) { + images, err := s.Cache.LookUpImages(needle, true) + if err != nil { + return images, err + } + if len(images) == 0 { + if _, err = s.GetImages(); err != nil { + return nil, err + } + images, err = s.Cache.LookUpImages(needle, true) + } + return images, err +} + +// ResolveBootscript attempts to find a matching Identifier for the input string +func (s *ScalewayAPI) ResolveBootscript(needle string) (types.ScalewayResolverResults, error) { + bootscripts, err := s.Cache.LookUpBootscripts(needle, true) + if err != nil { + return bootscripts, err + } + if len(bootscripts) == 0 { + if _, err = s.GetBootscripts(); err != nil { + return nil, err + } + bootscripts, err = s.Cache.LookUpBootscripts(needle, true) + } + return bootscripts, err +} + +// GetImages gets the list of images from the ScalewayAPI +func (s *ScalewayAPI) GetImages() (*[]types.MarketImage, error) { + images, err := s.GetMarketPlaceImages("") + if err != nil { + return nil, err + } + s.Cache.ClearImages() + for i, image := range images.Images { + if image.CurrentPublicVersion != "" { + for _, version := range image.Versions { + if version.ID == image.CurrentPublicVersion { + for _, localImage := range version.LocalImages { + images.Images[i].Public = true + s.Cache.InsertImage(localImage.ID, localImage.Zone, localImage.Arch, image.Organization.ID, image.Name, image.CurrentPublicVersion) + } + } + } + } + } + values := url.Values{} + values.Set("organization", s.Organization) + resp, err := s.GetResponsePaginate(s.computeAPI, "images", values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var OrgaImages types.ScalewayImages + + if err = json.Unmarshal(body, &OrgaImages); err != nil { + return nil, err + } + + for _, orgaImage := range OrgaImages.Images { + images.Images = append(images.Images, types.MarketImage{ + Categories: []string{"MyImages"}, + CreationDate: orgaImage.CreationDate, + CurrentPublicVersion: orgaImage.Identifier, + ModificationDate: orgaImage.ModificationDate, + Name: orgaImage.Name, + Public: false, + MarketVersions: types.MarketVersions{ + Versions: []types.MarketVersionDefinition{ + { + CreationDate: orgaImage.CreationDate, + ID: orgaImage.Identifier, + ModificationDate: orgaImage.ModificationDate, + MarketLocalImages: types.MarketLocalImages{ + LocalImages: []types.MarketLocalImageDefinition{ + { + Arch: orgaImage.Arch, + ID: orgaImage.Identifier, + // TODO: fecth images from ams1 and par1 + Zone: s.Region, + }, + }, + }, + }, + }, + }, + }) + s.Cache.InsertImage(orgaImage.Identifier, s.Region, orgaImage.Arch, orgaImage.Organization, orgaImage.Name, "") + } + return &images.Images, nil +} + +// GetImage gets an image from the ScalewayAPI +func (s *ScalewayAPI) GetImage(imageID string) (*types.ScalewayImage, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "images/"+imageID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var oneImage types.ScalewayOneImage + + if err = json.Unmarshal(body, &oneImage); err != nil { + return nil, err + } + // FIXME owner, title + s.Cache.InsertImage(oneImage.Image.Identifier, s.Region, oneImage.Image.Arch, oneImage.Image.Organization, oneImage.Image.Name, "") + return &oneImage.Image, nil +} + +// DeleteImage deletes a image +func (s *ScalewayAPI) DeleteImage(imageID string) error { + defer s.Cache.RemoveImage(imageID) + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("images/%s", imageID)) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { + return err + } + return nil +} + +// DeleteSnapshot deletes a snapshot +func (s *ScalewayAPI) DeleteSnapshot(snapshotID string) error { + defer s.Cache.RemoveSnapshot(snapshotID) + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("snapshots/%s", snapshotID)) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { + return err + } + return nil +} + +// DeleteVolume deletes a volume +func (s *ScalewayAPI) DeleteVolume(volumeID string) error { + defer s.Cache.RemoveVolume(volumeID) + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("volumes/%s", volumeID)) + if err != nil { + return err + } + defer resp.Body.Close() + + if _, err := s.handleHTTPError([]int{http.StatusNoContent}, resp); err != nil { + return err + } + return nil +} + +// GetSnapshots gets the list of snapshots from the ScalewayAPI +func (s *ScalewayAPI) GetSnapshots() (*[]types.ScalewaySnapshot, error) { + query := url.Values{} + s.Cache.ClearSnapshots() + + resp, err := s.GetResponsePaginate(s.computeAPI, "snapshots", query) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var snapshots types.ScalewaySnapshots + + if err = json.Unmarshal(body, &snapshots); err != nil { + return nil, err + } + for _, snapshot := range snapshots.Snapshots { + // FIXME region, arch, owner, title + s.Cache.InsertSnapshot(snapshot.Identifier, "", "", snapshot.Organization, snapshot.Name) + } + return &snapshots.Snapshots, nil +} + +// GetSnapshot gets a snapshot from the ScalewayAPI +func (s *ScalewayAPI) GetSnapshot(snapshotID string) (*types.ScalewaySnapshot, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "snapshots/"+snapshotID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var oneSnapshot types.ScalewayOneSnapshot + + if err = json.Unmarshal(body, &oneSnapshot); err != nil { + return nil, err + } + // FIXME region, arch, owner, title + s.Cache.InsertSnapshot(oneSnapshot.Snapshot.Identifier, "", "", oneSnapshot.Snapshot.Organization, oneSnapshot.Snapshot.Name) + return &oneSnapshot.Snapshot, nil +} + +// GetVolumes gets the list of volumes from the ScalewayAPI +func (s *ScalewayAPI) GetVolumes() (*[]types.ScalewayVolume, error) { + query := url.Values{} + s.Cache.ClearVolumes() + + resp, err := s.GetResponsePaginate(s.computeAPI, "volumes", query) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + + var volumes types.ScalewayVolumes + + if err = json.Unmarshal(body, &volumes); err != nil { + return nil, err + } + for _, volume := range volumes.Volumes { + // FIXME region, arch, owner, title + s.Cache.InsertVolume(volume.Identifier, "", "", volume.Organization, volume.Name) + } + return &volumes.Volumes, nil +} + +// GetVolume gets a volume from the ScalewayAPI +func (s *ScalewayAPI) GetVolume(volumeID string) (*types.ScalewayVolume, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "volumes/"+volumeID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var oneVolume types.ScalewayOneVolume + + if err = json.Unmarshal(body, &oneVolume); err != nil { + return nil, err + } + // FIXME region, arch, owner, title + s.Cache.InsertVolume(oneVolume.Volume.Identifier, "", "", oneVolume.Volume.Organization, oneVolume.Volume.Name) + return &oneVolume.Volume, nil +} + +// GetBootscripts gets the list of bootscripts from the ScalewayAPI +func (s *ScalewayAPI) GetBootscripts() (*[]types.ScalewayBootscript, error) { + query := url.Values{} + + s.Cache.ClearBootscripts() + resp, err := s.GetResponsePaginate(s.computeAPI, "bootscripts", query) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var bootscripts types.ScalewayBootscripts + + if err = json.Unmarshal(body, &bootscripts); err != nil { + return nil, err + } + for _, bootscript := range bootscripts.Bootscripts { + // FIXME region, arch, owner, title + s.Cache.InsertBootscript(bootscript.Identifier, "", bootscript.Arch, bootscript.Organization, bootscript.Title) + } + return &bootscripts.Bootscripts, nil +} + +// GetBootscript gets a bootscript from the ScalewayAPI +func (s *ScalewayAPI) GetBootscript(bootscriptID string) (*types.ScalewayBootscript, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "bootscripts/"+bootscriptID, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var oneBootscript types.ScalewayOneBootscript + + if err = json.Unmarshal(body, &oneBootscript); err != nil { + return nil, err + } + // FIXME region, arch, owner, title + s.Cache.InsertBootscript(oneBootscript.Bootscript.Identifier, "", oneBootscript.Bootscript.Arch, oneBootscript.Bootscript.Organization, oneBootscript.Bootscript.Title) + return &oneBootscript.Bootscript, nil +} + +// GetUserdatas gets list of userdata for a server +func (s *ScalewayAPI) GetUserdatas(serverID string, metadata bool) (*types.ScalewayUserdatas, error) { + var uri, endpoint string + + endpoint = s.computeAPI + if metadata { + uri = "/user_data" + endpoint = MetadataAPI + } else { + uri = fmt.Sprintf("servers/%s/user_data", serverID) + } + + resp, err := s.GetResponsePaginate(endpoint, uri, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var userdatas types.ScalewayUserdatas + + if err = json.Unmarshal(body, &userdatas); err != nil { + return nil, err + } + return &userdatas, nil +} + +// GetUserdata gets a specific userdata for a server +func (s *ScalewayAPI) GetUserdata(serverID, key string, metadata bool) (*types.ScalewayUserdata, error) { + var uri, endpoint string + + endpoint = s.computeAPI + if metadata { + uri = fmt.Sprintf("/user_data/%s", key) + endpoint = MetadataAPI + } else { + uri = fmt.Sprintf("servers/%s/user_data/%s", serverID, key) + } + + var err error + resp, err := s.GetResponsePaginate(endpoint, uri, url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("no such user_data %q (%d)", key, resp.StatusCode) + } + var data types.ScalewayUserdata + data, err = ioutil.ReadAll(resp.Body) + return &data, err +} + +// PatchUserdata sets a user data +func (s *ScalewayAPI) PatchUserdata(serverID, key string, value []byte, metadata bool) error { + var resource, endpoint string + + endpoint = s.computeAPI + if metadata { + resource = fmt.Sprintf("/user_data/%s", key) + endpoint = MetadataAPI + } else { + resource = fmt.Sprintf("servers/%s/user_data/%s", serverID, key) + } + + uri := fmt.Sprintf("%s/%s", strings.TrimRight(endpoint, "/"), resource) + payload := new(bytes.Buffer) + payload.Write(value) + + req, err := http.NewRequest("PATCH", uri, payload) + if err != nil { + return err + } + + req.Header.Set("X-Auth-Token", s.Token) + req.Header.Set("Content-Type", "text/plain") + req.Header.Set("User-Agent", s.userAgent) + + s.LogHTTP(req) + + resp, err := s.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode == http.StatusNoContent { + return nil + } + + return fmt.Errorf("cannot set user_data (%d)", resp.StatusCode) +} + +// DeleteUserdata deletes a server user_data +func (s *ScalewayAPI) DeleteUserdata(serverID, key string, metadata bool) error { + var url, endpoint string + + endpoint = s.computeAPI + if metadata { + url = fmt.Sprintf("/user_data/%s", key) + endpoint = MetadataAPI + } else { + url = fmt.Sprintf("servers/%s/user_data/%s", serverID, key) + } + + resp, err := s.DeleteResponse(endpoint, url) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// GetTasks get the list of tasks from the ScalewayAPI +func (s *ScalewayAPI) GetTasks() (*[]types.ScalewayTask, error) { + query := url.Values{} + resp, err := s.GetResponsePaginate(s.computeAPI, "tasks", query) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var tasks types.ScalewayTasks + + if err = json.Unmarshal(body, &tasks); err != nil { + return nil, err + } + return &tasks.Tasks, nil +} + +// CheckCredentials performs a dummy check to ensure we can contact the API +func (s *ScalewayAPI) CheckCredentials() error { + query := url.Values{} + + resp, err := s.GetResponsePaginate(AccountAPI, "tokens", query) + if err != nil { + return err + } + defer resp.Body.Close() + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return err + } + found := false + var tokens types.ScalewayGetTokens + + if err = json.Unmarshal(body, &tokens); err != nil { + return err + } + for _, token := range tokens.Tokens { + if token.ID == s.Token { + found = true + break + } + } + if !found { + return fmt.Errorf("Invalid token %v", s.Token) + } + return nil +} + +// GetUserID returns the userID +func (s *ScalewayAPI) GetUserID() (string, error) { + resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("tokens/%s", s.Token), url.Values{}) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return "", err + } + var token types.ScalewayTokensDefinition + + if err = json.Unmarshal(body, &token); err != nil { + return "", err + } + return token.Token.UserID, nil +} + +// GetOrganization returns Organization +func (s *ScalewayAPI) GetOrganization() (*types.ScalewayOrganizationsDefinition, error) { + resp, err := s.GetResponsePaginate(AccountAPI, "organizations", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var data types.ScalewayOrganizationsDefinition + + if err = json.Unmarshal(body, &data); err != nil { + return nil, err + } + return &data, nil +} + +// GetUser returns the user +func (s *ScalewayAPI) GetUser() (*types.ScalewayUserDefinition, error) { + userID, err := s.GetUserID() + if err != nil { + return nil, err + } + resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("users/%s", userID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var user types.ScalewayUsersDefinition + + if err = json.Unmarshal(body, &user); err != nil { + return nil, err + } + return &user.User, nil +} + +// GetPermissions returns the permissions +func (s *ScalewayAPI) GetPermissions() (*types.ScalewayPermissionDefinition, error) { + resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("tokens/%s/permissions", s.Token), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var permissions types.ScalewayPermissionDefinition + + if err = json.Unmarshal(body, &permissions); err != nil { + return nil, err + } + return &permissions, nil +} + +// GetDashboard returns the dashboard +func (s *ScalewayAPI) GetDashboard() (*types.ScalewayDashboard, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "dashboard", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var dashboard types.ScalewayDashboardResp + + if err = json.Unmarshal(body, &dashboard); err != nil { + return nil, err + } + return &dashboard.Dashboard, nil +} + +// GetServerID returns exactly one server matching +func (s *ScalewayAPI) GetServerID(needle string) (string, error) { + // Parses optional type prefix, i.e: "server:name" -> "name" + _, needle = types.ParseNeedle(needle) + + servers, err := s.ResolveServer(needle) + if err != nil { + return "", fmt.Errorf("Unable to resolve server %s: %s", needle, err) + } + if len(servers) == 1 { + return servers[0].Identifier, nil + } + if len(servers) == 0 { + return "", fmt.Errorf("No such server: %s", needle) + } + return "", showResolverResults(needle, servers) +} + +func showResolverResults(needle string, results types.ScalewayResolverResults) error { + w := tabwriter.NewWriter(os.Stderr, 20, 1, 3, ' ', 0) + defer w.Flush() + sort.Sort(results) + fmt.Fprintf(w, " IMAGEID\tFROM\tNAME\tZONE\tARCH\n") + for _, result := range results { + if result.Arch == "" { + result.Arch = "n/a" + } + fmt.Fprintf(w, "- %s\t%s\t%s\t%s\t%s\n", result.TruncIdentifier(), result.CodeName(), result.Name, result.Region, result.Arch) + } + return fmt.Errorf("Too many candidates for %s (%d)", needle, len(results)) +} + +// GetVolumeID returns exactly one volume matching +func (s *ScalewayAPI) GetVolumeID(needle string) (string, error) { + // Parses optional type prefix, i.e: "volume:name" -> "name" + _, needle = types.ParseNeedle(needle) + + volumes, err := s.ResolveVolume(needle) + if err != nil { + return "", fmt.Errorf("Unable to resolve volume %s: %s", needle, err) + } + if len(volumes) == 1 { + return volumes[0].Identifier, nil + } + if len(volumes) == 0 { + return "", fmt.Errorf("No such volume: %s", needle) + } + return "", showResolverResults(needle, volumes) +} + +// GetSnapshotID returns exactly one snapshot matching +func (s *ScalewayAPI) GetSnapshotID(needle string) (string, error) { + // Parses optional type prefix, i.e: "snapshot:name" -> "name" + _, needle = types.ParseNeedle(needle) + + snapshots, err := s.ResolveSnapshot(needle) + if err != nil { + return "", fmt.Errorf("Unable to resolve snapshot %s: %s", needle, err) + } + if len(snapshots) == 1 { + return snapshots[0].Identifier, nil + } + if len(snapshots) == 0 { + return "", fmt.Errorf("No such snapshot: %s", needle) + } + return "", showResolverResults(needle, snapshots) +} + +// FilterImagesByArch removes entry that doesn't match with architecture +func FilterImagesByArch(res types.ScalewayResolverResults, arch string) (ret types.ScalewayResolverResults) { + if arch == "*" { + return res + } + for _, result := range res { + if result.Arch == arch { + ret = append(ret, result) + } + } + return +} + +// FilterImagesByRegion removes entry that doesn't match with region +func FilterImagesByRegion(res types.ScalewayResolverResults, region string) (ret types.ScalewayResolverResults) { + if region == "*" { + return res + } + for _, result := range res { + if result.Region == region { + ret = append(ret, result) + } + } + return +} + +// GetImageID returns exactly one image matching +func (s *ScalewayAPI) GetImageID(needle, arch string) (*types.ScalewayImageIdentifier, error) { + // Parses optional type prefix, i.e: "image:name" -> "name" + _, needle = types.ParseNeedle(needle) + + images, err := s.ResolveImage(needle) + if err != nil { + return nil, fmt.Errorf("Unable to resolve image %s: %s", needle, err) + } + images = FilterImagesByArch(images, arch) + images = FilterImagesByRegion(images, s.Region) + if len(images) == 1 { + return &types.ScalewayImageIdentifier{ + Identifier: images[0].Identifier, + Arch: images[0].Arch, + // FIXME region, owner hardcoded + Region: images[0].Region, + Owner: "", + }, nil + } + if len(images) == 0 { + return nil, fmt.Errorf("No such image (zone %s, arch %s) : %s", s.Region, arch, needle) + } + return nil, showResolverResults(needle, images) +} + +// GetSecurityGroups returns a ScalewaySecurityGroups +func (s *ScalewayAPI) GetSecurityGroups() (*types.ScalewayGetSecurityGroups, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "security_groups", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var securityGroups types.ScalewayGetSecurityGroups + + if err = json.Unmarshal(body, &securityGroups); err != nil { + return nil, err + } + return &securityGroups, nil +} + +// GetSecurityGroupRules returns a ScalewaySecurityGroupRules +func (s *ScalewayAPI) GetSecurityGroupRules(groupID string) (*types.ScalewayGetSecurityGroupRules, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s/rules", groupID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var securityGroupRules types.ScalewayGetSecurityGroupRules + + if err = json.Unmarshal(body, &securityGroupRules); err != nil { + return nil, err + } + return &securityGroupRules, nil +} + +// GetASecurityGroupRule returns a ScalewaySecurityGroupRule +func (s *ScalewayAPI) GetASecurityGroupRule(groupID string, rulesID string) (*types.ScalewayGetSecurityGroupRule, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", groupID, rulesID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var securityGroupRules types.ScalewayGetSecurityGroupRule + + if err = json.Unmarshal(body, &securityGroupRules); err != nil { + return nil, err + } + return &securityGroupRules, nil +} + +// GetASecurityGroup returns a ScalewaySecurityGroup +func (s *ScalewayAPI) GetASecurityGroup(groupsID string) (*types.ScalewayGetSecurityGroup, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("security_groups/%s", groupsID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var securityGroups types.ScalewayGetSecurityGroup + + if err = json.Unmarshal(body, &securityGroups); err != nil { + return nil, err + } + return &securityGroups, nil +} + +// PostSecurityGroup posts a group on a server +func (s *ScalewayAPI) PostSecurityGroup(group types.ScalewayNewSecurityGroup) error { + resp, err := s.PostResponse(s.computeAPI, "security_groups", group) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusCreated}, resp) + return err +} + +// PostSecurityGroupRule posts a rule on a server +func (s *ScalewayAPI) PostSecurityGroupRule(SecurityGroupID string, rules types.ScalewayNewSecurityGroupRule) error { + resp, err := s.PostResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules", SecurityGroupID), rules) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusCreated}, resp) + return err +} + +// DeleteSecurityGroup deletes a SecurityGroup +func (s *ScalewayAPI) DeleteSecurityGroup(securityGroupID string) error { + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("security_groups/%s", securityGroupID)) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// PutSecurityGroup updates a SecurityGroup +func (s *ScalewayAPI) PutSecurityGroup(group types.ScalewayUpdateSecurityGroup, securityGroupID string) error { + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("security_groups/%s", securityGroupID), group) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// PutSecurityGroupRule updates a SecurityGroupRule +func (s *ScalewayAPI) PutSecurityGroupRule(rules types.ScalewayNewSecurityGroupRule, securityGroupID, RuleID string) error { + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", securityGroupID, RuleID), rules) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// DeleteSecurityGroupRule deletes a SecurityGroupRule +func (s *ScalewayAPI) DeleteSecurityGroupRule(SecurityGroupID, RuleID string) error { + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("security_groups/%s/rules/%s", SecurityGroupID, RuleID)) + if err != nil { + return err + } + defer resp.Body.Close() + + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// GetContainers returns a types.ScalewayGetContainers +func (s *ScalewayAPI) GetContainers() (*types.ScalewayGetContainers, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "containers", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var containers types.ScalewayGetContainers + + if err = json.Unmarshal(body, &containers); err != nil { + return nil, err + } + return &containers, nil +} + +// GetContainerDatas returns a types.ScalewayGetContainerDatas +func (s *ScalewayAPI) GetContainerDatas(container string) (*types.ScalewayGetContainerDatas, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("containers/%s", container), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var datas types.ScalewayGetContainerDatas + + if err = json.Unmarshal(body, &datas); err != nil { + return nil, err + } + return &datas, nil +} + +// GetIPS returns a types.ScalewayGetIPS +func (s *ScalewayAPI) GetIPS() (*types.ScalewayGetIPS, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "ips", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ips types.ScalewayGetIPS + + if err = json.Unmarshal(body, &ips); err != nil { + return nil, err + } + return &ips, nil +} + +// NewIP returns a new IP +func (s *ScalewayAPI) NewIP() (*types.ScalewayGetIP, error) { + var orga struct { + Organization string `json:"organization"` + } + orga.Organization = s.Organization + resp, err := s.PostResponse(s.computeAPI, "ips", orga) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusCreated}, resp) + if err != nil { + return nil, err + } + var ip types.ScalewayGetIP + + if err = json.Unmarshal(body, &ip); err != nil { + return nil, err + } + return &ip, nil +} + +// AttachIP attachs an IP to a server +func (s *ScalewayAPI) AttachIP(ipID, serverID string) error { + var update struct { + Address string `json:"address"` + ID string `json:"id"` + Reverse *string `json:"reverse"` + Organization string `json:"organization"` + Server string `json:"server"` + } + + ip, err := s.GetIP(ipID) + if err != nil { + return err + } + update.Address = ip.IP.Address + update.ID = ip.IP.ID + update.Organization = ip.IP.Organization + update.Server = serverID + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID), update) + if err != nil { + return err + } + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// DetachIP detaches an IP from a server +func (s *ScalewayAPI) DetachIP(ipID string) error { + ip, err := s.GetIP(ipID) + if err != nil { + return err + } + ip.IP.Server = nil + resp, err := s.PutResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID), ip.IP) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// DeleteIP deletes an IP +func (s *ScalewayAPI) DeleteIP(ipID string) error { + resp, err := s.DeleteResponse(s.computeAPI, fmt.Sprintf("ips/%s", ipID)) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// GetIP returns a types.ScalewayGetIP +func (s *ScalewayAPI) GetIP(ipID string) (*types.ScalewayGetIP, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, fmt.Sprintf("ips/%s", ipID), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ip types.ScalewayGetIP + + if err = json.Unmarshal(body, &ip); err != nil { + return nil, err + } + return &ip, nil +} + +// GetQuotas returns a types.ScalewayGetQuotas +func (s *ScalewayAPI) GetQuotas() (*types.ScalewayGetQuotas, error) { + resp, err := s.GetResponsePaginate(AccountAPI, fmt.Sprintf("organizations/%s/quotas", s.Organization), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var quotas types.ScalewayGetQuotas + + if err = json.Unmarshal(body, "as); err != nil { + return nil, err + } + return "as, nil +} + +// GetBootscriptID returns exactly one bootscript matching +func (s *ScalewayAPI) GetBootscriptID(needle, arch string) (string, error) { + // Parses optional type prefix, i.e: "bootscript:name" -> "name" + _, needle = types.ParseNeedle(needle) + + bootscripts, err := s.ResolveBootscript(needle) + if err != nil { + return "", fmt.Errorf("Unable to resolve bootscript %s: %s", needle, err) + } + bootscripts.FilterByArch(arch) + if len(bootscripts) == 1 { + return bootscripts[0].Identifier, nil + } + if len(bootscripts) == 0 { + return "", fmt.Errorf("No such bootscript: %s", needle) + } + return "", showResolverResults(needle, bootscripts) +} + +func rootNetDial(network, addr string) (net.Conn, error) { + dialer := net.Dialer{ + Timeout: 10 * time.Second, + KeepAlive: 10 * time.Second, + } + + // bruteforce privileged ports + var localAddr net.Addr + var err error + for port := 1; port <= 1024; port++ { + localAddr, err = net.ResolveTCPAddr("tcp", fmt.Sprintf(":%d", port)) + + // this should never happen + if err != nil { + return nil, err + } + + dialer.LocalAddr = localAddr + + conn, err := dialer.Dial(network, addr) + + // if err is nil, dialer.Dial succeed, so let's go + // else, err != nil, but we don't care + if err == nil { + return conn, nil + } + } + // if here, all privileged ports were tried without success + return nil, fmt.Errorf("bind: permission denied, are you root ?") +} + +// SetPassword register the password +func (s *ScalewayAPI) SetPassword(password string) { + s.password = password +} + +// GetMarketPlaceImages returns images from marketplace +func (s *ScalewayAPI) GetMarketPlaceImages(uuidImage string) (*types.MarketImages, error) { + resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%s", uuidImage), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ret types.MarketImages + + if uuidImage != "" { + ret.Images = make([]types.MarketImage, 1) + + var img types.MarketImage + + if err = json.Unmarshal(body, &img); err != nil { + return nil, err + } + ret.Images[0] = img + } else { + if err = json.Unmarshal(body, &ret); err != nil { + return nil, err + } + } + return &ret, nil +} + +// GetMarketPlaceImageVersions returns image version +func (s *ScalewayAPI) GetMarketPlaceImageVersions(uuidImage, uuidVersion string) (*types.MarketVersions, error) { + resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s", uuidImage, uuidVersion), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ret types.MarketVersions + + if uuidImage != "" { + var version types.MarketVersion + ret.Versions = make([]types.MarketVersionDefinition, 1) + + if err = json.Unmarshal(body, &version); err != nil { + return nil, err + } + ret.Versions[0] = version.Version + } else { + if err = json.Unmarshal(body, &ret); err != nil { + return nil, err + } + } + return &ret, nil +} + +// GetMarketPlaceImageCurrentVersion return the image current version +func (s *ScalewayAPI) GetMarketPlaceImageCurrentVersion(uuidImage string) (*types.MarketVersion, error) { + resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/current", uuidImage), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ret types.MarketVersion + + if err = json.Unmarshal(body, &ret); err != nil { + return nil, err + } + return &ret, nil +} + +// GetMarketPlaceLocalImages returns images from local region +func (s *ScalewayAPI) GetMarketPlaceLocalImages(uuidImage, uuidVersion, uuidLocalImage string) (*types.MarketLocalImages, error) { + resp, err := s.GetResponsePaginate(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%s", uuidImage, uuidVersion, uuidLocalImage), url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + var ret types.MarketLocalImages + if uuidLocalImage != "" { + var localImage types.MarketLocalImage + ret.LocalImages = make([]types.MarketLocalImageDefinition, 1) + + if err = json.Unmarshal(body, &localImage); err != nil { + return nil, err + } + ret.LocalImages[0] = localImage.LocalImages + } else { + if err = json.Unmarshal(body, &ret); err != nil { + return nil, err + } + } + return &ret, nil +} + +// PostMarketPlaceImage adds new image +func (s *ScalewayAPI) PostMarketPlaceImage(images types.MarketImage) error { + resp, err := s.PostResponse(MarketplaceAPI, "images/", images) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) + return err +} + +// PostMarketPlaceImageVersion adds new image version +func (s *ScalewayAPI) PostMarketPlaceImageVersion(uuidImage string, version types.MarketVersion) error { + resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions", uuidImage), version) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) + return err +} + +// PostMarketPlaceLocalImage adds new local image +func (s *ScalewayAPI) PostMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string, local types.MarketLocalImage) error { + resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage), local) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusAccepted}, resp) + return err +} + +// PutMarketPlaceImage updates image +func (s *ScalewayAPI) PutMarketPlaceImage(uudiImage string, images types.MarketImage) error { + resp, err := s.PutResponse(MarketplaceAPI, fmt.Sprintf("images/%v", uudiImage), images) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// PutMarketPlaceImageVersion updates image version +func (s *ScalewayAPI) PutMarketPlaceImageVersion(uuidImage, uuidVersion string, version types.MarketVersion) error { + resp, err := s.PutResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%v", uuidImage, uuidVersion), version) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// PutMarketPlaceLocalImage updates local image +func (s *ScalewayAPI) PutMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string, local types.MarketLocalImage) error { + resp, err := s.PostResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage), local) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusOK}, resp) + return err +} + +// DeleteMarketPlaceImage deletes image +func (s *ScalewayAPI) DeleteMarketPlaceImage(uudImage string) error { + resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v", uudImage)) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// DeleteMarketPlaceImageVersion delete image version +func (s *ScalewayAPI) DeleteMarketPlaceImageVersion(uuidImage, uuidVersion string) error { + resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%v", uuidImage, uuidVersion)) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// DeleteMarketPlaceLocalImage deletes local image +func (s *ScalewayAPI) DeleteMarketPlaceLocalImage(uuidImage, uuidVersion, uuidLocalImage string) error { + resp, err := s.DeleteResponse(MarketplaceAPI, fmt.Sprintf("images/%v/versions/%s/local_images/%v", uuidImage, uuidVersion, uuidLocalImage)) + if err != nil { + return err + } + defer resp.Body.Close() + _, err = s.handleHTTPError([]int{http.StatusNoContent}, resp) + return err +} + +// ResolveTTYUrl return an URL to get a tty +func (s *ScalewayAPI) ResolveTTYUrl() string { + switch s.Region { + case "par1", "": + return "https://tty-par1.scaleway.com/v2/" + case "ams1": + return "https://tty-ams1.scaleway.com" + } + return "" +} + +// GetProductServers Fetches all the server type and their constraints from the Products API +func (s *ScalewayAPI) GetProductsServers() (*types.ScalewayProductsServers, error) { + resp, err := s.GetResponsePaginate(s.computeAPI, "products/servers", url.Values{}) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := s.handleHTTPError([]int{http.StatusOK}, resp) + if err != nil { + return nil, err + } + + var productServers types.ScalewayProductsServers + if err = json.Unmarshal(body, &productServers); err != nil { + return nil, err + } + + return &productServers, nil +} + +// HideAPICredentials removes API credentials from a string +func (s *ScalewayAPI) HideAPICredentials(input string) string { + output := input + if s.Token != "" { + output = strings.Replace(output, s.Token, "00000000-0000-4000-8000-000000000000", -1) + } + if s.Organization != "" { + output = strings.Replace(output, s.Organization, "00000000-0000-5000-9000-000000000000", -1) + } + if s.password != "" { + output = strings.Replace(output, s.password, "XX-XX-XX-XX", -1) + } + return output +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/cache/cache.go b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/cache/cache.go new file mode 100644 index 000000000..91023585e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/cache/cache.go @@ -0,0 +1,693 @@ +// Copyright (C) 2018 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package cache + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + "sync" + + "github.com/moul/anonuuid" + "github.com/renstrom/fuzzysearch/fuzzy" + "github.com/scaleway/go-scaleway/types" +) + +const ( + // CacheRegion permits to access at the region field + CacheRegion = iota + // CacheArch permits to access at the arch field + CacheArch + // CacheOwner permits to access at the owner field + CacheOwner + // CacheTitle permits to access at the title field + CacheTitle + // CacheMarketPlaceUUID is used to determine the UUID of local images + CacheMarketPlaceUUID + // CacheMaxfield is used to determine the size of array + CacheMaxfield +) + +// ScalewayCache is used not to query the API to resolve full identifiers +type ScalewayCache struct { + // Images contains names of Scaleway images indexed by identifier + Images map[string][CacheMaxfield]string `json:"images"` + + // Snapshots contains names of Scaleway snapshots indexed by identifier + Snapshots map[string][CacheMaxfield]string `json:"snapshots"` + + // Volumes contains names of Scaleway volumes indexed by identifier + Volumes map[string][CacheMaxfield]string `json:"volumes"` + + // Bootscripts contains names of Scaleway bootscripts indexed by identifier + Bootscripts map[string][CacheMaxfield]string `json:"bootscripts"` + + // Servers contains names of Scaleway servers indexed by identifier + Servers map[string][CacheMaxfield]string `json:"servers"` + + // Path is the path to the cache file + Path string `json:"-"` + + // Modified tells if the cache needs to be overwritten or not + Modified bool `json:"-"` + + // Lock allows ScalewayCache to be used concurrently + Lock sync.Mutex `json:"-"` + + hookSave func() +} + +// NewScalewayCache loads a per-user cache +func NewScalewayCache(hookSave func()) (*ScalewayCache, error) { + var cache ScalewayCache + + cache.hookSave = hookSave + homeDir := os.Getenv("HOME") // *nix + if homeDir == "" { // Windows + homeDir = os.Getenv("USERPROFILE") + } + if homeDir == "" { + homeDir = "/tmp" + } + cachePath := filepath.Join(homeDir, ".scw-cache.db") + cache.Path = cachePath + _, err := os.Stat(cachePath) + if os.IsNotExist(err) { + cache.Clear() + return &cache, nil + } else if err != nil { + return nil, err + } + file, err := ioutil.ReadFile(cachePath) + if err != nil { + return nil, err + } + err = json.Unmarshal(file, &cache) + if err != nil { + // fix compatibility with older version + if err = os.Remove(cachePath); err != nil { + return nil, err + } + cache.Clear() + return &cache, nil + } + if cache.Images == nil { + cache.Images = make(map[string][CacheMaxfield]string) + } + if cache.Snapshots == nil { + cache.Snapshots = make(map[string][CacheMaxfield]string) + } + if cache.Volumes == nil { + cache.Volumes = make(map[string][CacheMaxfield]string) + } + if cache.Servers == nil { + cache.Servers = make(map[string][CacheMaxfield]string) + } + if cache.Bootscripts == nil { + cache.Bootscripts = make(map[string][CacheMaxfield]string) + } + return &cache, nil +} + +// Clear removes all information from the cache +func (c *ScalewayCache) Clear() { + c.Images = make(map[string][CacheMaxfield]string) + c.Snapshots = make(map[string][CacheMaxfield]string) + c.Volumes = make(map[string][CacheMaxfield]string) + c.Bootscripts = make(map[string][CacheMaxfield]string) + c.Servers = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// Flush flushes the cache database +func (c *ScalewayCache) Flush() error { + return os.Remove(c.Path) +} + +// Save atomically overwrites the current cache database +func (c *ScalewayCache) Save() error { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.hookSave() + if c.Modified { + file, err := ioutil.TempFile(filepath.Dir(c.Path), filepath.Base(c.Path)) + if err != nil { + return err + } + + if err := json.NewEncoder(file).Encode(c); err != nil { + file.Close() + os.Remove(file.Name()) + return err + } + + file.Close() + if err := os.Rename(file.Name(), c.Path); err != nil { + os.Remove(file.Name()) + return err + } + } + return nil +} + +// ComputeRankMatch fills `ScalewayResolverResult.RankMatch` with its `fuzzy` score +func ComputeRankMatch(s *types.ScalewayResolverResult, needle string) { + s.Needle = needle + s.RankMatch = fuzzy.RankMatch(needle, s.Name) +} + +// LookUpImages attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpImages(needle string, acceptUUID bool) (types.ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res types.ScalewayResolverResults + var exactMatches types.ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Images[needle]; ok { + entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierImage) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "") + // FIXME: if 'user/' is in needle, only watch for a user image + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Images { + if fields[CacheTitle] == needle { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierImage) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierImage) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } else if strings.HasPrefix(fields[CacheMarketPlaceUUID], needle) || nameRegex.MatchString(fields[CacheMarketPlaceUUID]) { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierImage) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// LookUpSnapshots attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpSnapshots(needle string, acceptUUID bool) (types.ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res types.ScalewayResolverResults + var exactMatches types.ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Snapshots[needle]; ok { + entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierSnapshot) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + needle = regexp.MustCompile(`^user/`).ReplaceAllString(needle, "") + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Snapshots { + if fields[CacheTitle] == needle { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierSnapshot) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierSnapshot) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// LookUpVolumes attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpVolumes(needle string, acceptUUID bool) (types.ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res types.ScalewayResolverResults + var exactMatches types.ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Volumes[needle]; ok { + entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierVolume) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Volumes { + if fields[CacheTitle] == needle { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierVolume) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierVolume) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// LookUpBootscripts attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpBootscripts(needle string, acceptUUID bool) (types.ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res types.ScalewayResolverResults + var exactMatches types.ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Bootscripts[needle]; ok { + entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierBootscript) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Bootscripts { + if fields[CacheTitle] == needle { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierBootscript) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierBootscript) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// LookUpServers attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpServers(needle string, acceptUUID bool) (types.ScalewayResolverResults, error) { + c.Lock.Lock() + defer c.Lock.Unlock() + + var res types.ScalewayResolverResults + var exactMatches types.ScalewayResolverResults + + if acceptUUID && anonuuid.IsUUID(needle) == nil { + if fields, ok := c.Servers[needle]; ok { + entry, err := types.NewScalewayResolverResult(needle, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierServer) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + nameRegex := regexp.MustCompile(`(?i)` + regexp.MustCompile(`[_-]`).ReplaceAllString(needle, ".*")) + for identifier, fields := range c.Servers { + if fields[CacheTitle] == needle { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierServer) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + exactMatches = append(exactMatches, entry) + } + if strings.HasPrefix(identifier, needle) || nameRegex.MatchString(fields[CacheTitle]) { + entry, err := types.NewScalewayResolverResult(identifier, fields[CacheTitle], fields[CacheArch], fields[CacheRegion], types.IdentifierServer) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + res = append(res, entry) + } + } + + if len(exactMatches) == 1 { + return exactMatches, nil + } + + return removeDuplicatesResults(res), nil +} + +// removeDuplicatesResults transforms an array into a unique array +func removeDuplicatesResults(elements types.ScalewayResolverResults) types.ScalewayResolverResults { + encountered := map[string]types.ScalewayResolverResult{} + + // Create a map of all unique elements. + for v := range elements { + encountered[elements[v].Identifier] = elements[v] + } + + // Place all keys from the map into a slice. + results := types.ScalewayResolverResults{} + for _, result := range encountered { + results = append(results, result) + } + return results +} + +// LookUpIdentifiers attempts to return identifiers matching a pattern +func (c *ScalewayCache) LookUpIdentifiers(needle string) (types.ScalewayResolverResults, error) { + results := types.ScalewayResolverResults{} + + identifierType, needle := types.ParseNeedle(needle) + + if identifierType&(types.IdentifierUnknown|types.IdentifierServer) > 0 { + servers, err := c.LookUpServers(needle, false) + if err != nil { + return types.ScalewayResolverResults{}, err + } + for _, result := range servers { + entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierServer) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + results = append(results, entry) + } + } + + if identifierType&(types.IdentifierUnknown|types.IdentifierImage) > 0 { + images, err := c.LookUpImages(needle, false) + if err != nil { + return types.ScalewayResolverResults{}, err + } + for _, result := range images { + entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierImage) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + results = append(results, entry) + } + } + + if identifierType&(types.IdentifierUnknown|types.IdentifierSnapshot) > 0 { + snapshots, err := c.LookUpSnapshots(needle, false) + if err != nil { + return types.ScalewayResolverResults{}, err + } + for _, result := range snapshots { + entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierSnapshot) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + results = append(results, entry) + } + } + + if identifierType&(types.IdentifierUnknown|types.IdentifierVolume) > 0 { + volumes, err := c.LookUpVolumes(needle, false) + if err != nil { + return types.ScalewayResolverResults{}, err + } + for _, result := range volumes { + entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierVolume) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + results = append(results, entry) + } + } + + if identifierType&(types.IdentifierUnknown|types.IdentifierBootscript) > 0 { + bootscripts, err := c.LookUpBootscripts(needle, false) + if err != nil { + return types.ScalewayResolverResults{}, err + } + for _, result := range bootscripts { + entry, err := types.NewScalewayResolverResult(result.Identifier, result.Name, result.Arch, result.Region, types.IdentifierBootscript) + if err != nil { + return types.ScalewayResolverResults{}, err + } + ComputeRankMatch(&entry, needle) + results = append(results, entry) + } + } + return results, nil +} + +// InsertServer registers a server in the cache +func (c *ScalewayCache) InsertServer(identifier, region, arch, owner, name string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Servers[identifier] + if !exists || fields[CacheTitle] != name { + c.Servers[identifier] = [CacheMaxfield]string{region, arch, owner, name} + c.Modified = true + } +} + +// RemoveServer removes a server from the cache +func (c *ScalewayCache) RemoveServer(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Servers, identifier) + c.Modified = true +} + +// ClearServers removes all servers from the cache +func (c *ScalewayCache) ClearServers() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Servers = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// InsertImage registers an image in the cache +func (c *ScalewayCache) InsertImage(identifier, region, arch, owner, name, marketPlaceUUID string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Images[identifier] + if !exists || fields[CacheTitle] != name { + c.Images[identifier] = [CacheMaxfield]string{region, arch, owner, name, marketPlaceUUID} + c.Modified = true + } +} + +// RemoveImage removes a server from the cache +func (c *ScalewayCache) RemoveImage(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Images, identifier) + c.Modified = true +} + +// ClearImages removes all images from the cache +func (c *ScalewayCache) ClearImages() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Images = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// InsertSnapshot registers an snapshot in the cache +func (c *ScalewayCache) InsertSnapshot(identifier, region, arch, owner, name string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Snapshots[identifier] + if !exists || fields[CacheTitle] != name { + c.Snapshots[identifier] = [CacheMaxfield]string{region, arch, owner, name} + c.Modified = true + } +} + +// RemoveSnapshot removes a server from the cache +func (c *ScalewayCache) RemoveSnapshot(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Snapshots, identifier) + c.Modified = true +} + +// ClearSnapshots removes all snapshots from the cache +func (c *ScalewayCache) ClearSnapshots() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Snapshots = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// InsertVolume registers an volume in the cache +func (c *ScalewayCache) InsertVolume(identifier, region, arch, owner, name string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Volumes[identifier] + if !exists || fields[CacheTitle] != name { + c.Volumes[identifier] = [CacheMaxfield]string{region, arch, owner, name} + c.Modified = true + } +} + +// RemoveVolume removes a server from the cache +func (c *ScalewayCache) RemoveVolume(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Volumes, identifier) + c.Modified = true +} + +// ClearVolumes removes all volumes from the cache +func (c *ScalewayCache) ClearVolumes() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Volumes = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// InsertBootscript registers an bootscript in the cache +func (c *ScalewayCache) InsertBootscript(identifier, region, arch, owner, name string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + fields, exists := c.Bootscripts[identifier] + if !exists || fields[CacheTitle] != name { + c.Bootscripts[identifier] = [CacheMaxfield]string{region, arch, owner, name} + c.Modified = true + } +} + +// RemoveBootscript removes a bootscript from the cache +func (c *ScalewayCache) RemoveBootscript(identifier string) { + c.Lock.Lock() + defer c.Lock.Unlock() + + delete(c.Bootscripts, identifier) + c.Modified = true +} + +// ClearBootscripts removes all bootscripts from the cache +func (c *ScalewayCache) ClearBootscripts() { + c.Lock.Lock() + defer c.Lock.Unlock() + + c.Bootscripts = make(map[string][CacheMaxfield]string) + c.Modified = true +} + +// GetNbServers returns the number of servers in the cache +func (c *ScalewayCache) GetNbServers() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Servers) +} + +// GetNbImages returns the number of images in the cache +func (c *ScalewayCache) GetNbImages() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Images) +} + +// GetNbSnapshots returns the number of snapshots in the cache +func (c *ScalewayCache) GetNbSnapshots() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Snapshots) +} + +// GetNbVolumes returns the number of volumes in the cache +func (c *ScalewayCache) GetNbVolumes() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Volumes) +} + +// GetNbBootscripts returns the number of bootscripts in the cache +func (c *ScalewayCache) GetNbBootscripts() int { + c.Lock.Lock() + defer c.Lock.Unlock() + + return len(c.Bootscripts) +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/logger/logger.go b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/logger/logger.go new file mode 100644 index 000000000..893cfd7a0 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/logger/logger.go @@ -0,0 +1,77 @@ +// Copyright (C) 2018 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package logger + +import ( + "fmt" + "log" + "net/http" + "os" +) + +// Logger handles logging concerns for the Scaleway API SDK +type Logger interface { + LogHTTP(*http.Request) + Fatalf(format string, v ...interface{}) + Debugf(format string, v ...interface{}) + Infof(format string, v ...interface{}) + Warnf(format string, v ...interface{}) +} + +// NewDefaultLogger returns a logger which is configured for stdout +func NewDefaultLogger() Logger { + return &defaultLogger{ + Logger: log.New(os.Stdout, "", log.LstdFlags), + } +} + +type defaultLogger struct { + *log.Logger +} + +func (l *defaultLogger) LogHTTP(r *http.Request) { + l.Printf("%s %s\n", r.Method, r.URL.RawPath) +} + +func (l *defaultLogger) Fatalf(format string, v ...interface{}) { + l.Printf("[FATAL] %s\n", fmt.Sprintf(format, v)) + os.Exit(1) +} + +func (l *defaultLogger) Debugf(format string, v ...interface{}) { + l.Printf("[DEBUG] %s\n", fmt.Sprintf(format, v)) +} + +func (l *defaultLogger) Infof(format string, v ...interface{}) { + l.Printf("[INFO ] %s\n", fmt.Sprintf(format, v)) +} + +func (l *defaultLogger) Warnf(format string, v ...interface{}) { + l.Printf("[WARN ] %s\n", fmt.Sprintf(format, v)) +} + +type disableLogger struct { +} + +// NewDisableLogger returns a logger which is configured to do nothing +func NewDisableLogger() Logger { + return &disableLogger{} +} + +func (d *disableLogger) LogHTTP(r *http.Request) { +} + +func (d *disableLogger) Fatalf(format string, v ...interface{}) { + panic(fmt.Sprintf(format, v)) +} + +func (d *disableLogger) Debugf(format string, v ...interface{}) { +} + +func (d *disableLogger) Infof(format string, v ...interface{}) { +} + +func (d *disableLogger) Warnf(format string, v ...interface{}) { +} diff --git a/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/types/types.go b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/types/types.go new file mode 100644 index 000000000..73fbc1348 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/scaleway/go-scaleway/types/types.go @@ -0,0 +1,983 @@ +// Copyright (C) 2018 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package types + +import ( + "bytes" + "encoding/json" + "fmt" + "html/template" + "regexp" + "strings" + "time" + + "github.com/moul/anonuuid" +) + +// ParseNeedle parses a user needle and try to extract a forced object type +// i.e: +// - server:blah-blah -> kind=server, needle=blah-blah +// - blah-blah -> kind="", needle=blah-blah +// - not-existing-type:blah-blah +func ParseNeedle(input string) (identifierType int, needle string) { + parts := strings.Split(input, ":") + if len(parts) == 2 { + switch parts[0] { + case "server": + return IdentifierServer, parts[1] + case "image": + return IdentifierImage, parts[1] + case "snapshot": + return IdentifierSnapshot, parts[1] + case "bootscript": + return IdentifierBootscript, parts[1] + case "volume": + return IdentifierVolume, parts[1] + } + } + return IdentifierUnknown, input +} + +const ( + // IdentifierUnknown is used when we don't know explicitly the type key of the object (used for nil comparison) + IdentifierUnknown = 1 << iota + // IdentifierServer is the type key of cached server objects + IdentifierServer + // IdentifierImage is the type key of cached image objects + IdentifierImage + // IdentifierSnapshot is the type key of cached snapshot objects + IdentifierSnapshot + // IdentifierBootscript is the type key of cached bootscript objects + IdentifierBootscript + // IdentifierVolume is the type key of cached volume objects + IdentifierVolume +) + +// ScalewayResolverResult is a structure containing human-readable information +// about resolver results. This structure is used to display the user choices. +type ScalewayResolverResult struct { + Identifier string + Type int + Name string + Arch string + Needle string + RankMatch int + Region string +} + +// ScalewayResolverResults is a list of `ScalewayResolverResult` +type ScalewayResolverResults []ScalewayResolverResult + +// NewScalewayResolverResult returns a new ScalewayResolverResult +func NewScalewayResolverResult(Identifier, Name, Arch, Region string, Type int) (ScalewayResolverResult, error) { + if err := anonuuid.IsUUID(Identifier); err != nil { + return ScalewayResolverResult{}, err + } + return ScalewayResolverResult{ + Identifier: Identifier, + Type: Type, + Name: Name, + Arch: Arch, + Region: Region, + }, nil +} + +func (s ScalewayResolverResults) Len() int { + return len(s) +} + +func (s ScalewayResolverResults) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s ScalewayResolverResults) Less(i, j int) bool { + return s[i].RankMatch < s[j].RankMatch +} + +// TruncIdentifier returns first 8 characters of an Identifier (UUID) +func (s *ScalewayResolverResult) TruncIdentifier() string { + return s.Identifier[:8] +} + +func identifierTypeName(kind int) string { + switch kind { + case IdentifierServer: + return "Server" + case IdentifierImage: + return "Image" + case IdentifierSnapshot: + return "Snapshot" + case IdentifierVolume: + return "Volume" + case IdentifierBootscript: + return "Bootscript" + } + return "" +} + +// CodeName returns a full resource name with typed prefix +func (s *ScalewayResolverResult) CodeName() string { + name := strings.ToLower(s.Name) + name = regexp.MustCompile(`[^a-z0-9-]`).ReplaceAllString(name, "-") + name = regexp.MustCompile(`--+`).ReplaceAllString(name, "-") + name = strings.Trim(name, "-") + + return fmt.Sprintf("%s:%s", strings.ToLower(identifierTypeName(s.Type)), name) +} + +// FilterByArch deletes the elements which not match with arch +func (s *ScalewayResolverResults) FilterByArch(arch string) { +REDO: + for i := range *s { + if (*s)[i].Arch != arch { + (*s)[i] = (*s)[len(*s)-1] + *s = (*s)[:len(*s)-1] + goto REDO + } + } +} + +// ScalewayAPIError represents a Scaleway API Error +type ScalewayAPIError struct { + // Message is a human-friendly error message + APIMessage string `json:"message,omitempty"` + + // Type is a string code that defines the kind of error + Type string `json:"type,omitempty"` + + // Fields contains detail about validation error + Fields map[string][]string `json:"fields,omitempty"` + + // StatusCode is the HTTP status code received + StatusCode int `json:"-"` + + // Message + Message string `json:"-"` +} + +// Error returns a string representing the error +func (e ScalewayAPIError) Error() string { + var b bytes.Buffer + + fmt.Fprintf(&b, "StatusCode: %v, ", e.StatusCode) + fmt.Fprintf(&b, "Type: %v, ", e.Type) + fmt.Fprintf(&b, "APIMessage: \x1b[31m%v\x1b[0m", e.APIMessage) + if len(e.Fields) > 0 { + fmt.Fprintf(&b, ", Details: %v", e.Fields) + } + return b.String() +} + +// ScalewayIPAddress represents a Scaleway IP address +type ScalewayIPAddress struct { + // Identifier is a unique identifier for the IP address + Identifier string `json:"id,omitempty"` + + // IP is an IPv4 address + IP string `json:"address,omitempty"` + + // Dynamic is a flag that defines an IP that change on each reboot + Dynamic *bool `json:"dynamic,omitempty"` +} + +// ScalewayVolume represents a Scaleway Volume +type ScalewayVolume struct { + // Identifier is a unique identifier for the volume + Identifier string `json:"id,omitempty"` + + // Size is the allocated size of the volume + Size uint64 `json:"size,omitempty"` + + // CreationDate is the creation date of the volume + CreationDate string `json:"creation_date,omitempty"` + + // ModificationDate is the date of the last modification of the volume + ModificationDate string `json:"modification_date,omitempty"` + + // Organization is the organization owning the volume + Organization string `json:"organization,omitempty"` + + // Name is the name of the volume + Name string `json:"name,omitempty"` + + // Server is the server using this image + Server *struct { + Identifier string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + } `json:"server,omitempty"` + + // VolumeType is a Scaleway identifier for the kind of volume (default: l_ssd) + VolumeType string `json:"volume_type,omitempty"` + + // ExportURI represents the url used by initrd/scripts to attach the volume + ExportURI string `json:"export_uri,omitempty"` +} + +// ScalewayOneVolume represents the response of a GET /volumes/UUID API call +type ScalewayOneVolume struct { + Volume ScalewayVolume `json:"volume,omitempty"` +} + +// ScalewayVolumes represents a group of Scaleway volumes +type ScalewayVolumes struct { + // Volumes holds scaleway volumes of the response + Volumes []ScalewayVolume `json:"volumes,omitempty"` +} + +// ScalewayVolumeDefinition represents a Scaleway volume definition +type ScalewayVolumeDefinition struct { + // Name is the user-defined name of the volume + Name string `json:"name"` + + // Image is the image used by the volume + Size uint64 `json:"size"` + + // Bootscript is the bootscript used by the volume + Type string `json:"volume_type"` + + // Organization is the owner of the volume + Organization string `json:"organization"` +} + +// ScalewayVolumePutDefinition represents a Scaleway volume with nullable fields (for PUT) +type ScalewayVolumePutDefinition struct { + Identifier *string `json:"id,omitempty"` + Size *uint64 `json:"size,omitempty"` + CreationDate *string `json:"creation_date,omitempty"` + ModificationDate *string `json:"modification_date,omitempty"` + Organization *string `json:"organization,omitempty"` + Name *string `json:"name,omitempty"` + Server struct { + Identifier *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + } `json:"server,omitempty"` + VolumeType *string `json:"volume_type,omitempty"` + ExportURI *string `json:"export_uri,omitempty"` +} + +// ScalewayImage represents a Scaleway Image +type ScalewayImage struct { + // Identifier is a unique identifier for the image + Identifier string `json:"id,omitempty"` + + // Name is a user-defined name for the image + Name string `json:"name,omitempty"` + + // CreationDate is the creation date of the image + CreationDate string `json:"creation_date,omitempty"` + + // ModificationDate is the date of the last modification of the image + ModificationDate string `json:"modification_date,omitempty"` + + // RootVolume is the root volume bound to the image + RootVolume ScalewayVolume `json:"root_volume,omitempty"` + + // Public is true for public images and false for user images + Public bool `json:"public,omitempty"` + + // Bootscript is the bootscript bound to the image + DefaultBootscript *ScalewayBootscript `json:"default_bootscript,omitempty"` + + // Organization is the owner of the image + Organization string `json:"organization,omitempty"` + + // Arch is the architecture target of the image + Arch string `json:"arch,omitempty"` + + // FIXME: extra_volumes +} + +// ScalewayImageIdentifier represents a Scaleway Image Identifier +type ScalewayImageIdentifier struct { + Identifier string + Arch string + Region string + Owner string +} + +// ScalewayOneImage represents the response of a GET /images/UUID API call +type ScalewayOneImage struct { + Image ScalewayImage `json:"image,omitempty"` +} + +// ScalewayImages represents a group of Scaleway images +type ScalewayImages struct { + // Images holds scaleway images of the response + Images []ScalewayImage `json:"images,omitempty"` +} + +// ProductNetworkInterface gives interval and external allowed bandwidth +type ProductNetworkInterface struct { + InternalBandwidth uint64 `json:"internal_bandwidth,omitempty"` + InternetBandwidth uint64 `json:"internet_bandwidth,omitempty"` +} + +// ProductNetwork lists all the network interfaces +type ProductNetwork struct { + Interfaces []ProductNetworkInterface `json:"interfaces,omitempty"` + TotalInternalBandwidth uint64 `json:"sum_internal_bandwidth,omitempty"` + TotalInternetBandwidth uint64 `json:"sum_internet_bandwidth,omitempty"` + IPv6_Support bool `json:"ipv6_support,omitempty"` +} + +// ProductVolumeConstraint contains any volume constraint that the offer has +type ProductVolumeConstraint struct { + MinSize uint64 `json:"min_size,omitempty"` + MaxSize uint64 `json:"max_size,omitempty"` +} + +// ProductServerOffer represents a specific offer +type ProductServer struct { + Arch string `json:"arch,omitempty"` + Ncpus uint64 `json:"ncpus,omitempty"` + Ram uint64 `json:"ram,omitempty"` + Baremetal bool `json:"baremetal,omitempty"` + VolumesConstraint ProductVolumeConstraint `json:"volumes_constraint,omitempty"` + AltNames []string `json:"alt_names,omitempty"` + Network ProductNetwork `json:"network,omitempty"` +} + +// Products holds a map of all Scaleway servers +type ScalewayProductsServers struct { + Servers map[string]ProductServer `json:"servers"` +} + +// ScalewaySnapshot represents a Scaleway Snapshot +type ScalewaySnapshot struct { + // Identifier is a unique identifier for the snapshot + Identifier string `json:"id,omitempty"` + + // Name is a user-defined name for the snapshot + Name string `json:"name,omitempty"` + + // CreationDate is the creation date of the snapshot + CreationDate string `json:"creation_date,omitempty"` + + // ModificationDate is the date of the last modification of the snapshot + ModificationDate string `json:"modification_date,omitempty"` + + // Size is the allocated size of the volume + Size uint64 `json:"size,omitempty"` + + // Organization is the owner of the snapshot + Organization string `json:"organization"` + + // State is the current state of the snapshot + State string `json:"state"` + + // VolumeType is the kind of volume behind the snapshot + VolumeType string `json:"volume_type"` + + // BaseVolume is the volume from which the snapshot inherits + BaseVolume ScalewayVolume `json:"base_volume,omitempty"` +} + +// ScalewayOneSnapshot represents the response of a GET /snapshots/UUID API call +type ScalewayOneSnapshot struct { + Snapshot ScalewaySnapshot `json:"snapshot,omitempty"` +} + +// ScalewaySnapshots represents a group of Scaleway snapshots +type ScalewaySnapshots struct { + // Snapshots holds scaleway snapshots of the response + Snapshots []ScalewaySnapshot `json:"snapshots,omitempty"` +} + +// ScalewayBootscript represents a Scaleway Bootscript +type ScalewayBootscript struct { + Bootcmdargs string `json:"bootcmdargs,omitempty"` + Dtb string `json:"dtb,omitempty"` + Initrd string `json:"initrd,omitempty"` + Kernel string `json:"kernel,omitempty"` + + // Arch is the architecture target of the bootscript + Arch string `json:"architecture,omitempty"` + + // Identifier is a unique identifier for the bootscript + Identifier string `json:"id,omitempty"` + + // Organization is the owner of the bootscript + Organization string `json:"organization,omitempty"` + + // Name is a user-defined name for the bootscript + Title string `json:"title,omitempty"` + + // Public is true for public bootscripts and false for user bootscripts + Public bool `json:"public,omitempty"` + + Default bool `json:"default,omitempty"` +} + +// ScalewayOneBootscript represents the response of a GET /bootscripts/UUID API call +type ScalewayOneBootscript struct { + Bootscript ScalewayBootscript `json:"bootscript,omitempty"` +} + +// ScalewayBootscripts represents a group of Scaleway bootscripts +type ScalewayBootscripts struct { + // Bootscripts holds Scaleway bootscripts of the response + Bootscripts []ScalewayBootscript `json:"bootscripts,omitempty"` +} + +// ScalewayTask represents a Scaleway Task +type ScalewayTask struct { + // Identifier is a unique identifier for the task + Identifier string `json:"id,omitempty"` + + // StartDate is the start date of the task + StartDate string `json:"started_at,omitempty"` + + // TerminationDate is the termination date of the task + TerminationDate string `json:"terminated_at,omitempty"` + + HrefFrom string `json:"href_from,omitempty"` + + Description string `json:"description,omitempty"` + + Status string `json:"status,omitempty"` + + Progress int `json:"progress,omitempty"` +} + +// ScalewayOneTask represents the response of a GET /tasks/UUID API call +type ScalewayOneTask struct { + Task ScalewayTask `json:"task,omitempty"` +} + +// ScalewayTasks represents a group of Scaleway tasks +type ScalewayTasks struct { + // Tasks holds scaleway tasks of the response + Tasks []ScalewayTask `json:"tasks,omitempty"` +} + +// ScalewaySecurityGroupRule definition +type ScalewaySecurityGroupRule struct { + Direction string `json:"direction"` + Protocol string `json:"protocol"` + IPRange string `json:"ip_range"` + DestPortFrom int `json:"dest_port_from,omitempty"` + Action string `json:"action"` + Position int `json:"position"` + DestPortTo string `json:"dest_port_to"` + Editable bool `json:"editable"` + ID string `json:"id"` +} + +// ScalewayGetSecurityGroupRules represents the response of a GET /security_group/{groupID}/rules +type ScalewayGetSecurityGroupRules struct { + Rules []ScalewaySecurityGroupRule `json:"rules"` +} + +// ScalewayGetSecurityGroupRule represents the response of a GET /security_group/{groupID}/rules/{ruleID} +type ScalewayGetSecurityGroupRule struct { + Rules ScalewaySecurityGroupRule `json:"rule"` +} + +// ScalewayNewSecurityGroupRule definition POST/PUT request /security_group/{groupID} +type ScalewayNewSecurityGroupRule struct { + Action string `json:"action"` + Direction string `json:"direction"` + IPRange string `json:"ip_range"` + Protocol string `json:"protocol"` + DestPortFrom int `json:"dest_port_from,omitempty"` +} + +// ScalewaySecurityGroups definition +type ScalewaySecurityGroups struct { + Description string `json:"description"` + ID string `json:"id"` + Organization string `json:"organization"` + Name string `json:"name"` + Servers []ScalewaySecurityGroup `json:"servers"` + EnableDefaultSecurity bool `json:"enable_default_security"` + OrganizationDefault bool `json:"organization_default"` +} + +// ScalewayGetSecurityGroups represents the response of a GET /security_groups/ +type ScalewayGetSecurityGroups struct { + SecurityGroups []ScalewaySecurityGroups `json:"security_groups"` +} + +// ScalewayGetSecurityGroup represents the response of a GET /security_groups/{groupID} +type ScalewayGetSecurityGroup struct { + SecurityGroups ScalewaySecurityGroups `json:"security_group"` +} + +// ScalewayIPDefinition represents the IP's fields +type ScalewayIPDefinition struct { + Organization string `json:"organization"` + Reverse *string `json:"reverse"` + ID string `json:"id"` + Server *struct { + Identifier string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + } `json:"server"` + Address string `json:"address"` +} + +// ScalewayGetIPS represents the response of a GET /ips/ +type ScalewayGetIPS struct { + IPS []ScalewayIPDefinition `json:"ips"` +} + +// ScalewayGetIP represents the response of a GET /ips/{id_ip} +type ScalewayGetIP struct { + IP ScalewayIPDefinition `json:"ip"` +} + +// ScalewaySecurityGroup represents a Scaleway security group +type ScalewaySecurityGroup struct { + // Identifier is a unique identifier for the security group + Identifier string `json:"id,omitempty"` + + // Name is the user-defined name of the security group + Name string `json:"name,omitempty"` +} + +// ScalewayNewSecurityGroup definition POST request /security_groups +type ScalewayNewSecurityGroup struct { + Organization string `json:"organization"` + Name string `json:"name"` + Description string `json:"description"` +} + +// ScalewayUpdateSecurityGroup definition PUT request /security_groups +type ScalewayUpdateSecurityGroup struct { + Organization string `json:"organization"` + Name string `json:"name"` + Description string `json:"description"` + OrganizationDefault bool `json:"organization_default"` +} + +// ScalewayServer represents a Scaleway server +type ScalewayServer struct { + // Arch is the architecture target of the server + Arch string `json:"arch,omitempty"` + + // Identifier is a unique identifier for the server + Identifier string `json:"id,omitempty"` + + // Name is the user-defined name of the server + Name string `json:"name,omitempty"` + + // CreationDate is the creation date of the server + CreationDate string `json:"creation_date,omitempty"` + + // ModificationDate is the date of the last modification of the server + ModificationDate string `json:"modification_date,omitempty"` + + // Image is the image used by the server + Image ScalewayImage `json:"image,omitempty"` + + // DynamicIPRequired is a flag that defines a server with a dynamic ip address attached + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + + // PublicIP is the public IP address bound to the server + PublicAddress ScalewayIPAddress `json:"public_ip,omitempty"` + + // State is the current status of the server + State string `json:"state,omitempty"` + + // StateDetail is the detailed status of the server + StateDetail string `json:"state_detail,omitempty"` + + // PrivateIP represents the private IPV4 attached to the server (changes on each boot) + PrivateIP string `json:"private_ip,omitempty"` + + // Bootscript is the unique identifier of the selected bootscript + Bootscript *ScalewayBootscript `json:"bootscript,omitempty"` + + // Hostname represents the ServerName in a format compatible with unix's hostname + Hostname string `json:"hostname,omitempty"` + + // Tags represents user-defined tags + Tags []string `json:"tags,omitempty"` + + // Volumes are the attached volumes + Volumes map[string]ScalewayVolume `json:"volumes,omitempty"` + + // SecurityGroup is the selected security group object + SecurityGroup ScalewaySecurityGroup `json:"security_group,omitempty"` + + // Organization is the owner of the server + Organization string `json:"organization,omitempty"` + + // CommercialType is the commercial type of the server (i.e: C1, C2[SML], VC1S) + CommercialType string `json:"commercial_type,omitempty"` + + // Location of the server + Location struct { + Platform string `json:"platform_id,omitempty"` + Chassis string `json:"chassis_id,omitempty"` + Cluster string `json:"cluster_id,omitempty"` + Hypervisor string `json:"hypervisor_id,omitempty"` + Blade string `json:"blade_id,omitempty"` + Node string `json:"node_id,omitempty"` + ZoneID string `json:"zone_id,omitempty"` + } `json:"location,omitempty"` + + IPV6 *ScalewayIPV6Definition `json:"ipv6,omitempty"` + + EnableIPV6 bool `json:"enable_ipv6,omitempty"` + + // This fields are not returned by the API, we generate it + DNSPublic string `json:"dns_public,omitempty"` + DNSPrivate string `json:"dns_private,omitempty"` +} + +// ScalewayIPV6Definition represents a Scaleway ipv6 +type ScalewayIPV6Definition struct { + Netmask string `json:"netmask"` + Gateway string `json:"gateway"` + Address string `json:"address"` +} + +// ScalewayServerPatchDefinition represents a Scaleway server with nullable fields (for PATCH) +type ScalewayServerPatchDefinition struct { + Arch *string `json:"arch,omitempty"` + Name *string `json:"name,omitempty"` + CreationDate *string `json:"creation_date,omitempty"` + ModificationDate *string `json:"modification_date,omitempty"` + Image *ScalewayImage `json:"image,omitempty"` + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + PublicAddress *ScalewayIPAddress `json:"public_ip,omitempty"` + State *string `json:"state,omitempty"` + StateDetail *string `json:"state_detail,omitempty"` + PrivateIP *string `json:"private_ip,omitempty"` + Bootscript *string `json:"bootscript,omitempty"` + Hostname *string `json:"hostname,omitempty"` + Volumes *map[string]ScalewayVolume `json:"volumes,omitempty"` + SecurityGroup *ScalewaySecurityGroup `json:"security_group,omitempty"` + Organization *string `json:"organization,omitempty"` + Tags *[]string `json:"tags,omitempty"` + IPV6 *ScalewayIPV6Definition `json:"ipv6,omitempty"` + EnableIPV6 *bool `json:"enable_ipv6,omitempty"` +} + +// ScalewayServerDefinition represents a Scaleway server with image definition +type ScalewayServerDefinition struct { + // Name is the user-defined name of the server + Name string `json:"name"` + + // Image is the image used by the server + Image *string `json:"image,omitempty"` + + // Volumes are the attached volumes + Volumes map[string]string `json:"volumes,omitempty"` + + // DynamicIPRequired is a flag that defines a server with a dynamic ip address attached + DynamicIPRequired *bool `json:"dynamic_ip_required,omitempty"` + + // BootType defines the type of boot + BootType string `json:"boot_type,omitempty"` + + // Bootscript is the bootscript used by the server + Bootscript *string `json:"bootscript"` + + // Tags are the metadata tags attached to the server + Tags []string `json:"tags,omitempty"` + + // Organization is the owner of the server + Organization string `json:"organization"` + + // CommercialType is the commercial type of the server (i.e: C1, C2[SML], VC1S) + CommercialType string `json:"commercial_type"` + + PublicIP string `json:"public_ip,omitempty"` + + EnableIPV6 bool `json:"enable_ipv6,omitempty"` + + SecurityGroup string `json:"security_group,omitempty"` +} + +// ScalewayOneServer represents the response of a GET /servers/UUID API call +type ScalewayOneServer struct { + Server ScalewayServer `json:"server,omitempty"` +} + +// ScalewayServers represents a group of Scaleway servers +type ScalewayServers struct { + // Servers holds scaleway servers of the response + Servers []ScalewayServer `json:"servers,omitempty"` +} + +// ScalewayServerAction represents an action to perform on a Scaleway server +type ScalewayServerAction struct { + // Action is the name of the action to trigger + Action string `json:"action,omitempty"` +} + +// ScalewaySnapshotDefinition represents a Scaleway snapshot definition +type ScalewaySnapshotDefinition struct { + VolumeIDentifier string `json:"volume_id"` + Name string `json:"name,omitempty"` + Organization string `json:"organization"` +} + +// ScalewayImageDefinition represents a Scaleway image definition +type ScalewayImageDefinition struct { + SnapshotIDentifier string `json:"root_volume"` + Name string `json:"name,omitempty"` + Organization string `json:"organization"` + Arch string `json:"arch"` + DefaultBootscript *string `json:"default_bootscript,omitempty"` +} + +// ScalewayRoleDefinition represents a Scaleway Token UserId Role +type ScalewayRoleDefinition struct { + Organization ScalewayOrganizationDefinition `json:"organization,omitempty"` + Role string `json:"role,omitempty"` +} + +// ScalewayTokenDefinition represents a Scaleway Token +type ScalewayTokenDefinition struct { + UserID string `json:"user_id"` + Description string `json:"description,omitempty"` + Roles ScalewayRoleDefinition `json:"roles"` + Expires string `json:"expires"` + InheritsUsersPerms bool `json:"inherits_user_perms"` + ID string `json:"id"` +} + +// ScalewayTokensDefinition represents a Scaleway Tokens +type ScalewayTokensDefinition struct { + Token ScalewayTokenDefinition `json:"token"` +} + +// ScalewayGetTokens represents a list of Scaleway Tokens +type ScalewayGetTokens struct { + Tokens []ScalewayTokenDefinition `json:"tokens"` +} + +// ScalewayContainerData represents a Scaleway container data (S3) +type ScalewayContainerData struct { + LastModified string `json:"last_modified"` + Name string `json:"name"` + Size string `json:"size"` +} + +// ScalewayGetContainerDatas represents a list of Scaleway containers data (S3) +type ScalewayGetContainerDatas struct { + Container []ScalewayContainerData `json:"container"` +} + +// ScalewayContainer represents a Scaleway container (S3) +type ScalewayContainer struct { + ScalewayOrganizationDefinition `json:"organization"` + Name string `json:"name"` + Size string `json:"size"` +} + +// ScalewayGetContainers represents a list of Scaleway containers (S3) +type ScalewayGetContainers struct { + Containers []ScalewayContainer `json:"containers"` +} + +// ScalewayConnectResponse represents the answer from POST /tokens +type ScalewayConnectResponse struct { + Token ScalewayTokenDefinition `json:"token"` +} + +// ScalewayConnect represents the data to connect +type ScalewayConnect struct { + Email string `json:"email"` + Password string `json:"password"` + Description string `json:"description"` + Expires bool `json:"expires"` +} + +// ScalewayConnectInterface is the interface implemented by ScalewayConnect, +// ScalewayConnectByOTP and ScalewayConnectByBackupCode +type ScalewayConnectInterface interface { + GetPassword() string +} + +func (s *ScalewayConnect) GetPassword() string { + return s.Password +} + +type ScalewayConnectByOTP struct { + ScalewayConnect + TwoFAToken string `json:"2FA_token"` +} + +type ScalewayConnectByBackupCode struct { + ScalewayConnect + TwoFABackupCode string `json:"2FA_backup_code"` +} + +// ScalewayOrganizationDefinition represents a Scaleway Organization +type ScalewayOrganizationDefinition struct { + ID string `json:"id"` + Name string `json:"name"` + Users []ScalewayUserDefinition `json:"users"` +} + +// ScalewayOrganizationsDefinition represents a Scaleway Organizations +type ScalewayOrganizationsDefinition struct { + Organizations []ScalewayOrganizationDefinition `json:"organizations"` +} + +// ScalewayUserDefinition represents a Scaleway User +type ScalewayUserDefinition struct { + Email string `json:"email"` + Firstname string `json:"firstname"` + Fullname string `json:"fullname"` + ID string `json:"id"` + Lastname string `json:"lastname"` + Organizations []ScalewayOrganizationDefinition `json:"organizations"` + Roles []ScalewayRoleDefinition `json:"roles"` + SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"` +} + +// ScalewayUsersDefinition represents the response of a GET /user +type ScalewayUsersDefinition struct { + User ScalewayUserDefinition `json:"user"` +} + +// ScalewayKeyDefinition represents a key +type ScalewayKeyDefinition struct { + Key string `json:"key"` + Fingerprint string `json:"fingerprint,omitempty"` +} + +// ScalewayUserPatchSSHKeyDefinition represents a User Patch +type ScalewayUserPatchSSHKeyDefinition struct { + SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"` +} + +// ScalewayDashboardResp represents a dashboard received from the API +type ScalewayDashboardResp struct { + Dashboard ScalewayDashboard +} + +// ScalewayDashboard represents a dashboard +type ScalewayDashboard struct { + VolumesCount int `json:"volumes_count"` + RunningServersCount int `json:"running_servers_count"` + ImagesCount int `json:"images_count"` + SnapshotsCount int `json:"snapshots_count"` + ServersCount int `json:"servers_count"` + IPsCount int `json:"ips_count"` +} + +// ScalewayPermissions represents the response of GET /permissions +type ScalewayPermissions map[string]ScalewayPermCategory + +// ScalewayPermCategory represents ScalewayPermissions's fields +type ScalewayPermCategory map[string][]string + +// ScalewayPermissionDefinition represents the permissions +type ScalewayPermissionDefinition struct { + Permissions ScalewayPermissions `json:"permissions"` +} + +// ScalewayUserdatas represents the response of a GET /user_data +type ScalewayUserdatas struct { + UserData []string `json:"user_data"` +} + +// ScalewayQuota represents a map of quota (name, value) +type ScalewayQuota map[string]int + +// ScalewayGetQuotas represents the response of GET /organizations/{orga_id}/quotas +type ScalewayGetQuotas struct { + Quotas ScalewayQuota `json:"quotas"` +} + +// ScalewayUserdata represents []byte +type ScalewayUserdata []byte + +// FuncMap used for json inspection +var FuncMap = template.FuncMap{ + "json": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, +} + +// MarketLocalImageDefinition represents localImage of marketplace version +type MarketLocalImageDefinition struct { + Arch string `json:"arch"` + ID string `json:"id"` + Zone string `json:"zone"` +} + +// MarketLocalImages represents an array of local images +type MarketLocalImages struct { + LocalImages []MarketLocalImageDefinition `json:"local_images"` +} + +// MarketLocalImage represents local image +type MarketLocalImage struct { + LocalImages MarketLocalImageDefinition `json:"local_image"` +} + +// MarketVersionDefinition represents version of marketplace image +type MarketVersionDefinition struct { + CreationDate string `json:"creation_date"` + ID string `json:"id"` + Image struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"image"` + ModificationDate string `json:"modification_date"` + Name string `json:"name"` + MarketLocalImages +} + +// MarketVersions represents an array of marketplace image versions +type MarketVersions struct { + Versions []MarketVersionDefinition `json:"versions"` +} + +// MarketVersion represents version of marketplace image +type MarketVersion struct { + Version MarketVersionDefinition `json:"version"` +} + +// MarketImage represents MarketPlace image +type MarketImage struct { + Categories []string `json:"categories"` + CreationDate string `json:"creation_date"` + CurrentPublicVersion string `json:"current_public_version"` + Description string `json:"description"` + ID string `json:"id"` + Logo string `json:"logo"` + ModificationDate string `json:"modification_date"` + Name string `json:"name"` + Organization struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"organization"` + Public bool `json:"-"` + MarketVersions +} + +// MarketImages represents MarketPlace images +type MarketImages struct { + Images []MarketImage `json:"images"` +} + +// ScalewaySortServers represents a wrapper to sort by CreationDate the servers +type ScalewaySortServers []ScalewayServer + +func (s ScalewaySortServers) Len() int { + return len(s) +} + +func (s ScalewaySortServers) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s ScalewaySortServers) Less(i, j int) bool { + date1, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", s[i].CreationDate) + date2, _ := time.Parse("2006-01-02T15:04:05.000000+00:00", s[j].CreationDate) + return date2.Before(date1) +} + +func (s *ScalewayUserdata) String() string { + return string(*s) +} diff --git a/src/cmd/linuxkit/vendor/golang.org/x/sync/LICENSE b/src/cmd/linuxkit/vendor/golang.org/x/sync/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/src/cmd/linuxkit/vendor/golang.org/x/sync/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/cmd/linuxkit/vendor/golang.org/x/sync/PATENTS b/src/cmd/linuxkit/vendor/golang.org/x/sync/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/src/cmd/linuxkit/vendor/golang.org/x/sync/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/src/cmd/linuxkit/vendor/golang.org/x/sync/README.md b/src/cmd/linuxkit/vendor/golang.org/x/sync/README.md new file mode 100644 index 000000000..1f8436cc9 --- /dev/null +++ b/src/cmd/linuxkit/vendor/golang.org/x/sync/README.md @@ -0,0 +1,18 @@ +# Go Sync + +This repository provides Go concurrency primitives in addition to the +ones provided by the language and "sync" and "sync/atomic" packages. + +## Download/Install + +The easiest way to install is to run `go get -u golang.org/x/sync`. You can +also manually git clone the repository to `$GOPATH/src/golang.org/x/sync`. + +## Report Issues / Send Patches + +This repository uses Gerrit for code changes. To learn how to submit changes to +this repository, see https://golang.org/doc/contribute.html. + +The main issue tracker for the sync repository is located at +https://github.com/golang/go/issues. Prefix your issue with "x/sync:" in the +subject line, so it is easy to find. diff --git a/src/cmd/linuxkit/vendor/golang.org/x/sync/errgroup/errgroup.go b/src/cmd/linuxkit/vendor/golang.org/x/sync/errgroup/errgroup.go new file mode 100644 index 000000000..533438d91 --- /dev/null +++ b/src/cmd/linuxkit/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -0,0 +1,67 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package errgroup provides synchronization, error propagation, and Context +// cancelation for groups of goroutines working on subtasks of a common task. +package errgroup + +import ( + "sync" + + "golang.org/x/net/context" +) + +// A Group is a collection of goroutines working on subtasks that are part of +// the same overall task. +// +// A zero Group is valid and does not cancel on error. +type Group struct { + cancel func() + + wg sync.WaitGroup + + errOnce sync.Once + err error +} + +// WithContext returns a new Group and an associated Context derived from ctx. +// +// The derived Context is canceled the first time a function passed to Go +// returns a non-nil error or the first time Wait returns, whichever occurs +// first. +func WithContext(ctx context.Context) (*Group, context.Context) { + ctx, cancel := context.WithCancel(ctx) + return &Group{cancel: cancel}, ctx +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the first non-nil error (if any) from them. +func (g *Group) Wait() error { + g.wg.Wait() + if g.cancel != nil { + g.cancel() + } + return g.err +} + +// Go calls the given function in a new goroutine. +// +// The first call to return a non-nil error cancels the group; its error will be +// returned by Wait. +func (g *Group) Go(f func() error) { + g.wg.Add(1) + + go func() { + defer g.wg.Done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() +}