Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions api/client/fip.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
71 changes: 53 additions & 18 deletions api/client/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Copy link
Contributor

@laijs laijs Jun 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sync.WaitGroup seems better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chan error can pass error, but sync.WaitGroup can't.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WaitGroup has to wait for all ExecCmd goroutines to stop before checking results, while current implementation can check for results as it goes and returns failure as soon as any command fails.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chan error has to stay even if execCount is changed to sync.WaitGroup.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the code before this patch, the execs are running parallel.
only the requests are sent in order. it is more efficient to send the requests parallel, but the changelog is confusing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is more about waiting in parallel rather than issuing requests in parallel. I'll fix up the changelog.

fip string
)

initConfig = &container.Config{
User: config.User,
Env: config.Env,
Image: INIT_VOLUME_IMAGE,
StopSignal: config.StopSignal,
}
Expand All @@ -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 {
Expand All @@ -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)
Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a bug here. the var vol is an encapsulated variable. the two threads share the vol, but the loop thread(for _, vol := range initvols {) may change the vol before this uploader thread, and the uploader thread use the wrong vol.

My test hits this bug

./hyper/hyper run -t -i -v https://github.com/nginxinc/docker-nginx/raw/master/stable/alpine/Dockerfile:/data1 -v https://github.com/nginxinc/docker-nginx.git:/data2 -v /Users/laijs/work/gopath/src/github.com/hyperhq/hypercli/hyper/:/data3 -v /Users/laijs/work/gopath/src/github.com/hyperhq/hypercli/man/:/data4 busybox sh
./hyper/hyper: Failed to upload https://github.com/nginxinc/docker-nginx/raw/master/stable/alpine/Dockerfile: stat https://github.com/nginxinc/docker-nginx/raw/master/stable/alpine/Dockerfile: no such file or directory.

simple fix

diff --git a/api/client/run.go b/api/client/run.go
index 30a66df..b21f0c4 100644
--- a/api/client/run.go
+++ b/api/client/run.go
@@ -186,13 +186,13 @@ func (cli *DockerCli) initSpecialVolumes(config *container.Config, hostConfig *c
                    return err
                }
            }
-           go func() {
+           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
        }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, indeed! Thanks!

}(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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same problem here with the cmd

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmd is different. It is a new variable for each vol in the loop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good, you are right.

}
}()
}

// 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)
Expand Down
36 changes: 33 additions & 3 deletions api/client/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
21 changes: 21 additions & 0 deletions vendor/src/github.com/dutchcoders/goftp/LICENSE
Original file line number Diff line number Diff line change
@@ -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.
109 changes: 109 additions & 0 deletions vendor/src/github.com/dutchcoders/goftp/README.md
Original file line number Diff line number Diff line change
@@ -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**
- <https://twitter.com/remco_verhoef>
- <https://twitter.com/dutchcoders>

## Copyright and license

Code and documentation copyright 2011-2014 Remco Verhoef.
Code released under [the MIT license](LICENSE).
27 changes: 27 additions & 0 deletions vendor/src/github.com/dutchcoders/goftp/doc.go
Original file line number Diff line number Diff line change
@@ -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
Loading