diff --git a/.travis.yml b/.travis.yml index a7eb399f6a..201cf4a784 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,6 @@ env: matrix: include: - - go: 1.3 - - go: 1.4 - go: 1.5 TEST_WITH_REAL_API=1 - go: tip allow_failures: diff --git a/Makefile b/Makefile index a4580834d8..6a3b5b5e60 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,6 @@ GOINSTALL ?= $(GOCMD) install GOTEST ?= $(GOCMD) test GOFMT ?= gofmt -w GOCOVER ?= $(GOTEST) -covermode=count -v -GOVERSIONMAJOR = $(shell go version | grep -o '[1-9].[0-9]' | cut -d '.' -f1) -GOVERSIONMINOR = $(shell go version | grep -o '[1-9].[0-9]' | cut -d '.' -f2) -VERSION_GE_1_5 = $(shell [ $(GOVERSIONMAJOR) -gt 1 -o $(GOVERSIONMINOR) -ge 5 ] && echo true) FPM_VERSION ?= $(shell ./dist/scw-Darwin-i386 --version | sed 's/.*v\([0-9.]*\),.*/\1/g') FPM_DOCKER ?= \ @@ -31,14 +28,17 @@ SRC = cmd/scw PACKAGES = pkg/api pkg/commands pkg/utils pkg/cli pkg/sshcommand pkg/config pkg/scwversion pkg/pricing REV = $(shell git rev-parse HEAD || echo "nogit") TAG = $(shell git describe --tags --always || echo "nogit") -BUILDER = scaleway-cli-builder -ALL_GO_FILES = $(shell find . -type f -name "*.go") -ifeq ($(VERSION_GE_1_5),true) LDFLAGS = "-X github.com/scaleway/scaleway-cli/pkg/scwversion.GITCOMMIT=$(REV) \ -X github.com/scaleway/scaleway-cli/pkg/scwversion.VERSION=$(TAG)" -else -LDFLAGS = "-X github.com/scaleway/scaleway-cli/pkg/scwversion.GITCOMMIT $(REV) \ - -X github.com/scaleway/scaleway-cli/pkg/scwversion.VERSION $(TAG)" +BUILDER = scaleway-cli-builder +ALL_GO_FILES = $(shell find . -type f -name "*.go") + +# Check go version +GOVERSIONMAJOR = $(shell go version | grep -o '[1-9].[0-9]' | cut -d '.' -f1) +GOVERSIONMINOR = $(shell go version | grep -o '[1-9].[0-9]' | cut -d '.' -f2) +VERSION_GE_1_5 = $(shell [ $(GOVERSIONMAJOR) -gt 1 -o $(GOVERSIONMINOR) -ge 5 ] && echo true) +ifneq ($(VERSION_GE_1_5),true) + $(error Bad go version, please install a version greater than or equal to 1.5) endif BUILD_LIST = $(foreach int, $(SRC), $(int)_build) @@ -73,7 +73,7 @@ $(CLEAN_LIST): %_clean: $(GOCLEAN) ./$* $(INSTALL_LIST): %_install: $(GOINSTALL) ./$* -$(IREF_LIST): %_iref: pkg/scwversion/version.go +$(IREF_LIST): %_iref: $(GOTEST) -ldflags $(LDFLAGS) -i ./$* $(TEST_LIST): %_test: $(GOTEST) -ldflags $(LDFLAGS) -v ./$* diff --git a/README.md b/README.md index 92849b943e..0c37f7956f 100644 --- a/README.md +++ b/README.md @@ -1132,6 +1132,8 @@ $ scw inspect myserver | jq '.[0].public_ip.address' ### master (unreleased) +* Remove go1.[34] support +* Update gotty-client to 1.2.0 * Improve _cs format ([#223](https://github.com/scaleway/scaleway-cli/issues/223)) * Use `gotty-client` instead of `termjs-cli` * Fix: bad detection of server already started when starting a server ([#224](https://github.com/scaleway/scaleway-cli/pull/224)) - [@arianvp](https://github.com/arianvp) diff --git a/vendor/github.com/moul/gotty-client/Makefile b/vendor/github.com/moul/gotty-client/Makefile index cded47b7e5..d695bb9beb 100644 --- a/vendor/github.com/moul/gotty-client/Makefile +++ b/vendor/github.com/moul/gotty-client/Makefile @@ -45,7 +45,7 @@ run-docker: build-docker $(notdir $(COMMANDS)): $(SOURCES) gofmt -w $(PACKAGES) ./cmd/$@ go test -i $(PACKAGES) ./cmd/$@ - go build -o $@ ./cmd/$@ + go build -ldflags "-X main.VERSION=$(VERSION)" -o $@ ./cmd/$@ ./$@ --version diff --git a/vendor/github.com/moul/gotty-client/README.md b/vendor/github.com/moul/gotty-client/README.md index 7a1723cb3d..8cfa6cc522 100644 --- a/vendor/github.com/moul/gotty-client/README.md +++ b/vendor/github.com/moul/gotty-client/README.md @@ -103,11 +103,22 @@ $ brew install https://raw.githubusercontent.com/moul/ssh2docker/master/contrib/ ### master (unreleased) +* No entry + +[full commits list](https://github.com/moul/gotty-client/compare/v1.2.0...master) + +### [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...master) +[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) diff --git a/vendor/github.com/moul/gotty-client/gotty-client.go b/vendor/github.com/moul/gotty-client/gotty-client.go index 2d135a5ebd..a3f7c41ab7 100644 --- a/vendor/github.com/moul/gotty-client/gotty-client.go +++ b/vendor/github.com/moul/gotty-client/gotty-client.go @@ -1,10 +1,10 @@ package gottyclient import ( - "bufio" "encoding/base64" "encoding/json" "fmt" + "io" "io/ioutil" "net/http" "net/url" @@ -15,10 +15,10 @@ import ( "sync" "syscall" "time" - "unicode/utf8" "unsafe" "github.com/scaleway/scaleway-cli/vendor/github.com/moul/gotty-client/vendor/github.com/Sirupsen/logrus" + "github.com/scaleway/scaleway-cli/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect" "github.com/scaleway/scaleway-cli/vendor/github.com/moul/gotty-client/vendor/github.com/gorilla/websocket" "github.com/scaleway/scaleway-cli/vendor/github.com/moul/gotty-client/vendor/golang.org/x/crypto/ssh/terminal" ) @@ -80,6 +80,7 @@ type Client struct { URL string Connected bool WriteMutex *sync.Mutex + Output io.Writer } type querySingleType struct { @@ -157,7 +158,7 @@ func (c *Client) Connect() error { if err != nil { return err } - var querySingle querySingleType = querySingleType{ + querySingle := querySingleType{ Arguments: "?" + query.Encode(), AuthToken: authToken, } @@ -200,11 +201,19 @@ func (c *Client) Loop() error { } } + var wg sync.WaitGroup + quit := make(chan struct{}) done := make(chan bool) - go c.readLoop(done) - go c.writeLoop(done) - go c.termsizeLoop(done) + + wg.Add(1) + go c.termsizeLoop(quit, &wg) + wg.Add(1) + go c.readLoop(done, quit, &wg) + wg.Add(1) + go c.writeLoop(done, quit, &wg) <-done + close(quit) + wg.Wait() return nil } @@ -216,9 +225,11 @@ type winsize struct { y uint16 } -func (c *Client) termsizeLoop(done chan bool) { +func (c *Client) termsizeLoop(quit chan struct{}, wg *sync.WaitGroup) { + defer wg.Done() ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGWINCH) + defer signal.Reset(syscall.SIGWINCH) ws := winsize{} for { @@ -235,75 +246,123 @@ func (c *Client) termsizeLoop(done chan bool) { if err != nil { logrus.Warnf("ws.WriteMessage failed: %v", err) } - - <-ch + select { + case <-quit: + return + case <-ch: + } } } -func (c *Client) writeLoop(done chan bool) { +type exposeFd interface { + Fd() uintptr +} + +func (c *Client) writeLoop(done chan bool, quit chan struct{}, wg *sync.WaitGroup) { + defer wg.Done() + + buff := make([]byte, 128) oldState, err := terminal.MakeRaw(0) if err == nil { defer terminal.Restore(0, oldState) } - reader := bufio.NewReader(os.Stdin) + rdfs := &goselect.FDSet{} + reader := io.Reader(os.Stdin) for { - x, size, err := reader.ReadRune() - if size <= 0 || err != nil { + rdfs.Zero() + rdfs.Set(reader.(exposeFd).Fd()) + err := goselect.Select(1, rdfs, nil, nil, 50*time.Millisecond) + if err != nil { done <- true return } - - p := make([]byte, size) - utf8.EncodeRune(p, x) - - err = c.write(append([]byte("0"), p...)) - if err != nil { - done <- true + if rdfs.IsSet(reader.(exposeFd).Fd()) { + size, err := reader.Read(buff) + if size <= 0 || err != nil { + done <- true + return + } + data := buff[:size] + err = c.write(append([]byte("0"), data...)) + if err != nil { + done <- true + return + } + } + select { + case <-quit: return + default: + break } } } -func (c *Client) readLoop(done chan bool) { +func (c *Client) readLoop(done chan bool, quit chan struct{}, wg *sync.WaitGroup) { + defer wg.Done() + + type MessageNonBlocking struct { + Data []byte + Err error + } + msgChan := make(chan MessageNonBlocking) + for { - _, data, err := c.Conn.ReadMessage() - if err != nil { - done <- true - logrus.Warnf("c.Conn.ReadMessage: %v", err) - return - } - if len(data) == 0 { - done <- true - logrus.Warnf("An error has occured") + go func() { + _, data, err := c.Conn.ReadMessage() + msgChan <- MessageNonBlocking{Data: data, Err: err} + }() + + select { + case <-quit: return - } - switch data[0] { - case '0': // data - buf, err := base64.StdEncoding.DecodeString(string(data[1:])) - if err != nil { - logrus.Warnf("Invalid base64 content: %q", data[1:]) + case msg := <-msgChan: + if msg.Err != nil { + done <- true + if _, ok := msg.Err.(*websocket.CloseError); !ok { + logrus.Warnf("c.Conn.ReadMessage: %v", msg.Err) + } + return + } + if len(msg.Data) == 0 { + done <- true + logrus.Warnf("An error has occured") + return + } + switch msg.Data[0] { + case '0': // data + buf, err := base64.StdEncoding.DecodeString(string(msg.Data[1:])) + if err != nil { + logrus.Warnf("Invalid base64 content: %q", msg.Data[1:]) + } + fmt.Fprintf(c.Output, string(buf)) + case '1': // pong + case '2': // new title + newTitle := string(msg.Data[1:]) + fmt.Fprintf(c.Output, "\033]0;%s\007", newTitle) + case '3': // json prefs + logrus.Debugf("Unhandled protocol message: json pref: %s", string(msg.Data[1:])) + case '4': // autoreconnect + logrus.Debugf("Unhandled protocol message: autoreconnect: %s", string(msg.Data)) + default: + logrus.Warnf("Unhandled protocol message: %s", string(msg.Data)) } - fmt.Print(string(buf)) - case '1': // pong - case '2': // new title - newTitle := string(data[1:]) - fmt.Printf("\033]0;%s\007", newTitle) - case '3': // json prefs - logrus.Debugf("Unhandled protocol message: json pref: %s", string(data[1:])) - case '4': // autoreconnect - logrus.Debugf("Unhandled protocol message: autoreconnect: %s", string(data)) - default: - logrus.Warnf("Unhandled protocol message: %s", string(data)) } } } +// SetOutput changes the output stream +func (c *Client) SetOutput(w io.Writer) { + c.Output = w +} + // NewClient returns a GoTTY client object func NewClient(httpURL string) (*Client, error) { return &Client{ Dialer: &websocket.Dialer{}, URL: httpURL, WriteMutex: &sync.Mutex{}, + Output: os.Stdout, }, nil } diff --git a/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/Dockerfile b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/Dockerfile new file mode 100644 index 0000000000..d03b5a9d9c --- /dev/null +++ b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/Dockerfile @@ -0,0 +1,5 @@ +FROM google/golang:stable +MAINTAINER Guillaume J. Charmes +CMD /tmp/a.out +ADD . /src +RUN cd /src && go build -o /tmp/a.out diff --git a/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/LICENSE b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/LICENSE new file mode 100644 index 0000000000..95c600af14 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/README.md b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/README.md new file mode 100644 index 0000000000..69300a29f0 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/README.md @@ -0,0 +1,30 @@ +# go-select + +select(2) implementation in Go + +## Supported platforms + +| | 386 | amd64 | arm | +|---------------|-----|-------|-----| +| **linux** | 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** | no | no | n/a | +| **android** | n/a | n/a | no | + +*n/a: platform not supported by Go + +Go on `windows`, `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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset.go new file mode 100644 index 0000000000..3ba3392f85 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset.go @@ -0,0 +1,31 @@ +// +build !freebsd,!windows,!plan9 + +package goselect + +import "syscall" + +// 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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_32.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_32.go new file mode 100644 index 0000000000..8b90d21a95 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_32.go @@ -0,0 +1,10 @@ +// +build darwin openbsd netbsd 386 arm + +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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_64.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_64.go new file mode 100644 index 0000000000..4211c66748 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_64.go @@ -0,0 +1,10 @@ +// +build amd64,!darwin,!netbsd,!openbsd + +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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_doc.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_doc.go new file mode 100644 index 0000000000..d9f15a1cec --- /dev/null +++ b/vendor/github.com/moul/gotty-client/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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_freebsd.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_freebsd.go new file mode 100644 index 0000000000..3ef9602e45 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_freebsd.go @@ -0,0 +1,31 @@ +// +build freebsd + +package goselect + +import "syscall" + +// 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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_unsupported.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_unsupported.go new file mode 100644 index 0000000000..3a3e081200 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/fdset_unsupported.go @@ -0,0 +1,18 @@ +// +build windows plan9 + +package goselect + +// 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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select.go new file mode 100644 index 0000000000..7f2875e736 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_linux.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_linux.go new file mode 100644 index 0000000000..acd569e9dd --- /dev/null +++ b/vendor/github.com/moul/gotty-client/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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_other.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_other.go new file mode 100644 index 0000000000..6c82081470 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/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/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_test.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_test.go new file mode 100644 index 0000000000..2287504514 --- /dev/null +++ b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_test.go @@ -0,0 +1,60 @@ +package goselect + +import ( + "fmt" + "io" + "os" + "testing" + "time" +) + +type fder interface { + Fd() uintptr +} + +func TestReadWriteSync(t *testing.T) { + const count = 500 + rrs := []io.Reader{} + wws := []io.Writer{} + rFDSet := &FDSet{} + for i := 0; i < count; i++ { + rr, ww, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + rrs = append(rrs, rr) + wws = append(wws, ww) + } + + go func() { + time.Sleep(time.Second) + for i := 0; i < count; i++ { + fmt.Fprintf(wws[i], "hello %d", i) + time.Sleep(time.Millisecond) + } + }() + + buf := make([]byte, 1024) + for i := 0; i < count; i++ { + rFDSet.Zero() + for i := 0; i < count; i++ { + rFDSet.Set(rrs[i].(fder).Fd()) + } + + if err := RetrySelect(1024, rFDSet, nil, nil, -1, 10, 10*time.Millisecond); err != nil { + t.Fatalf("select call failed: %s", err) + } + for j := 0; j < count; j++ { + if rFDSet.IsSet(rrs[j].(fder).Fd()) { + // println(i, j) + if i != j { + t.Fatalf("unexpected fd ready: %d,expected: %d", j, i) + } + _, err := rrs[j].Read(buf) + if err != nil { + t.Fatalf("read call failed: %s", err) + } + } + } + } +} diff --git a/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_unsupported.go b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_unsupported.go new file mode 100644 index 0000000000..9ac8584f3c --- /dev/null +++ b/vendor/github.com/moul/gotty-client/vendor/github.com/creack/goselect/select_unsupported.go @@ -0,0 +1,16 @@ +// +build windows 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 +}