зеркало из https://github.com/microsoft/docker.git
Merge branch 'master' into simpler-build-upload
Conflicts: api.go builder_client.go commands.go
This commit is contained in:
Коммит
e43323221b
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -1,5 +1,25 @@
|
|||
# Changelog
|
||||
|
||||
## 0.4.3 (2013-06-19)
|
||||
+ Builder: ADD of a local file will detect tar archives and unpack them
|
||||
* Runtime: Remove bsdtar dependency
|
||||
* Runtime: Add unix socket and multiple -H support
|
||||
* Runtime: Prevent rm of running containers
|
||||
* Runtime: Use go1.1 cookiejar
|
||||
* Builder: ADD improvements: use tar for copy + automatically unpack local archives
|
||||
* Builder: ADD uses tar/untar for copies instead of calling 'cp -ar'
|
||||
* Builder: nicer output for 'docker build'
|
||||
* Builder: fixed the behavior of ADD to be (mostly) reverse-compatible, predictable and well-documented.
|
||||
* Client: HumanReadable ProgressBar sizes in pull
|
||||
* Client: Fix docker version's git commit output
|
||||
* API: Send all tags on History API call
|
||||
* API: Add tag lookup to history command. Fixes #882
|
||||
- Runtime: Fix issue detaching from running TTY container
|
||||
- Runtime: Forbid parralel push/pull for a single image/repo. Fixes #311
|
||||
- Runtime: Fix race condition within Run command when attaching.
|
||||
- Builder: fix a bug which caused builds to fail if ADD was the first command
|
||||
- Documentation: fix missing command in irc bouncer example
|
||||
|
||||
## 0.4.2 (2013-06-17)
|
||||
- Packaging: Bumped version to work around an Ubuntu bug
|
||||
|
||||
|
|
3
Makefile
3
Makefile
|
@ -74,6 +74,9 @@ endif
|
|||
test: all
|
||||
@(cd $(DOCKER_DIR); sudo -E go test $(GO_OPTIONS))
|
||||
|
||||
testall: all
|
||||
@(cd $(DOCKER_DIR); sudo -E go test ./... $(GO_OPTIONS))
|
||||
|
||||
fmt:
|
||||
@gofmt -s -l -w .
|
||||
|
||||
|
|
19
api.go
19
api.go
|
@ -8,12 +8,16 @@ import (
|
|||
"github.com/gorilla/mux"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const APIVERSION = 1.3
|
||||
const DEFAULTHTTPHOST string = "127.0.0.1"
|
||||
const DEFAULTHTTPPORT int = 4243
|
||||
|
||||
func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
|
||||
conn, _, err := w.(http.Hijacker).Hijack()
|
||||
|
@ -836,12 +840,21 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
|
|||
return r, nil
|
||||
}
|
||||
|
||||
func ListenAndServe(addr string, srv *Server, logging bool) error {
|
||||
log.Printf("Listening for HTTP on %s\n", addr)
|
||||
func ListenAndServe(proto, addr string, srv *Server, logging bool) error {
|
||||
log.Printf("Listening for HTTP on %s (%s)\n", addr, proto)
|
||||
|
||||
r, err := createRouter(srv, logging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return http.ListenAndServe(addr, r)
|
||||
l, e := net.Listen(proto, addr)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
//as the daemon is launched as root, change to permission of the socket to allow non-root to connect
|
||||
if proto == "unix" {
|
||||
os.Chmod(addr, 0777)
|
||||
}
|
||||
httpSrv := http.Server{Addr: addr, Handler: r}
|
||||
return httpSrv.Serve(l)
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ func decodeAuth(authStr string) (*AuthConfig, error) {
|
|||
func LoadConfig(rootPath string) (*AuthConfig, error) {
|
||||
confFile := path.Join(rootPath, CONFIGFILE)
|
||||
if _, err := os.Stat(confFile); err != nil {
|
||||
return &AuthConfig{rootPath:rootPath}, ErrConfigFileMissing
|
||||
return &AuthConfig{rootPath: rootPath}, ErrConfigFileMissing
|
||||
}
|
||||
b, err := ioutil.ReadFile(confFile)
|
||||
if err != nil {
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
|
||||
func TestEncodeAuth(t *testing.T) {
|
||||
newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
|
||||
authStr := EncodeAuth(newAuthConfig)
|
||||
decAuthConfig, err := DecodeAuth(authStr)
|
||||
authStr := encodeAuth(newAuthConfig)
|
||||
decAuthConfig, err := decodeAuth(authStr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ func TestLogin(t *testing.T) {
|
|||
os.Setenv("DOCKER_INDEX_URL", "https://indexstaging-docker.dotcloud.com")
|
||||
defer os.Setenv("DOCKER_INDEX_URL", "")
|
||||
authConfig := NewAuthConfig("unittester", "surlautrerivejetattendrai", "noise+unittester@dotcloud.com", "/tmp")
|
||||
status, err := Login(authConfig)
|
||||
status, err := Login(authConfig, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func TestCreateAccount(t *testing.T) {
|
|||
token := hex.EncodeToString(tokenBuffer)[:12]
|
||||
username := "ut" + token
|
||||
authConfig := NewAuthConfig(username, "test42", "docker-ut+"+token+"@example.com", "/tmp")
|
||||
status, err := Login(authConfig)
|
||||
status, err := Login(authConfig, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func TestCreateAccount(t *testing.T) {
|
|||
t.Fatalf("Expected status: \"%s\", found \"%s\" instead.", expectedStatus, status)
|
||||
}
|
||||
|
||||
status, err = Login(authConfig)
|
||||
status, err = Login(authConfig, false)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but found nil instead")
|
||||
}
|
||||
|
|
67
commands.go
67
commands.go
|
@ -28,7 +28,7 @@ import (
|
|||
"unicode"
|
||||
)
|
||||
|
||||
const VERSION = "0.4.2"
|
||||
const VERSION = "0.4.3"
|
||||
|
||||
var (
|
||||
GITCOMMIT string
|
||||
|
@ -39,8 +39,8 @@ func (cli *DockerCli) getMethod(name string) (reflect.Method, bool) {
|
|||
return reflect.TypeOf(cli).MethodByName(methodName)
|
||||
}
|
||||
|
||||
func ParseCommands(addr string, port int, args ...string) error {
|
||||
cli := NewDockerCli(addr, port)
|
||||
func ParseCommands(proto, addr string, args ...string) error {
|
||||
cli := NewDockerCli(proto, addr)
|
||||
|
||||
if len(args) > 0 {
|
||||
method, exists := cli.getMethod(args[0])
|
||||
|
@ -73,7 +73,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=\"%s:%d\": Host:port to bind/connect to\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", cli.host, cli.port)
|
||||
help := fmt.Sprintf("Usage: docker [OPTIONS] COMMAND [arg...]\n -H=[tcp://%s:%d]: tcp://host:port to bind/connect to or unix://path/to/socker to use\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n", DEFAULTHTTPHOST, DEFAULTHTTPPORT)
|
||||
for _, command := range [][2]string{
|
||||
{"attach", "Attach to a running container"},
|
||||
{"build", "Build a container from a Dockerfile"},
|
||||
|
@ -1055,37 +1055,18 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
|
|||
return fmt.Errorf("Impossible to attach to a stopped container, start it first")
|
||||
}
|
||||
|
||||
splitStderr := container.Config.Tty
|
||||
|
||||
connections := 1
|
||||
if splitStderr {
|
||||
connections += 1
|
||||
}
|
||||
chErrors := make(chan error, connections)
|
||||
if container.Config.Tty {
|
||||
cli.monitorTtySize(cmd.Arg(0))
|
||||
}
|
||||
if splitStderr {
|
||||
go func() {
|
||||
chErrors <- cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?stream=1&stderr=1", false, nil, os.Stderr)
|
||||
}()
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("stream", "1")
|
||||
v.Set("stdin", "1")
|
||||
v.Set("stdout", "1")
|
||||
if !splitStderr {
|
||||
v.Set("stderr", "1")
|
||||
}
|
||||
go func() {
|
||||
chErrors <- cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, os.Stdin, os.Stdout)
|
||||
}()
|
||||
for connections > 0 {
|
||||
err := <-chErrors
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
connections -= 1
|
||||
v.Set("stderr", "1")
|
||||
|
||||
if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty, os.Stdin, os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -1311,7 +1292,7 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
|
|||
params = bytes.NewBuffer(buf)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), params)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), params)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
@ -1321,7 +1302,13 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
|
|||
} else if method == "POST" {
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
dial, err := net.Dial(cli.proto, cli.addr)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
clientconn := httputil.NewClientConn(dial, nil)
|
||||
resp, err := clientconn.Do(req)
|
||||
defer clientconn.Close()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return nil, -1, fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||
|
@ -1346,7 +1333,7 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
|
|||
if (method == "POST" || method == "PUT") && in == nil {
|
||||
in = bytes.NewReader([]byte{})
|
||||
}
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("http://%s:%d/v%g%s", cli.host, cli.port, APIVERSION, path), in)
|
||||
req, err := http.NewRequest(method, fmt.Sprintf("/v%g%s", APIVERSION, path), in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1354,7 +1341,13 @@ func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer) e
|
|||
if method == "POST" {
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
dial, err := net.Dial(cli.proto, cli.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientconn := httputil.NewClientConn(dial, nil)
|
||||
resp, err := clientconn.Do(req)
|
||||
defer clientconn.Close()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
|
||||
|
@ -1404,7 +1397,7 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in *os.Fi
|
|||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
dial, err := net.Dial("tcp", fmt.Sprintf("%s:%d", cli.host, cli.port))
|
||||
dial, err := net.Dial(cli.proto, cli.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1487,13 +1480,13 @@ func Subcmd(name, signature, description string) *flag.FlagSet {
|
|||
return flags
|
||||
}
|
||||
|
||||
func NewDockerCli(addr string, port int) *DockerCli {
|
||||
func NewDockerCli(proto, addr string) *DockerCli {
|
||||
authConfig, _ := auth.LoadConfig(os.Getenv("HOME"))
|
||||
return &DockerCli{addr, port, authConfig}
|
||||
return &DockerCli{proto, addr, authConfig}
|
||||
}
|
||||
|
||||
type DockerCli struct {
|
||||
host string
|
||||
port int
|
||||
proto string
|
||||
addr string
|
||||
authConfig *auth.AuthConfig
|
||||
}
|
||||
|
|
|
@ -24,40 +24,29 @@ func main() {
|
|||
docker.SysInit()
|
||||
return
|
||||
}
|
||||
host := "127.0.0.1"
|
||||
port := 4243
|
||||
// FIXME: Switch d and D ? (to be more sshd like)
|
||||
flDaemon := flag.Bool("d", false, "Daemon mode")
|
||||
flDebug := flag.Bool("D", false, "Debug mode")
|
||||
flAutoRestart := flag.Bool("r", false, "Restart previously running containers")
|
||||
bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge")
|
||||
pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
|
||||
flHost := flag.String("H", fmt.Sprintf("%s:%d", host, port), "Host:port to bind/connect to")
|
||||
flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
|
||||
flDns := flag.String("dns", "", "Set custom dns servers")
|
||||
flHosts := docker.ListOpts{fmt.Sprintf("tcp://%s:%d", docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT)}
|
||||
flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use")
|
||||
flag.Parse()
|
||||
if len(flHosts) > 1 {
|
||||
flHosts = flHosts[1:len(flHosts)] //trick to display a nice defaul value in the usage
|
||||
}
|
||||
for i, flHost := range flHosts {
|
||||
flHosts[i] = utils.ParseHost(docker.DEFAULTHTTPHOST, docker.DEFAULTHTTPPORT, flHost)
|
||||
}
|
||||
|
||||
if *bridgeName != "" {
|
||||
docker.NetworkBridgeIface = *bridgeName
|
||||
} else {
|
||||
docker.NetworkBridgeIface = docker.DefaultNetworkBridge
|
||||
}
|
||||
|
||||
if strings.Contains(*flHost, ":") {
|
||||
hostParts := strings.Split(*flHost, ":")
|
||||
if len(hostParts) != 2 {
|
||||
log.Fatal("Invalid bind address format.")
|
||||
os.Exit(-1)
|
||||
}
|
||||
if hostParts[0] != "" {
|
||||
host = hostParts[0]
|
||||
}
|
||||
if p, err := strconv.Atoi(hostParts[1]); err == nil {
|
||||
port = p
|
||||
}
|
||||
} else {
|
||||
host = *flHost
|
||||
}
|
||||
|
||||
if *flDebug {
|
||||
os.Setenv("DEBUG", "1")
|
||||
}
|
||||
|
@ -67,12 +56,17 @@ func main() {
|
|||
flag.Usage()
|
||||
return
|
||||
}
|
||||
if err := daemon(*pidfile, host, port, *flAutoRestart, *flEnableCors, *flDns); err != nil {
|
||||
if err := daemon(*pidfile, flHosts, *flAutoRestart, *flEnableCors, *flDns); err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
} else {
|
||||
if err := docker.ParseCommands(host, port, flag.Args()...); err != nil {
|
||||
if len(flHosts) > 1 {
|
||||
log.Fatal("Please specify only one -H")
|
||||
return
|
||||
}
|
||||
protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
|
||||
if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
|
||||
log.Fatal(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
@ -106,10 +100,7 @@ func removePidFile(pidfile string) {
|
|||
}
|
||||
}
|
||||
|
||||
func daemon(pidfile, addr string, port int, autoRestart, enableCors bool, flDns string) error {
|
||||
if addr != "127.0.0.1" {
|
||||
log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
||||
}
|
||||
func daemon(pidfile string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error {
|
||||
if err := createPidFile(pidfile); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -131,6 +122,28 @@ func daemon(pidfile, addr string, port int, autoRestart, enableCors bool, flDns
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return docker.ListenAndServe(fmt.Sprintf("%s:%d", addr, port), server, true)
|
||||
chErrors := make(chan error, len(protoAddrs))
|
||||
for _, protoAddr := range protoAddrs {
|
||||
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
|
||||
if protoAddrParts[0] == "unix" {
|
||||
syscall.Unlink(protoAddrParts[1])
|
||||
} else if protoAddrParts[0] == "tcp" {
|
||||
if !strings.HasPrefix(protoAddrParts[1], "127.0.0.1") {
|
||||
log.Println("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
||||
}
|
||||
} else {
|
||||
log.Fatal("Invalid protocol format.")
|
||||
os.Exit(-1)
|
||||
}
|
||||
go func() {
|
||||
chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true)
|
||||
}()
|
||||
}
|
||||
for i := 0; i < len(protoAddrs); i += 1 {
|
||||
err := <-chErrors
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1027,5 +1027,5 @@ In this version of the API, /attach, uses hijacking to transport stdin, stdout a
|
|||
|
||||
To enable cross origin requests to the remote api add the flag "-api-enable-cors" when running docker in daemon mode.
|
||||
|
||||
docker -d -H="192.168.1.9:4243" -api-enable-cors
|
||||
docker -d -H="tcp://192.168.1.9:4243" -api-enable-cors
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ To list available commands, either run ``docker`` with no parameters or execute
|
|||
|
||||
$ docker
|
||||
Usage: docker [OPTIONS] COMMAND [arg...]
|
||||
-H="127.0.0.1:4243": Host:port to bind/connect to
|
||||
-H=[tcp://127.0.0.1:4243]: tcp://host:port to bind/connect to or unix://path/to/socket to use
|
||||
|
||||
A self-sufficient runtime for linux containers.
|
||||
|
||||
|
|
|
@ -33,11 +33,20 @@ Running an interactive shell
|
|||
# allocate a tty, attach stdin and stdout
|
||||
docker run -i -t base /bin/bash
|
||||
|
||||
Bind Docker to another host/port
|
||||
--------------------------------
|
||||
Bind Docker to another host/port or a unix socket
|
||||
-------------------------------------------------
|
||||
|
||||
If you want Docker to listen to another port and bind to another ip
|
||||
use -host and -port on both deamon and client
|
||||
With -H it is possible to make the Docker daemon to listen on a specific ip and port. By default, it will listen on 127.0.0.1:4243 to allow only local connections but you can set it to 0.0.0.0:4243 or a specific host ip to give access to everybody.
|
||||
|
||||
Similarly, the Docker client can use -H to connect to a custom port.
|
||||
|
||||
-H accepts host and port assignment in the following format: tcp://[host][:port] or unix://path
|
||||
For example:
|
||||
|
||||
* tcp://host -> tcp connection on host:4243
|
||||
* tcp://host:port -> tcp connection on host:port
|
||||
* tcp://:port -> tcp connection on 127.0.0.1:port
|
||||
* unix://path/to/socket -> unix socket located at path/to/socket
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
|
@ -46,6 +55,17 @@ use -host and -port on both deamon and client
|
|||
# Download a base image
|
||||
docker -H :5555 pull base
|
||||
|
||||
You can use multiple -H, for example, if you want to listen
|
||||
on both tcp and a unix socket
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Run docker in daemon mode
|
||||
sudo <path to>/docker -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock
|
||||
# Download a base image
|
||||
docker pull base
|
||||
# OR
|
||||
docker -H unix:///var/run/docker.sock pull base
|
||||
|
||||
Starting a long-running worker process
|
||||
--------------------------------------
|
||||
|
|
|
@ -19,19 +19,14 @@ run add-apt-repository "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc)
|
|||
run add-apt-repository -y ppa:dotcloud/docker-golang/ubuntu
|
||||
run apt-get update
|
||||
# Packages required to checkout, build and upload docker
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q curl
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q s3cmd curl
|
||||
run curl -s -o /go.tar.gz https://go.googlecode.com/files/go1.1.1.linux-amd64.tar.gz
|
||||
run tar -C /usr/local -xzf /go.tar.gz
|
||||
run echo "export PATH=/usr/local/go/bin:$PATH" > /.bashrc
|
||||
run echo "export PATH=/usr/local/go/bin:$PATH" > /.bash_profile
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q build-essential
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q git build-essential
|
||||
# Packages required to build an ubuntu package
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang-stable
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q debhelper
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q autotools-dev
|
||||
run apt-get install -y -q devscripts
|
||||
run DEBIAN_FRONTEND=noninteractive apt-get install -y -q golang-stable debhelper autotools-dev devscripts
|
||||
# Copy dockerbuilder files into the container
|
||||
add . /src
|
||||
run cp /src/dockerbuilder /usr/local/bin/ && chmod +x /usr/local/bin/dockerbuilder
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
#
|
||||
# Dependencies: debhelper autotools-dev devscripts golang-stable
|
||||
# Notes:
|
||||
# Use 'make ubuntu' to create the ubuntu package
|
||||
# GPG_KEY environment variable needs to contain a GPG private key for package to be signed
|
||||
# and uploaded to docker PPA.
|
||||
# If GPG_KEY is not defined, make ubuntu will create docker package and exit with
|
||||
# status code 2
|
||||
# Use 'make ubuntu' to create the ubuntu package and push it to stating PPA by
|
||||
# default. To push to production, set PUBLISH_PPA=1 before doing 'make ubuntu'
|
||||
# GPG_KEY environment variable needs to contain a GPG private key for package
|
||||
# to be signed and uploaded to docker PPA. If GPG_KEY is not defined,
|
||||
# make ubuntu will create docker package and exit with status code 2
|
||||
|
||||
PKG_NAME=lxc-docker
|
||||
GITHUB_PATH=github.com/dotcloud/docker
|
||||
|
@ -52,9 +52,11 @@ ubuntu:
|
|||
if /usr/bin/test "$${GPG_KEY}" == ""; then exit 2; fi
|
||||
mkdir ${BUILD_SRC}
|
||||
# Import gpg signing key
|
||||
echo "$${GPG_KEY}" | gpg --allow-secret-key-import --import
|
||||
echo "$${GPG_KEY}" | gpg --allow-secret-key-import --import || true
|
||||
# Sign the package
|
||||
cd ${BUILD_SRC}; dpkg-source -x ${BUILD_SRC}/../${PKG_NAME}_${VERSION}-1.dsc
|
||||
cd ${BUILD_SRC}/${PKG_NAME}-${VERSION}; debuild -S -sa
|
||||
cd ${BUILD_SRC};dput ppa:dotcloud/lxc-docker ${PKG_NAME}_${VERSION}-1_source.changes
|
||||
# Upload to PPA
|
||||
if [ "${PUBLISH_PPA}" = "1" ]; then cd ${BUILD_SRC};dput ppa:dotcloud/lxc-docker ${PKG_NAME}_${VERSION}-1_source.changes; fi
|
||||
if [ "${PUBLISH_PPA}" != "1" ]; then cd ${BUILD_SRC};dput ppa:dotcloud/docker-staging ${PKG_NAME}_${VERSION}-1_source.changes; fi
|
||||
rm -rf ${BUILD_SRC}
|
||||
|
|
|
@ -7,10 +7,10 @@ import (
|
|||
"fmt"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/shin-/cookiejar"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -156,7 +156,7 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [
|
|||
}
|
||||
for _, host := range registries {
|
||||
endpoint := fmt.Sprintf("https://%s/v1/repositories/%s/tags", host, repository)
|
||||
req, err := http.NewRequest("GET", endpoint, nil)
|
||||
req, err := r.opaqueRequest("GET", endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ func (r *Registry) GetRemoteTags(registries []string, repository string, token [
|
|||
func (r *Registry) GetRepositoryData(remote string) (*RepositoryData, error) {
|
||||
repositoryTarget := auth.IndexServerAddress() + "/repositories/" + remote + "/images"
|
||||
|
||||
req, err := http.NewRequest("GET", repositoryTarget, nil)
|
||||
req, err := r.opaqueRequest("GET", repositoryTarget, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -309,6 +309,15 @@ func (r *Registry) PushImageLayerRegistry(imgId string, layer io.Reader, registr
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) opaqueRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
|
||||
req, err := http.NewRequest(method, urlStr, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.URL.Opaque = strings.Replace(urlStr, req.URL.Scheme+":", "", 1)
|
||||
return req, err
|
||||
}
|
||||
|
||||
// push a tag on the registry.
|
||||
// Remote has the format '<user>/<repo>
|
||||
func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token []string) error {
|
||||
|
@ -316,7 +325,7 @@ func (r *Registry) PushRegistryTag(remote, revision, tag, registry string, token
|
|||
revision = "\"" + revision + "\""
|
||||
registry = "https://" + registry + "/v1"
|
||||
|
||||
req, err := http.NewRequest("PUT", registry+"/repositories/"+remote+"/tags/"+tag, strings.NewReader(revision))
|
||||
req, err := r.opaqueRequest("PUT", registry+"/repositories/"+remote+"/tags/"+tag, strings.NewReader(revision))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -346,7 +355,7 @@ func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validat
|
|||
|
||||
utils.Debugf("Image list pushed to index:\n%s\n", imgListJSON)
|
||||
|
||||
req, err := http.NewRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJSON))
|
||||
req, err := r.opaqueRequest("PUT", auth.IndexServerAddress()+"/repositories/"+remote+"/"+suffix, bytes.NewReader(imgListJSON))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -366,7 +375,7 @@ func (r *Registry) PushImageJSONIndex(remote string, imgList []*ImgData, validat
|
|||
// Redirect if necessary
|
||||
for res.StatusCode >= 300 && res.StatusCode < 400 {
|
||||
utils.Debugf("Redirected to %s\n", res.Header.Get("Location"))
|
||||
req, err = http.NewRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJSON))
|
||||
req, err = r.opaqueRequest("PUT", res.Header.Get("Location"), bytes.NewReader(imgListJSON))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -444,11 +453,6 @@ func (r *Registry) SearchRepositories(term string) (*SearchResults, error) {
|
|||
return result, err
|
||||
}
|
||||
|
||||
func (r *Registry) ResetClient(authConfig *auth.AuthConfig) {
|
||||
r.authConfig = authConfig
|
||||
r.client.Jar = cookiejar.NewCookieJar()
|
||||
}
|
||||
|
||||
func (r *Registry) GetAuthConfig(withPasswd bool) *auth.AuthConfig {
|
||||
password := ""
|
||||
if withPasswd {
|
||||
|
@ -484,18 +488,18 @@ type Registry struct {
|
|||
authConfig *auth.AuthConfig
|
||||
}
|
||||
|
||||
func NewRegistry(root string, authConfig *auth.AuthConfig) *Registry {
|
||||
func NewRegistry(root string, authConfig *auth.AuthConfig) (r *Registry, err error) {
|
||||
httpTransport := &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
}
|
||||
|
||||
r := &Registry{
|
||||
r = &Registry{
|
||||
authConfig: authConfig,
|
||||
client: &http.Client{
|
||||
Transport: httpTransport,
|
||||
},
|
||||
}
|
||||
r.client.Jar = cookiejar.NewCookieJar()
|
||||
return r
|
||||
r.client.Jar, err = cookiejar.New(nil)
|
||||
return r, err
|
||||
}
|
||||
|
|
23
server.go
23
server.go
|
@ -55,8 +55,11 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
|
|||
}
|
||||
|
||||
func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
|
||||
|
||||
results, err := registry.NewRegistry(srv.runtime.root, nil).SearchRepositories(term)
|
||||
r, err := registry.NewRegistry(srv.runtime.root, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results, err := r.SearchRepositories(term)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -450,12 +453,15 @@ func (srv *Server) poolRemove(kind, key string) error {
|
|||
}
|
||||
|
||||
func (srv *Server) ImagePull(name, tag, endpoint string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig) error {
|
||||
r, err := registry.NewRegistry(srv.runtime.root, authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := srv.poolAdd("pull", name+":"+tag); err != nil {
|
||||
return err
|
||||
}
|
||||
defer srv.poolRemove("pull", name+":"+tag)
|
||||
|
||||
r := registry.NewRegistry(srv.runtime.root, authConfig)
|
||||
out = utils.NewWriteFlusher(out)
|
||||
if endpoint != "" {
|
||||
if err := srv.pullImage(r, out, name, endpoint, nil, sf); err != nil {
|
||||
|
@ -572,7 +578,7 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, name stri
|
|||
// FIXME: Continue on error?
|
||||
return err
|
||||
}
|
||||
out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/users/"+srvName+"/"+elem.Tag))
|
||||
out.Write(sf.FormatStatus("Pushing tags for rev [%s] on {%s}", elem.ID, ep+"/repositories/"+srvName+"/tags/"+elem.Tag))
|
||||
if err := r.PushRegistryTag(srvName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -654,8 +660,10 @@ func (srv *Server) ImagePush(name, endpoint string, out io.Writer, sf *utils.Str
|
|||
|
||||
out = utils.NewWriteFlusher(out)
|
||||
img, err := srv.runtime.graph.Get(name)
|
||||
r := registry.NewRegistry(srv.runtime.root, authConfig)
|
||||
|
||||
r, err2 := registry.NewRegistry(srv.runtime.root, authConfig)
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if err != nil {
|
||||
out.Write(sf.FormatStatus("The push refers to a repository [%s] (len: %d)", name, len(srv.runtime.repositories.Repositories[name])))
|
||||
// If it fails, try to get the repository
|
||||
|
@ -751,6 +759,9 @@ func (srv *Server) ContainerRestart(name string, t int) error {
|
|||
|
||||
func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
|
||||
if container := srv.runtime.Get(name); container != nil {
|
||||
if container.State.Running {
|
||||
return fmt.Errorf("Impossible to remove a running container, please stop it first")
|
||||
}
|
||||
volumes := make(map[string]struct{})
|
||||
// Store all the deleted containers volumes
|
||||
for _, volumeId := range container.Volumes {
|
||||
|
|
|
@ -9,16 +9,16 @@ const (
|
|||
getTermios = syscall.TIOCGETA
|
||||
setTermios = syscall.TIOCSETA
|
||||
|
||||
ECHO = 0x00000008
|
||||
ONLCR = 0x2
|
||||
ISTRIP = 0x20
|
||||
INLCR = 0x40
|
||||
ISIG = 0x80
|
||||
IGNCR = 0x80
|
||||
ICANON = 0x100
|
||||
ICRNL = 0x100
|
||||
IXOFF = 0x400
|
||||
IXON = 0x200
|
||||
ECHO = 0x00000008
|
||||
ONLCR = 0x2
|
||||
ISTRIP = 0x20
|
||||
INLCR = 0x40
|
||||
ISIG = 0x80
|
||||
IGNCR = 0x80
|
||||
ICANON = 0x100
|
||||
ICRNL = 0x100
|
||||
IXOFF = 0x400
|
||||
IXON = 0x200
|
||||
)
|
||||
|
||||
type Termios struct {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"index/suffixarray"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -652,3 +653,28 @@ func CheckLocalDns() bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ParseHost(host string, port int, addr string) string {
|
||||
if strings.HasPrefix(addr, "unix://") {
|
||||
return addr
|
||||
}
|
||||
if strings.HasPrefix(addr, "tcp://") {
|
||||
addr = strings.TrimPrefix(addr, "tcp://")
|
||||
}
|
||||
if strings.Contains(addr, ":") {
|
||||
hostParts := strings.Split(addr, ":")
|
||||
if len(hostParts) != 2 {
|
||||
log.Fatal("Invalid bind address format.")
|
||||
os.Exit(-1)
|
||||
}
|
||||
if hostParts[0] != "" {
|
||||
host = hostParts[0]
|
||||
}
|
||||
if p, err := strconv.Atoi(hostParts[1]); err == nil {
|
||||
port = p
|
||||
}
|
||||
} else {
|
||||
host = addr
|
||||
}
|
||||
return fmt.Sprintf("tcp://%s:%d", host, port)
|
||||
}
|
||||
|
|
|
@ -274,3 +274,21 @@ func TestHumanSize(t *testing.T) {
|
|||
t.Errorf("1024 -> expected 1.024 kB, got %s", size1024)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseHost(t *testing.T) {
|
||||
if addr := ParseHost("127.0.0.1", 4243, "0.0.0.0"); addr != "tcp://0.0.0.0:4243" {
|
||||
t.Errorf("0.0.0.0 -> expected tcp://0.0.0.0:4243, got %s", addr)
|
||||
}
|
||||
if addr := ParseHost("127.0.0.1", 4243, "0.0.0.1:5555"); addr != "tcp://0.0.0.1:5555" {
|
||||
t.Errorf("0.0.0.1:5555 -> expected tcp://0.0.0.1:5555, got %s", addr)
|
||||
}
|
||||
if addr := ParseHost("127.0.0.1", 4243, ":6666"); addr != "tcp://127.0.0.1:6666" {
|
||||
t.Errorf(":6666 -> expected tcp://127.0.0.1:6666, got %s", addr)
|
||||
}
|
||||
if addr := ParseHost("127.0.0.1", 4243, "tcp://:7777"); addr != "tcp://127.0.0.1:7777" {
|
||||
t.Errorf("tcp://:7777 -> expected tcp://127.0.0.1:7777, got %s", addr)
|
||||
}
|
||||
if addr := ParseHost("127.0.0.1", 4243, "unix:///var/run/docker.sock"); addr != "unix:///var/run/docker.sock" {
|
||||
t.Errorf("unix:///var/run/docker.sock -> expected unix:///var/run/docker.sock, got %s", addr)
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче