diff --git a/api/client/fip.go b/api/client/fip.go index 4198848ba..293f4095f 100644 --- a/api/client/fip.go +++ b/api/client/fip.go @@ -160,3 +160,39 @@ func fipUsage() string { help += fmt.Sprintf("\nRun 'hyper fip COMMAND --help' for more information on a command.") return help } + +// Allocate and associate a fip +func (cli *DockerCli) associateNewFip(contID string) (string, error) { + fips, err := cli.client.FipAllocate("1") + if err != nil { + return "", err + } + + for _, ip := range fips { + err = cli.client.FipAssociate(ip, contID) + if err != nil { + go func() { + cli.client.FipRelease(ip) + }() + return "", err + } + return ip, nil + } + + return "", fmt.Errorf("Server failed to create new fip") +} + +// Release a fip +func (cli *DockerCli) releaseFip(ip string) error { + return cli.client.FipRelease(ip) +} + +// Disassociate and release a fip +func (cli *DockerCli) releaseContainerFip(contID string) error { + ip, err := cli.client.FipDisassociate(contID) + if err != nil { + return err + } + + return cli.client.FipRelease(ip) +} diff --git a/api/client/run.go b/api/client/run.go index 38a42889e..b21f0c4dc 100644 --- a/api/client/run.go +++ b/api/client/run.go @@ -20,6 +20,7 @@ import ( "github.com/hyperhq/hypercli/pkg/signal" "github.com/hyperhq/hypercli/pkg/stringid" runconfigopts "github.com/hyperhq/hypercli/runconfig/opts" + "github.com/satori/go.uuid" ) type InitVolume struct { @@ -121,13 +122,13 @@ func (cli *DockerCli) initSpecialVolumes(config *container.Config, hostConfig *c var ( initConfig *container.Config initHostConfig *container.HostConfig - execJobs []string - execID string + errCh chan error + execCount uint32 + fip string ) initConfig = &container.Config{ User: config.User, - Env: config.Env, Image: INIT_VOLUME_IMAGE, StopSignal: config.StopSignal, } @@ -143,6 +144,8 @@ func (cli *DockerCli) initSpecialVolumes(config *container.Config, hostConfig *c for _, vol := range initvols { initHostConfig.Binds = append(initHostConfig.Binds, vol.Name+":"+INIT_VOLUME_PATH+vol.Destination) } + passwd := uuid.NewV1() + initConfig.Env = append(config.Env, "ROOTPASSWORD="+passwd.String(), "LOCALROOT="+INIT_VOLUME_PATH) createResponse, err := cli.createContainer(initConfig, initHostConfig, networkingConfig, hostConfig.ContainerIDFile, "") if err != nil { @@ -154,12 +157,18 @@ func (cli *DockerCli) initSpecialVolumes(config *container.Config, hostConfig *c fmt.Fprintf(cli.err, "clean up init container failed: %s\n", rmErr.Error()) } } + if fip != "" { + if rmErr := cli.releaseFip(fip); rmErr != nil { + fmt.Fprintf(cli.err, "failed to clean up container fip %s: %s\n", fip, rmErr.Error()) + } + } }() if err = cli.client.ContainerStart(createResponse.ID); err != nil { return err } + errCh = make(chan error, len(initvols)) for _, vol := range initvols { var cmd []string volType := checkSourceType(vol.Source) @@ -170,36 +179,62 @@ func (cli *DockerCli) initSpecialVolumes(config *container.Config, hostConfig *c parts := strings.Split(vol.Source, "/") cmd = append(cmd, "wget", "--no-check-certificate", "--tries=5", "--mirror", "--no-host-directories", "--cut-dirs="+strconv.Itoa(len(parts)), vol.Source, "--directory-prefix="+INIT_VOLUME_PATH+vol.Destination) case "local": - // TODO + execCount++ + if fip == "" { + fip, err = cli.associateNewFip(createResponse.ID) + if err != nil { + return err + } + } + go func(vol *InitVolume) { + err := cli.uploadLocalResource(vol.Source, INIT_VOLUME_PATH+vol.Destination, fip, "root", passwd.String()) + if err != nil { + err = fmt.Errorf("Failed to upload %s: %s", vol.Source, err.Error()) + } + errCh <- err + }(vol) default: continue } if len(cmd) == 0 { continue } - if execID, err = cli.ExecCmd(initConfig.User, createResponse.ID, cmd); err != nil { - return err - } else { - execJobs = append(execJobs, execID) - } + + execCount++ + go func() { + execID, err := cli.ExecCmd(initConfig.User, createResponse.ID, cmd) + if err != nil { + errCh <- err + } else { + errCh <- cli.WaitExec(execID) + } + }() } // wait results - for _, execID = range execJobs { - if err = cli.WaitExec(execID); err != nil { + for ; execCount > 0; execCount-- { + err = <-errCh + if err != nil { return err } } - // Need to sync before tearing down container because data might be still cached - if len(execJobs) > 0 { - syncCmd := []string{"sync"} - if execID, err = cli.ExecCmd(initConfig.User, createResponse.ID, syncCmd); err != nil { - return err - } - if err = cli.WaitExec(execID); err != nil { + // release fip + if fip != "" { + if err = cli.releaseContainerFip(createResponse.ID); err != nil { return err } + fip = "" + } + + // Need to sync before tearing down container because data might be still cached + syncCmd := []string{"sync"} + execID, err := cli.ExecCmd(initConfig.User, createResponse.ID, syncCmd) + if err != nil { + return err + } + if err = cli.WaitExec(execID); err != nil { + return err } _, err = cli.removeContainer(createResponse.ID, false, false, true) diff --git a/api/client/utils.go b/api/client/utils.go index 7b5e4e538..0e5b86d45 100644 --- a/api/client/utils.go +++ b/api/client/utils.go @@ -14,12 +14,13 @@ import ( "time" "github.com/Sirupsen/logrus" - "github.com/hyperhq/hypercli/pkg/signal" - "github.com/hyperhq/hypercli/pkg/term" - "github.com/hyperhq/hypercli/registry" "github.com/docker/engine-api/client" "github.com/docker/engine-api/types" registrytypes "github.com/docker/engine-api/types/registry" + "github.com/dutchcoders/goftp" + "github.com/hyperhq/hypercli/pkg/signal" + "github.com/hyperhq/hypercli/pkg/term" + "github.com/hyperhq/hypercli/registry" ) func (cli *DockerCli) electAuthServer() string { @@ -216,3 +217,32 @@ func (cli *DockerCli) resolveAuthConfig(authConfigs map[string]types.AuthConfig, // When all else fails, return an empty auth config return types.AuthConfig{} } + +// uploadLocalResource upload a local file/directory to a container +func (cli *DockerCli) uploadLocalResource(source, dest, serverIP, user, passwd string) error { + if !strings.Contains(serverIP, ":") { + serverIP += ":21" + } + ftp, err := goftp.Connect(serverIP) + if err != nil { + return err + } + + defer func() { + ftp.Close() + }() + + if err = ftp.Login(user, passwd); err != nil { + return err + } + + if err = ftp.Cwd(dest); err != nil { + return err + } + + if err = ftp.Upload(source); err != nil { + return err + } + + return nil +} diff --git a/vendor/src/github.com/dutchcoders/goftp/LICENSE b/vendor/src/github.com/dutchcoders/goftp/LICENSE new file mode 100644 index 000000000..824455671 --- /dev/null +++ b/vendor/src/github.com/dutchcoders/goftp/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 + +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/src/github.com/dutchcoders/goftp/README.md b/vendor/src/github.com/dutchcoders/goftp/README.md new file mode 100644 index 000000000..fbf249b2c --- /dev/null +++ b/vendor/src/github.com/dutchcoders/goftp/README.md @@ -0,0 +1,109 @@ +goftp +===== + +Golang FTP library with Walk support. + +## Features + +* AUTH TLS support +* Walk + +## Sample + package main + + import ( + "crypto/sha256" + "crypto/tls" + "fmt" + "io" + "os" + + "github.com/dutchcoders/goftp" + ) + + func main() { + var err error + var ftp *goftp.FTP + + // For debug messages: goftp.ConnectDbg("ftp.server.com:21") + if ftp, err = goftp.Connect("ftp.server.com:21"); err != nil { + panic(err) + } + + defer ftp.Close() + + config := tls.Config{ + InsecureSkipVerify: true, + ClientAuth: tls.RequestClientCert, + } + + if err = ftp.AuthTLS(config); err != nil { + panic(err) + } + + if err = ftp.Login("username", "password"); err != nil { + panic(err) + } + + if err = ftp.Cwd("/"); err != nil { + panic(err) + } + + var curpath string + if curpath, err = ftp.Pwd(); err != nil { + panic(err) + } + + fmt.Printf("Current path: %s", curpath) + + var files []string + if files, err = ftp.List(""); err != nil { + panic(err) + } + + fmt.Println(files) + + var file *os.File + if file, err = os.Open("/tmp/test.txt"); err != nil { + panic(err) + } + + if err := ftp.Stor("/test.txt", file); err != nil { + panic(err) + } + + err = ftp.Walk("/", func(path string, info os.FileMode, err error) error { + _, err = ftp.Retr(path, func(r io.Reader) error { + var hasher = sha256.New() + if _, err = io.Copy(hasher, r); err != nil { + return err + } + + hash := fmt.Sprintf("%s %x", path, sha256.Sum256(nil)) + fmt.Println(hash) + + return err + }) + + return nil + }) + } + +## Contributions + +Contributions are welcome. + +* Sourav Datta: for his work on the anonymous user login and multiline return status. +* Vincenzo La Spesa: for his work on resolving login issues with specific ftp servers + + +## Creators + +**Remco Verhoef** +- +- + +## Copyright and license + +Code and documentation copyright 2011-2014 Remco Verhoef. +Code released under [the MIT license](LICENSE). diff --git a/vendor/src/github.com/dutchcoders/goftp/doc.go b/vendor/src/github.com/dutchcoders/goftp/doc.go new file mode 100644 index 000000000..b360a3834 --- /dev/null +++ b/vendor/src/github.com/dutchcoders/goftp/doc.go @@ -0,0 +1,27 @@ +/* +The MIT License (MIT) + +Copyright (c) 2014 DutchCoders [https://github.com/dutchcoders/] + +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. +*/ + +// ftp client library with Walk and TLS support + +package goftp diff --git a/vendor/src/github.com/dutchcoders/goftp/ftp.go b/vendor/src/github.com/dutchcoders/goftp/ftp.go new file mode 100644 index 000000000..321ef6e4e --- /dev/null +++ b/vendor/src/github.com/dutchcoders/goftp/ftp.go @@ -0,0 +1,617 @@ +package goftp + +import ( + "bufio" + "crypto/tls" + "errors" + "fmt" + "io" + "log" + "net" + "os" + "regexp" + "strconv" + "strings" +) + +var REGEX_PWD_PATH *regexp.Regexp = regexp.MustCompile(`\"(.*)\"`) + +type FTP struct { + conn net.Conn + + addr string + + debug bool + tlsconfig *tls.Config + + reader *bufio.Reader + writer *bufio.Writer +} + +func (ftp *FTP) Close() { + ftp.conn.Close() +} + +type WalkFunc func(path string, info os.FileMode, err error) error +type RetrFunc func(r io.Reader) error + +func parseLine(line string) (perm string, t string, filename string) { + for _, v := range strings.Split(line, ";") { + v2 := strings.Split(v, "=") + + switch v2[0] { + case "perm": + perm = v2[1] + case "type": + t = v2[1] + default: + filename = v[1 : len(v)-2] + } + } + + return +} + +// walks recursively through path and call walkfunc for each file +func (ftp *FTP) Walk(path string, walkFn WalkFunc) (err error) { + /* + if err = walkFn(path, os.ModeDir, nil); err != nil { + if err == filepath.SkipDir { + return nil + } + } + */ + if ftp.debug { + log.Printf("Walking: '%s'\n", path) + } + + var lines []string + + if lines, err = ftp.List(path); err != nil { + return + } + + for _, line := range lines { + _, t, subpath := parseLine(line) + + switch t { + case "dir": + if subpath == "." { + } else if subpath == ".." { + } else { + if err = ftp.Walk(path+subpath+"/", walkFn); err != nil { + return + } + } + case "file": + if err = walkFn(path+subpath, os.FileMode(0), nil); err != nil { + return + } + } + } + + return +} + +// send quit to the server and close the connection +func (ftp *FTP) Quit() (err error) { + if _, err := ftp.cmd("221", "QUIT"); err != nil { + return err + } + + ftp.conn.Close() + ftp.conn = nil + + return nil +} + +// will send a NOOP (no operation) to the server +func (ftp *FTP) Noop() (err error) { + _, err = ftp.cmd("200", "NOOP") + return +} + +// Send raw commands, return response as string and response code as int +func (ftp *FTP) RawCmd(command string, args ...interface{}) (code int, line string) { + if ftp.debug { + log.Printf("Raw-> %s\n", fmt.Sprintf(command, args...), code) + } + + code = -1 + var err error + if err = ftp.send(command, args...); err != nil { + return code, "" + } + if line, err = ftp.receive(); err != nil { + return code, "" + } + code, err = strconv.Atoi(line[:3]) + if ftp.debug { + log.Printf("Raw<- <- %d \n", code) + } + return code, line +} + +// private function to send command and compare return code with expects +func (ftp *FTP) cmd(expects string, command string, args ...interface{}) (line string, err error) { + if err = ftp.send(command, args...); err != nil { + return + } + + if line, err = ftp.receive(); err != nil { + return + } + + if !strings.HasPrefix(line, expects) { + err = errors.New(line) + return + } + + return +} + +// rename file +func (ftp *FTP) Rename(from string, to string) (err error) { + if _, err = ftp.cmd("350", "RNFR %s", from); err != nil { + return + } + + if _, err = ftp.cmd("250", "RNTO %s", to); err != nil { + return + } + + return +} + +// make directory +func (ftp *FTP) Mkd(path string) error { + _, err := ftp.cmd("257", "MKD %s", path) + return err +} + +// get current path +func (ftp *FTP) Pwd() (path string, err error) { + var line string + if line, err = ftp.cmd("257", "PWD"); err != nil { + return + } + + res := REGEX_PWD_PATH.FindAllStringSubmatch(line[4:], -1) + + path = res[0][1] + return +} + +// change current path +func (ftp *FTP) Cwd(path string) (err error) { + _, err = ftp.cmd("250", "CWD %s", path) + return +} + +// delete file +func (ftp *FTP) Dele(path string) (err error) { + if err = ftp.send("DELE %s", path); err != nil { + return + } + + var line string + if line, err = ftp.receive(); err != nil { + return + } + + if !strings.HasPrefix(line, "250") { + return errors.New(line) + } + + return +} + +// secures the ftp connection by using TLS +func (ftp *FTP) AuthTLS(config tls.Config) error { + if _, err := ftp.cmd("234", "AUTH TLS"); err != nil { + return err + } + + // wrap tls on existing connection + ftp.tlsconfig = &config + + ftp.conn = tls.Client(ftp.conn, &config) + ftp.writer = bufio.NewWriter(ftp.conn) + ftp.reader = bufio.NewReader(ftp.conn) + + if _, err := ftp.cmd("200", "PBSZ 0"); err != nil { + return err + } + + if _, err := ftp.cmd("200", "PROT P"); err != nil { + return err + } + + return nil +} + +// read all the buffered bytes and return +func (ftp *FTP) ReadAndDiscard() (int, error) { + var i int + var err error + buffer_size := ftp.reader.Buffered() + for i = 0; i < buffer_size; i++ { + if _, err = ftp.reader.ReadByte(); err != nil { + return i, err + } + } + return i, err +} + +// change transfer type +func (ftp *FTP) Type(t string) error { + _, err := ftp.cmd("200", "TYPE %s", t) + return err +} + +func (ftp *FTP) receiveLine() (string, error) { + line, err := ftp.reader.ReadString('\n') + + if ftp.debug { + log.Printf("< %s", line) + } + + return line, err +} + +func (ftp *FTP) receive() (string, error) { + line, err := ftp.receiveLine() + + if err != nil { + return line, err + } + + if (len(line) >= 4) && (line[3] == '-') { + //Multiline response + closingCode := line[:3] + " " + for { + str, err := ftp.receiveLine() + line = line + str + if err != nil { + return line, err + } + if len(str) < 4 { + if ftp.debug { + log.Println("Uncorrectly terminated response") + } + break + } else { + if str[:4] == closingCode { + break + } + } + } + } + ftp.ReadAndDiscard() + //fmt.Println(line) + return line, err +} + +func (ftp *FTP) send(command string, arguments ...interface{}) error { + if ftp.debug { + log.Printf("> %s", fmt.Sprintf(command, arguments...)) + } + + command = fmt.Sprintf(command, arguments...) + command += "\r\n" + + if _, err := ftp.writer.WriteString(command); err != nil { + return err + } + + if err := ftp.writer.Flush(); err != nil { + return err + } + + return nil +} + +// enables passive data connection and returns port number +func (ftp *FTP) Pasv() (port int, err error) { + var line string + if line, err = ftp.cmd("227", "PASV"); err != nil { + return + } + + re, err := regexp.Compile(`\((.*)\)`) + + res := re.FindAllStringSubmatch(line, -1) + + s := strings.Split(res[0][1], ",") + + l1, _ := strconv.Atoi(s[len(s)-2]) + l2, _ := strconv.Atoi(s[len(s)-1]) + + port = l1<<8 + l2 + + return +} + +// open new data connection +func (ftp *FTP) newConnection(port int) (conn net.Conn, err error) { + addr := fmt.Sprintf("%s:%d", strings.Split(ftp.addr, ":")[0], port) + + if ftp.debug { + log.Printf("Connecting to %s\n", addr) + } + + if conn, err = net.Dial("tcp", addr); err != nil { + return + } + + if ftp.tlsconfig != nil { + conn = tls.Client(conn, ftp.tlsconfig) + } + + return +} + +// upload file +func (ftp *FTP) Stor(path string, r io.Reader) (err error) { + if err = ftp.Type("I"); err != nil { + return + } + + var port int + if port, err = ftp.Pasv(); err != nil { + return + } + + if err = ftp.send("STOR %s", path); err != nil { + return + } + + var pconn net.Conn + if pconn, err = ftp.newConnection(port); err != nil { + return + } + + var line string + if line, err = ftp.receive(); err != nil { + return + } + + if !strings.HasPrefix(line, "150") { + err = errors.New(line) + return + } + + if _, err = io.Copy(pconn, r); err != nil { + return + } + + pconn.Close() + + if line, err = ftp.receive(); err != nil { + return + } + + if !strings.HasPrefix(line, "226") { + err = errors.New(line) + return + } + + return + +} + +// retrieves file +func (ftp *FTP) Retr(path string, retrFn RetrFunc) (s string, err error) { + if err = ftp.Type("I"); err != nil { + return + } + + var port int + if port, err = ftp.Pasv(); err != nil { + return + } + + if err = ftp.send("RETR %s", path); err != nil { + return + } + + var pconn net.Conn + if pconn, err = ftp.newConnection(port); err != nil { + return + } + + var line string + if line, err = ftp.receive(); err != nil { + return + } + + if !strings.HasPrefix(line, "150") { + err = errors.New(line) + return + } + + if err = retrFn(pconn); err != nil { + return + } + + pconn.Close() + + if line, err = ftp.receive(); err != nil { + return + } + + if !strings.HasPrefix(line, "226") { + err = errors.New(line) + return + } + + return +} + +/*func GetFilesList(path string) (files []string, err error) { + +}*/ + +// list the path (or current directory) +func (ftp *FTP) List(path string) (files []string, err error) { + if err = ftp.Type("A"); err != nil { + return + } + + var port int + if port, err = ftp.Pasv(); err != nil { + return + } + + // check if MLSD works + if err = ftp.send("MLSD %s", path); err != nil { + } + + var pconn net.Conn + if pconn, err = ftp.newConnection(port); err != nil { + return + } + + var line string + if line, err = ftp.receive(); err != nil { + return + } + + if !strings.HasPrefix(line, "150") { + // MLSD failed, lets try LIST + if err = ftp.send("LIST %s", path); err != nil { + return + } + + if line, err = ftp.receive(); err != nil { + return + } + + if !strings.HasPrefix(line, "150") { + // Really list is not working here + err = errors.New(line) + return + } + } + + reader := bufio.NewReader(pconn) + + for { + line, err = reader.ReadString('\n') + + if err == io.EOF { + break + } else if err != nil { + return + } + + files = append(files, string(line)) + } + + pconn.Close() + + if line, err = ftp.receive(); err != nil { + return + } + + if !strings.HasPrefix(line, "226") { + err = errors.New(line) + return + } + + return +} + +/* + + +// login on server with strange login behavior +func (ftp *FTP) SmartLogin(username string, password string) (err error) { + var code int + // Maybe the server has some useless words to say. Make him talk + code, _ = ftp.RawCmd("NOOP") + + if code == 220 || code == 530 { + // Maybe with another Noop the server will ask us to login? + code, _ = ftp.RawCmd("NOOP") + if code == 530 { + // ok, let's login + code, _ = ftp.RawCmd("USER %s", username) + code, _ = ftp.RawCmd("NOOP") + if code == 331 { + // user accepted, password required + code, _ = ftp.RawCmd("PASS %s", password) + code, _ = ftp.RawCmd("PASS %s", password) + if code == 230 { + code, _ = ftp.RawCmd("NOOP") + return + } + } + } + + } + // Nothing strange... let's try a normal login + return ftp.Login(username, password) +} + +*/ + +// login to the server +func (ftp *FTP) Login(username string, password string) (err error) { + if _, err = ftp.cmd("331", "USER %s", username); err != nil { + if strings.HasPrefix(err.Error(), "230") { + // Ok, probably anonymous server + // but login was fine, so return no error + err = nil + } else { + return + } + } + + if _, err = ftp.cmd("230", "PASS %s", password); err != nil { + return + } + + return +} + +// connect to server, debug is OFF +func Connect(addr string) (*FTP, error) { + var err error + var conn net.Conn + + if conn, err = net.Dial("tcp", addr); err != nil { + return nil, err + } + + writer := bufio.NewWriter(conn) + reader := bufio.NewReader(conn) + + //reader.ReadString('\n') + object := &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: false} + object.receive() + + return object, nil +} + +// connect to server, debug is ON +func ConnectDbg(addr string) (*FTP, error) { + var err error + var conn net.Conn + + if conn, err = net.Dial("tcp", addr); err != nil { + return nil, err + } + + writer := bufio.NewWriter(conn) + reader := bufio.NewReader(conn) + + var line string + + object := &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: false} + line, _ = object.receive() + + log.Print(line) + + return object, nil +} diff --git a/vendor/src/github.com/dutchcoders/goftp/ftp_test.go b/vendor/src/github.com/dutchcoders/goftp/ftp_test.go new file mode 100644 index 000000000..17c2b0b88 --- /dev/null +++ b/vendor/src/github.com/dutchcoders/goftp/ftp_test.go @@ -0,0 +1,58 @@ +package goftp + +import "testing" + +//import "fmt" + +var goodServer string +var uglyServer string +var badServer string + +func init() { + //ProFTPD 1.3.5 Server (Debian) + goodServer = "bo.mirror.garr.it:21" + + //Symantec EMEA FTP Server + badServer = "ftp.packardbell.com:21" + + //Unknown server + uglyServer = "ftp.musicbrainz.org:21" +} + +func standard(host string) (msg string) { + var err error + var connection *FTP + + if connection, err = Connect(host); err != nil { + return "Can't connect ->" + err.Error() + } + if err = connection.Login("anonymous", "anonymous"); err != nil { + return "Can't login ->" + err.Error() + } + if _, err = connection.List(""); err != nil { + return "Can't list ->" + err.Error() + } + connection.Close() + return "" +} + +func TestLogin_good(t *testing.T) { + str := standard(goodServer) + if len(str) > 0 { + t.Error(str) + } +} + +func TestLogin_bad(t *testing.T) { + str := standard(badServer) + if len(str) > 0 { + t.Error(str) + } +} + +func TestLogin_ugly(t *testing.T) { + str := standard(uglyServer) + if len(str) > 0 { + t.Error(str) + } +} diff --git a/vendor/src/github.com/dutchcoders/goftp/upload.go b/vendor/src/github.com/dutchcoders/goftp/upload.go new file mode 100644 index 000000000..8dbd110f0 --- /dev/null +++ b/vendor/src/github.com/dutchcoders/goftp/upload.go @@ -0,0 +1,98 @@ +// Package goftp upload helper +package goftp + +import ( + "os" + "path/filepath" +) + +func (ftp *FTP) copyDir(localPath string) error { + fullPath, err := filepath.Abs(localPath) + if err != nil { + return err + } + + pwd, err := ftp.Pwd() + if err != nil { + return err + } + + walkFunc := func(path string, fi os.FileInfo, err error) error { + // Stop upon error + if err != nil { + return err + } + relPath, err := filepath.Rel(fullPath, path) + if err != nil { + return err + } + switch { + case fi.IsDir(): + // Walk calls walkFn on root as well + if path == fullPath { + return nil + } + if err = ftp.Mkd(relPath); err != nil { + if _, err = ftp.List(relPath + "/"); err != nil { + return err + } + } + case fi.Mode()&os.ModeSymlink == os.ModeSymlink: + fInfo, err := os.Stat(path) + if err != nil { + return err + } + if fInfo.IsDir() { + err = ftp.Mkd(relPath) + return err + } else if fInfo.Mode()&os.ModeType != 0 { + // ignore other special files + return nil + } + fallthrough + case fi.Mode()&os.ModeType == 0: + if err = ftp.copyFile(path, pwd+"/"+relPath); err != nil { + return err + } + default: + // Ignore other special files + } + + return nil + } + + return filepath.Walk(fullPath, walkFunc) +} + +func (ftp *FTP) copyFile(localPath, serverPath string) (err error) { + var file *os.File + if file, err = os.Open(localPath); err != nil { + return err + } + if err := ftp.Stor(serverPath, file); err != nil { + return err + } + + return nil +} + +// Upload a file, or recursively upload a directory. +// Only normal files and directories are uploaded. +// Symlinks are not kept but treated as normal files/directories if targets are so. +func (ftp *FTP) Upload(localPath string) (err error) { + fInfo, err := os.Stat(localPath) + if err != nil { + return err + } + + switch { + case fInfo.IsDir(): + return ftp.copyDir(localPath) + case fInfo.Mode()&os.ModeType == 0: + return ftp.copyFile(localPath, filepath.Base(localPath)) + default: + // Ignore other special files + } + + return nil +} diff --git a/vendor/src/github.com/satori/go.uuid/.travis.yml b/vendor/src/github.com/satori/go.uuid/.travis.yml new file mode 100644 index 000000000..5e2174620 --- /dev/null +++ b/vendor/src/github.com/satori/go.uuid/.travis.yml @@ -0,0 +1,12 @@ +language: go +go: + - 1.0 + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - 1.6 +sudo: false +notifications: + email: false diff --git a/vendor/src/github.com/satori/go.uuid/LICENSE b/vendor/src/github.com/satori/go.uuid/LICENSE new file mode 100644 index 000000000..488357b8a --- /dev/null +++ b/vendor/src/github.com/satori/go.uuid/LICENSE @@ -0,0 +1,20 @@ +Copyright (C) 2013-2016 by Maxim Bublis + +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/src/github.com/satori/go.uuid/README.md b/vendor/src/github.com/satori/go.uuid/README.md new file mode 100644 index 000000000..b6b626b27 --- /dev/null +++ b/vendor/src/github.com/satori/go.uuid/README.md @@ -0,0 +1,64 @@ +# UUID package for Go language + +[![Build Status](https://travis-ci.org/satori/go.uuid.png?branch=master)](https://travis-ci.org/satori/go.uuid) +[![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.png)](http://godoc.org/github.com/satori/go.uuid) + +This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs. + +With 100% test coverage and benchmarks out of box. + +Supported versions: +* Version 1, based on timestamp and MAC address (RFC 4122) +* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) +* Version 3, based on MD5 hashing (RFC 4122) +* Version 4, based on random numbers (RFC 4122) +* Version 5, based on SHA-1 hashing (RFC 4122) + +## Installation + +Use the `go` command: + + $ go get github.com/satori/go.uuid + +## Requirements + +UUID package requires any stable version of Go Programming Language. + +## Example + +```go +package main + +import ( + "fmt" + "github.com/satori/go.uuid" +) + +func main() { + // Creating UUID Version 4 + u1 := uuid.NewV4() + fmt.Printf("UUIDv4: %s\n", u1) + + // Parsing UUID from string input + u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + if err != nil { + fmt.Printf("Something gone wrong: %s", err) + } + fmt.Printf("Successfully parsed: %s", u2) +} +``` + +## Documentation + +[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project. + +## Links +* [RFC 4122](http://tools.ietf.org/html/rfc4122) +* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) + +## Copyright + +Copyright (C) 2013-2016 by Maxim Bublis . + +UUID package released under MIT License. +See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details. diff --git a/vendor/src/github.com/satori/go.uuid/benchmarks_test.go b/vendor/src/github.com/satori/go.uuid/benchmarks_test.go new file mode 100644 index 000000000..b4e567fc6 --- /dev/null +++ b/vendor/src/github.com/satori/go.uuid/benchmarks_test.go @@ -0,0 +1,121 @@ +// Copyright (C) 2013-2015 by Maxim Bublis +// +// 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. + +package uuid + +import ( + "testing" +) + +func BenchmarkFromBytes(b *testing.B) { + bytes := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + for i := 0; i < b.N; i++ { + FromBytes(bytes) + } +} + +func BenchmarkFromString(b *testing.B) { + s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + for i := 0; i < b.N; i++ { + FromString(s) + } +} + +func BenchmarkFromStringUrn(b *testing.B) { + s := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" + for i := 0; i < b.N; i++ { + FromString(s) + } +} + +func BenchmarkFromStringWithBrackets(b *testing.B) { + s := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" + for i := 0; i < b.N; i++ { + FromString(s) + } +} + +func BenchmarkNewV1(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV1() + } +} + +func BenchmarkNewV2(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV2(DomainPerson) + } +} + +func BenchmarkNewV3(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV3(NamespaceDNS, "www.example.com") + } +} + +func BenchmarkNewV4(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV4() + } +} + +func BenchmarkNewV5(b *testing.B) { + for i := 0; i < b.N; i++ { + NewV5(NamespaceDNS, "www.example.com") + } +} + +func BenchmarkMarshalBinary(b *testing.B) { + u := NewV4() + for i := 0; i < b.N; i++ { + u.MarshalBinary() + } +} + +func BenchmarkMarshalText(b *testing.B) { + u := NewV4() + for i := 0; i < b.N; i++ { + u.MarshalText() + } +} + +func BenchmarkUnmarshalBinary(b *testing.B) { + bytes := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + u := UUID{} + for i := 0; i < b.N; i++ { + u.UnmarshalBinary(bytes) + } +} + +func BenchmarkUnmarshalText(b *testing.B) { + bytes := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + u := UUID{} + for i := 0; i < b.N; i++ { + u.UnmarshalText(bytes) + } +} + +func BenchmarkMarshalToString(b *testing.B) { + u := NewV4() + for i := 0; i < b.N; i++ { + u.String() + } +} diff --git a/vendor/src/github.com/satori/go.uuid/uuid.go b/vendor/src/github.com/satori/go.uuid/uuid.go new file mode 100644 index 000000000..da6ff7120 --- /dev/null +++ b/vendor/src/github.com/satori/go.uuid/uuid.go @@ -0,0 +1,442 @@ +// Copyright (C) 2013-2015 by Maxim Bublis +// +// 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. + +// Package uuid provides implementation of Universally Unique Identifier (UUID). +// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and +// version 2 (as specified in DCE 1.1). +package uuid + +import ( + "bytes" + "crypto/md5" + "crypto/rand" + "crypto/sha1" + "database/sql/driver" + "encoding/binary" + "encoding/hex" + "fmt" + "hash" + "net" + "os" + "sync" + "time" +) + +// UUID layout variants. +const ( + VariantNCS = iota + VariantRFC4122 + VariantMicrosoft + VariantFuture +) + +// UUID DCE domains. +const ( + DomainPerson = iota + DomainGroup + DomainOrg +) + +// Difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). +const epochStart = 122192928000000000 + +// Used in string method conversion +const dash byte = '-' + +// UUID v1/v2 storage. +var ( + storageMutex sync.Mutex + storageOnce sync.Once + epochFunc = unixTimeFunc + clockSequence uint16 + lastTime uint64 + hardwareAddr [6]byte + posixUID = uint32(os.Getuid()) + posixGID = uint32(os.Getgid()) +) + +// String parse helpers. +var ( + urnPrefix = []byte("urn:uuid:") + byteGroups = []int{8, 4, 4, 4, 12} +) + +func initClockSequence() { + buf := make([]byte, 2) + safeRandom(buf) + clockSequence = binary.BigEndian.Uint16(buf) +} + +func initHardwareAddr() { + interfaces, err := net.Interfaces() + if err == nil { + for _, iface := range interfaces { + if len(iface.HardwareAddr) >= 6 { + copy(hardwareAddr[:], iface.HardwareAddr) + return + } + } + } + + // Initialize hardwareAddr randomly in case + // of real network interfaces absence + safeRandom(hardwareAddr[:]) + + // Set multicast bit as recommended in RFC 4122 + hardwareAddr[0] |= 0x01 +} + +func initStorage() { + initClockSequence() + initHardwareAddr() +} + +func safeRandom(dest []byte) { + if _, err := rand.Read(dest); err != nil { + panic(err) + } +} + +// Returns difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and current time. +// This is default epoch calculation function. +func unixTimeFunc() uint64 { + return epochStart + uint64(time.Now().UnixNano()/100) +} + +// UUID representation compliant with specification +// described in RFC 4122. +type UUID [16]byte + +// The nil UUID is special form of UUID that is specified to have all +// 128 bits set to zero. +var Nil = UUID{} + +// Predefined namespace UUIDs. +var ( + NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8") + NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8") + NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8") +) + +// And returns result of binary AND of two UUIDs. +func And(u1 UUID, u2 UUID) UUID { + u := UUID{} + for i := 0; i < 16; i++ { + u[i] = u1[i] & u2[i] + } + return u +} + +// Or returns result of binary OR of two UUIDs. +func Or(u1 UUID, u2 UUID) UUID { + u := UUID{} + for i := 0; i < 16; i++ { + u[i] = u1[i] | u2[i] + } + return u +} + +// Equal returns true if u1 and u2 equals, otherwise returns false. +func Equal(u1 UUID, u2 UUID) bool { + return bytes.Equal(u1[:], u2[:]) +} + +// Version returns algorithm version used to generate UUID. +func (u UUID) Version() uint { + return uint(u[6] >> 4) +} + +// Variant returns UUID layout variant. +func (u UUID) Variant() uint { + switch { + case (u[8] & 0x80) == 0x00: + return VariantNCS + case (u[8]&0xc0)|0x80 == 0x80: + return VariantRFC4122 + case (u[8]&0xe0)|0xc0 == 0xc0: + return VariantMicrosoft + } + return VariantFuture +} + +// Bytes returns bytes slice representation of UUID. +func (u UUID) Bytes() []byte { + return u[:] +} + +// Returns canonical string representation of UUID: +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. +func (u UUID) String() string { + buf := make([]byte, 36) + + hex.Encode(buf[0:8], u[0:4]) + buf[8] = dash + hex.Encode(buf[9:13], u[4:6]) + buf[13] = dash + hex.Encode(buf[14:18], u[6:8]) + buf[18] = dash + hex.Encode(buf[19:23], u[8:10]) + buf[23] = dash + hex.Encode(buf[24:], u[10:]) + + return string(buf) +} + +// SetVersion sets version bits. +func (u *UUID) SetVersion(v byte) { + u[6] = (u[6] & 0x0f) | (v << 4) +} + +// SetVariant sets variant bits as described in RFC 4122. +func (u *UUID) SetVariant() { + u[8] = (u[8] & 0xbf) | 0x80 +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The encoding is the same as returned by String. +func (u UUID) MarshalText() (text []byte, err error) { + text = []byte(u.String()) + return +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Following formats are supported: +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" +func (u *UUID) UnmarshalText(text []byte) (err error) { + if len(text) < 32 { + err = fmt.Errorf("uuid: UUID string too short: %s", text) + return + } + + t := text[:] + + if bytes.Equal(t[:9], urnPrefix) { + t = t[9:] + } else if t[0] == '{' { + t = t[1:] + } + + b := u[:] + + for _, byteGroup := range byteGroups { + if t[0] == '-' { + t = t[1:] + } + + if len(t) < byteGroup { + err = fmt.Errorf("uuid: UUID string too short: %s", text) + return + } + + _, err = hex.Decode(b[:byteGroup/2], t[:byteGroup]) + + if err != nil { + return + } + + t = t[byteGroup:] + b = b[byteGroup/2:] + } + + return +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (u UUID) MarshalBinary() (data []byte, err error) { + data = u.Bytes() + return +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It will return error if the slice isn't 16 bytes long. +func (u *UUID) UnmarshalBinary(data []byte) (err error) { + if len(data) != 16 { + err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) + return + } + copy(u[:], data) + + return +} + +// Value implements the driver.Valuer interface. +func (u UUID) Value() (driver.Value, error) { + return u.String(), nil +} + +// Scan implements the sql.Scanner interface. +// A 16-byte slice is handled by UnmarshalBinary, while +// a longer byte slice or a string is handled by UnmarshalText. +func (u *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + if len(src) == 16 { + return u.UnmarshalBinary(src) + } + return u.UnmarshalText(src) + + case string: + return u.UnmarshalText([]byte(src)) + } + + return fmt.Errorf("uuid: cannot convert %T to UUID", src) +} + +// FromBytes returns UUID converted from raw byte slice input. +// It will return error if the slice isn't 16 bytes long. +func FromBytes(input []byte) (u UUID, err error) { + err = u.UnmarshalBinary(input) + return +} + +// FromBytesOrNil returns UUID converted from raw byte slice input. +// Same behavior as FromBytes, but returns a Nil UUID on error. +func FromBytesOrNil(input []byte) UUID { + uuid, err := FromBytes(input) + if err != nil { + return Nil + } + return uuid +} + +// FromString returns UUID parsed from string input. +// Input is expected in a form accepted by UnmarshalText. +func FromString(input string) (u UUID, err error) { + err = u.UnmarshalText([]byte(input)) + return +} + +// FromStringOrNil returns UUID parsed from string input. +// Same behavior as FromString, but returns a Nil UUID on error. +func FromStringOrNil(input string) UUID { + uuid, err := FromString(input) + if err != nil { + return Nil + } + return uuid +} + +// Returns UUID v1/v2 storage state. +// Returns epoch timestamp, clock sequence, and hardware address. +func getStorage() (uint64, uint16, []byte) { + storageOnce.Do(initStorage) + + storageMutex.Lock() + defer storageMutex.Unlock() + + timeNow := epochFunc() + // Clock changed backwards since last UUID generation. + // Should increase clock sequence. + if timeNow <= lastTime { + clockSequence++ + } + lastTime = timeNow + + return timeNow, clockSequence, hardwareAddr[:] +} + +// NewV1 returns UUID based on current timestamp and MAC address. +func NewV1() UUID { + u := UUID{} + + timeNow, clockSeq, hardwareAddr := getStorage() + + binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + + copy(u[10:], hardwareAddr) + + u.SetVersion(1) + u.SetVariant() + + return u +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func NewV2(domain byte) UUID { + u := UUID{} + + timeNow, clockSeq, hardwareAddr := getStorage() + + switch domain { + case DomainPerson: + binary.BigEndian.PutUint32(u[0:], posixUID) + case DomainGroup: + binary.BigEndian.PutUint32(u[0:], posixGID) + } + + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + u[9] = domain + + copy(u[10:], hardwareAddr) + + u.SetVersion(2) + u.SetVariant() + + return u +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func NewV3(ns UUID, name string) UUID { + u := newFromHash(md5.New(), ns, name) + u.SetVersion(3) + u.SetVariant() + + return u +} + +// NewV4 returns random generated UUID. +func NewV4() UUID { + u := UUID{} + safeRandom(u[:]) + u.SetVersion(4) + u.SetVariant() + + return u +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func NewV5(ns UUID, name string) UUID { + u := newFromHash(sha1.New(), ns, name) + u.SetVersion(5) + u.SetVariant() + + return u +} + +// Returns UUID based on hashing of namespace UUID and name. +func newFromHash(h hash.Hash, ns UUID, name string) UUID { + u := UUID{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} diff --git a/vendor/src/github.com/satori/go.uuid/uuid_test.go b/vendor/src/github.com/satori/go.uuid/uuid_test.go new file mode 100644 index 000000000..aa275345f --- /dev/null +++ b/vendor/src/github.com/satori/go.uuid/uuid_test.go @@ -0,0 +1,520 @@ +// Copyright (C) 2013, 2015 by Maxim Bublis +// +// 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. + +package uuid + +import ( + "bytes" + "testing" +) + +func TestBytes(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + bytes1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + if !bytes.Equal(u.Bytes(), bytes1) { + t.Errorf("Incorrect bytes representation for UUID: %s", u) + } +} + +func TestString(t *testing.T) { + if NamespaceDNS.String() != "6ba7b810-9dad-11d1-80b4-00c04fd430c8" { + t.Errorf("Incorrect string representation for UUID: %s", NamespaceDNS.String()) + } +} + +func TestEqual(t *testing.T) { + if !Equal(NamespaceDNS, NamespaceDNS) { + t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceDNS) + } + + if Equal(NamespaceDNS, NamespaceURL) { + t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceURL) + } +} + +func TestOr(t *testing.T) { + u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff} + u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00} + + u := UUID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + + if !Equal(u, Or(u1, u2)) { + t.Errorf("Incorrect bitwise OR result %s", Or(u1, u2)) + } +} + +func TestAnd(t *testing.T) { + u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff} + u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00} + + u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if !Equal(u, And(u1, u2)) { + t.Errorf("Incorrect bitwise AND result %s", And(u1, u2)) + } +} + +func TestVersion(t *testing.T) { + u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u.Version() != 1 { + t.Errorf("Incorrect version for UUID: %d", u.Version()) + } +} + +func TestSetVersion(t *testing.T) { + u := UUID{} + u.SetVersion(4) + + if u.Version() != 4 { + t.Errorf("Incorrect version for UUID after u.setVersion(4): %d", u.Version()) + } +} + +func TestVariant(t *testing.T) { + u1 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u1.Variant() != VariantNCS { + t.Errorf("Incorrect variant for UUID variant %d: %d", VariantNCS, u1.Variant()) + } + + u2 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u2.Variant() != VariantRFC4122 { + t.Errorf("Incorrect variant for UUID variant %d: %d", VariantRFC4122, u2.Variant()) + } + + u3 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u3.Variant() != VariantMicrosoft { + t.Errorf("Incorrect variant for UUID variant %d: %d", VariantMicrosoft, u3.Variant()) + } + + u4 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + + if u4.Variant() != VariantFuture { + t.Errorf("Incorrect variant for UUID variant %d: %d", VariantFuture, u4.Variant()) + } +} + +func TestSetVariant(t *testing.T) { + u := new(UUID) + u.SetVariant() + + if u.Variant() != VariantRFC4122 { + t.Errorf("Incorrect variant for UUID after u.setVariant(): %d", u.Variant()) + } +} + +func TestFromBytes(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + u1, err := FromBytes(b1) + if err != nil { + t.Errorf("Error parsing UUID from bytes: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + b2 := []byte{} + + _, err = FromBytes(b2) + if err == nil { + t.Errorf("Should return error parsing from empty byte slice, got %s", err) + } +} + +func TestMarshalBinary(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + b2, err := u.MarshalBinary() + if err != nil { + t.Errorf("Error marshaling UUID: %s", err) + } + + if !bytes.Equal(b1, b2) { + t.Errorf("Marshaled UUID should be %s, got %s", b1, b2) + } +} + +func TestUnmarshalBinary(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + u1 := UUID{} + err := u1.UnmarshalBinary(b1) + if err != nil { + t.Errorf("Error unmarshaling UUID: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + b2 := []byte{} + u2 := UUID{} + + err = u2.UnmarshalBinary(b2) + if err == nil { + t.Errorf("Should return error unmarshalling from empty byte slice, got %s", err) + } +} + +func TestFromString(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + s2 := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" + s3 := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" + + _, err := FromString("") + if err == nil { + t.Errorf("Should return error trying to parse empty string, got %s", err) + } + + u1, err := FromString(s1) + if err != nil { + t.Errorf("Error parsing UUID from string: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + u2, err := FromString(s2) + if err != nil { + t.Errorf("Error parsing UUID from string: %s", err) + } + + if !Equal(u, u2) { + t.Errorf("UUIDs should be equal: %s and %s", u, u2) + } + + u3, err := FromString(s3) + if err != nil { + t.Errorf("Error parsing UUID from string: %s", err) + } + + if !Equal(u, u3) { + t.Errorf("UUIDs should be equal: %s and %s", u, u3) + } +} + +func TestFromStringShort(t *testing.T) { + // Invalid 35-character UUID string + s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c" + + for i := len(s1); i >= 0; i-- { + _, err := FromString(s1[:i]) + if err == nil { + t.Errorf("Should return error trying to parse too short string, got %s", err) + } + } +} + +func TestFromStringOrNil(t *testing.T) { + u := FromStringOrNil("") + if u != Nil { + t.Errorf("Should return Nil UUID on parse failure, got %s", u) + } +} + +func TestFromBytesOrNil(t *testing.T) { + b := []byte{} + u := FromBytesOrNil(b) + if u != Nil { + t.Errorf("Should return Nil UUID on parse failure, got %s", u) + } +} + +func TestMarshalText(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + + b2, err := u.MarshalText() + if err != nil { + t.Errorf("Error marshaling UUID: %s", err) + } + + if !bytes.Equal(b1, b2) { + t.Errorf("Marshaled UUID should be %s, got %s", b1, b2) + } +} + +func TestUnmarshalText(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + + u1 := UUID{} + err := u1.UnmarshalText(b1) + if err != nil { + t.Errorf("Error unmarshaling UUID: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + b2 := []byte("") + u2 := UUID{} + + err = u2.UnmarshalText(b2) + if err == nil { + t.Errorf("Should return error trying to unmarshal from empty string") + } +} + +func TestValue(t *testing.T) { + u, err := FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + if err != nil { + t.Errorf("Error parsing UUID from string: %s", err) + } + + val, err := u.Value() + if err != nil { + t.Errorf("Error getting UUID value: %s", err) + } + + if val != u.String() { + t.Errorf("Wrong value returned, should be equal: %s and %s", val, u) + } +} + +func TestScanBinary(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + + u1 := UUID{} + err := u1.Scan(b1) + if err != nil { + t.Errorf("Error unmarshaling UUID: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + b2 := []byte{} + u2 := UUID{} + + err = u2.Scan(b2) + if err == nil { + t.Errorf("Should return error unmarshalling from empty byte slice, got %s", err) + } +} + +func TestScanString(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8" + + u1 := UUID{} + err := u1.Scan(s1) + if err != nil { + t.Errorf("Error unmarshaling UUID: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + s2 := "" + u2 := UUID{} + + err = u2.Scan(s2) + if err == nil { + t.Errorf("Should return error trying to unmarshal from empty string") + } +} + +func TestScanText(t *testing.T) { + u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8} + b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + + u1 := UUID{} + err := u1.Scan(b1) + if err != nil { + t.Errorf("Error unmarshaling UUID: %s", err) + } + + if !Equal(u, u1) { + t.Errorf("UUIDs should be equal: %s and %s", u, u1) + } + + b2 := []byte("") + u2 := UUID{} + + err = u2.Scan(b2) + if err == nil { + t.Errorf("Should return error trying to unmarshal from empty string") + } +} + +func TestScanUnsupported(t *testing.T) { + u := UUID{} + + err := u.Scan(true) + if err == nil { + t.Errorf("Should return error trying to unmarshal from bool") + } +} + +func TestNewV1(t *testing.T) { + u := NewV1() + + if u.Version() != 1 { + t.Errorf("UUIDv1 generated with incorrect version: %d", u.Version()) + } + + if u.Variant() != VariantRFC4122 { + t.Errorf("UUIDv1 generated with incorrect variant: %d", u.Variant()) + } + + u1 := NewV1() + u2 := NewV1() + + if Equal(u1, u2) { + t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u1, u2) + } + + oldFunc := epochFunc + epochFunc = func() uint64 { return 0 } + + u3 := NewV1() + u4 := NewV1() + + if Equal(u3, u4) { + t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u3, u4) + } + + epochFunc = oldFunc +} + +func TestNewV2(t *testing.T) { + u1 := NewV2(DomainPerson) + + if u1.Version() != 2 { + t.Errorf("UUIDv2 generated with incorrect version: %d", u1.Version()) + } + + if u1.Variant() != VariantRFC4122 { + t.Errorf("UUIDv2 generated with incorrect variant: %d", u1.Variant()) + } + + u2 := NewV2(DomainGroup) + + if u2.Version() != 2 { + t.Errorf("UUIDv2 generated with incorrect version: %d", u2.Version()) + } + + if u2.Variant() != VariantRFC4122 { + t.Errorf("UUIDv2 generated with incorrect variant: %d", u2.Variant()) + } +} + +func TestNewV3(t *testing.T) { + u := NewV3(NamespaceDNS, "www.example.com") + + if u.Version() != 3 { + t.Errorf("UUIDv3 generated with incorrect version: %d", u.Version()) + } + + if u.Variant() != VariantRFC4122 { + t.Errorf("UUIDv3 generated with incorrect variant: %d", u.Variant()) + } + + if u.String() != "5df41881-3aed-3515-88a7-2f4a814cf09e" { + t.Errorf("UUIDv3 generated incorrectly: %s", u.String()) + } + + u = NewV3(NamespaceDNS, "python.org") + + if u.String() != "6fa459ea-ee8a-3ca4-894e-db77e160355e" { + t.Errorf("UUIDv3 generated incorrectly: %s", u.String()) + } + + u1 := NewV3(NamespaceDNS, "golang.org") + u2 := NewV3(NamespaceDNS, "golang.org") + if !Equal(u1, u2) { + t.Errorf("UUIDv3 generated different UUIDs for same namespace and name: %s and %s", u1, u2) + } + + u3 := NewV3(NamespaceDNS, "example.com") + if Equal(u1, u3) { + t.Errorf("UUIDv3 generated same UUIDs for different names in same namespace: %s and %s", u1, u2) + } + + u4 := NewV3(NamespaceURL, "golang.org") + if Equal(u1, u4) { + t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4) + } +} + +func TestNewV4(t *testing.T) { + u := NewV4() + + if u.Version() != 4 { + t.Errorf("UUIDv4 generated with incorrect version: %d", u.Version()) + } + + if u.Variant() != VariantRFC4122 { + t.Errorf("UUIDv4 generated with incorrect variant: %d", u.Variant()) + } +} + +func TestNewV5(t *testing.T) { + u := NewV5(NamespaceDNS, "www.example.com") + + if u.Version() != 5 { + t.Errorf("UUIDv5 generated with incorrect version: %d", u.Version()) + } + + if u.Variant() != VariantRFC4122 { + t.Errorf("UUIDv5 generated with incorrect variant: %d", u.Variant()) + } + + u = NewV5(NamespaceDNS, "python.org") + + if u.String() != "886313e1-3b8a-5372-9b90-0c9aee199e5d" { + t.Errorf("UUIDv5 generated incorrectly: %s", u.String()) + } + + u1 := NewV5(NamespaceDNS, "golang.org") + u2 := NewV5(NamespaceDNS, "golang.org") + if !Equal(u1, u2) { + t.Errorf("UUIDv5 generated different UUIDs for same namespace and name: %s and %s", u1, u2) + } + + u3 := NewV5(NamespaceDNS, "example.com") + if Equal(u1, u3) { + t.Errorf("UUIDv5 generated same UUIDs for different names in same namespace: %s and %s", u1, u2) + } + + u4 := NewV5(NamespaceURL, "golang.org") + if Equal(u1, u4) { + t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4) + } +}