Compare commits

..

1 Commits

Author SHA1 Message Date
Jeremy Grosser bf369bd1b8 Debianize 2013-03-13 00:40:21 +00:00
149 changed files with 4152 additions and 20665 deletions
+2 -13
View File
@@ -1,17 +1,6 @@
.vagrant*
bin
.vagrant
docker/docker
dockerd/dockerd
.*.swp
a.out
*.orig
build_src
command-line-arguments.test
.flymake*
docker.test
auth/auth.test
.idea
.DS_Store
docs/_build
docs/_static
docs/_templates
.gopath/
-16
View File
@@ -1,16 +0,0 @@
# Generate AUTHORS: git log --all --format='%aN <%aE>' | sort -uf | grep -v vagrant-ubuntu-12
<charles.hooper@dotcloud.com> <chooper@plumata.com>
<daniel.mizyrycki@dotcloud.com> <daniel@dotcloud.com>
<daniel.mizyrycki@dotcloud.com> <mzdaniel@glidelink.net>
Guillaume J. Charmes <guillaume.charmes@dotcloud.com> creack <charmes.guillaume@gmail.com>
<guillaume.charmes@dotcloud.com> <guillaume@dotcloud.com>
<kencochrane@gmail.com> <KenCochrane@gmail.com>
<sridharr@activestate.com> <github@srid.name>
Thatcher Peskens <thatcher@dotcloud.com> dhrp <thatcher@dotcloud.com>
Thatcher Peskens <thatcher@dotcloud.com> dhrp <thatcher@gmx.net>
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com> jpetazzo <jerome.petazzoni@dotcloud.com>
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com> <jp@enix.org>
Joffrey F <joffrey@dotcloud.com>
<joffrey@dotcloud.com> <f.joffrey@gmail.com>
Tim Terhorst <mynamewastaken+git@gmail.com>
Andy Smith <github@anarkystic.com>
-37
View File
@@ -1,37 +0,0 @@
Andrea Luzzardi <aluzzardi@gmail.com>
Andy Rothfusz <github@metaliveblog.com>
Andy Smith <github@anarkystic.com>
Antony Messerli <amesserl@rackspace.com>
Brian McCallister <brianm@skife.org>
Caleb Spare <cespare@gmail.com>
Charles Hooper <charles.hooper@dotcloud.com>
Daniel Mizyrycki <daniel.mizyrycki@dotcloud.com>
Daniel Robinson <gottagetmac@gmail.com>
Dominik Honnef <dominik@honnef.co>
Don Spaulding <donspauldingii@gmail.com>
ezbercih <cem.ezberci@gmail.com>
Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
Guillaume J. Charmes <guillaume.charmes@dotcloud.com>
Hunter Blanks <hunter@twilio.com>
Jeff Lindsay <progrium@gmail.com>
Jeremy Grosser <jeremy@synack.me>
Joffrey F <joffrey@dotcloud.com>
John Costa <john.costa@gmail.com>
Jonathan Rudenberg <jonathan@titanous.com>
Julien Barbier <write0@gmail.com>
Jérôme Petazzoni <jerome.petazzoni@dotcloud.com>
Ken Cochrane <kencochrane@gmail.com>
Louis Opter <kalessin@kalessin.fr>
Mikhail Sobolev <mss@mawhrin.net>
Nelson Chen <crazysim@gmail.com>
Niall O'Higgins <niallo@unworkable.org>
Piotr Bogdan <ppbogdan@gmail.com>
Sam Alba <sam.alba@gmail.com>
Shawn Siefkas <shawn.siefkas@meredith.com>
Silas Sewell <silas@sewell.org>
Solomon Hykes <solomon@dotcloud.com>
Sridhar Ratnakumar <sridharr@activestate.com>
Thatcher Peskens <thatcher@dotcloud.com>
Tim Terhorst <mynamewastaken+git@gmail.com>
Troy Howard <thoward37@gmail.com>
Vivek Agarwal <me@vivek.im>
-93
View File
@@ -1,93 +0,0 @@
# Contributing to Docker
Want to hack on Docker? Awesome! There are instructions to get you
started on the website: http://docker.io/gettingstarted.html
They are probably not perfect, please let us know if anything feels
wrong or incomplete.
## Contribution guidelines
### Pull requests are always welcome
We are always thrilled to receive pull requests, and do our best to
process them as fast as possible. Not sure if that typo is worth a pull
request? Do it! We will appreciate it.
If your pull request is not accepted on the first try, don't be
discouraged! If there's a problem with the implementation, hopefully you
received feedback on what to improve.
We're trying very hard to keep Docker lean and focused. We don't want it
to do everything for everybody. This means that we might decide against
incorporating a new feature. However, there might be a way to implement
that feature *on top of* docker.
### Discuss your design on the mailing list
We recommend discussing your plans [on the mailing
list](https://groups.google.com/forum/?fromgroups#!forum/docker-club)
before starting to code - especially for more ambitious contributions.
This gives other contributors a chance to point you in the right
direction, give feedback on your design, and maybe point out if someone
else is working on the same thing.
### Create issues...
Any significant improvement should be documented as [a github
issue](https://github.com/dotcloud/docker/issues) before anybody
starts working on it.
### ...but check for existing issues first!
Please take a moment to check that an issue doesn't already exist
documenting your bug report or improvement proposal. If it does, it
never hurts to add a quick "+1" or "I have this problem too". This will
help prioritize the most common problems and requests.
### Conventions
Fork the repo and make changes on your fork in a feature branch:
- If it's a bugfix branch, name it XXX-something where XXX is the number of the
issue
- If it's a feature branch, create an enhancement issue to announce your
intentions, and name it XXX-something where XXX is the number of the issue.
Submit unit tests for your changes. Go has a great test framework built in; use
it! Take a look at existing tests for inspiration. Run the full test suite on
your branch before submitting a pull request.
Make sure you include relevant updates or additions to documentation when
creating or modifying features.
Write clean code. Universally formatted code promotes ease of writing, reading,
and maintenance. Always run `go fmt` before committing your changes. Most
editors have plugins that do this automatically, and there's also a git
pre-commit hook:
```
curl -o .git/hooks/pre-commit https://raw.github.com/edsrzf/gofmt-git-hook/master/fmt-check && chmod +x .git/hooks/pre-commit
```
Pull requests descriptions should be as clear as possible and include a
reference to all the issues that they address.
Code review comments may be added to your pull request. Discuss, then make the
suggested modifications and push additional commits to your feature branch. Be
sure to post a comment after pushing. The new commits will show up in the pull
request automatically, but the reviewers will not be notified unless you
comment.
Before the pull request is merged, make sure that you squash your commits into
logical units of work using `git rebase -i` and `git push -f`. After every
commit the test suite should be passing. Include documentation changes in the
same commit so that a revert would remove all traces of the feature or fix.
Commits that fix or close an issue should include a reference like `Closes #XXX`
or `Fixes #XXX`, which will automatically close the issue when merged.
Add your name to the AUTHORS file, but make sure the list is sorted and your
name and email address match your git configuration. The AUTHORS file is
regenerated occasionally from the git commit history, so a mismatch may result
in your changes being overwritten.
+16 -44
View File
@@ -1,51 +1,23 @@
DOCKER_PACKAGE := github.com/dotcloud/docker
GOPATH := $(PWD)/env
BUILDPATH := $(PWD)/build
BUILD_DIR := $(CURDIR)/.gopath
all: docker.o dockerd.o
GOPATH ?= $(BUILD_DIR)
export GOPATH
env:
mkdir -p ${BUILDPATH} ${GOPATH}/src/github.com/dotcloud/
ln -s $(PWD) ${GOPATH}/src/github.com/dotcloud/docker
GO_OPTIONS ?=
ifeq ($(VERBOSE), 1)
GO_OPTIONS += -v
endif
GIT_COMMIT = $(shell git rev-parse --short HEAD)
GIT_STATUS = $(shell test -n "`git status --porcelain`" && echo "+CHANGES")
BUILD_OPTIONS = -ldflags "-X main.GIT_COMMIT $(GIT_COMMIT)$(GIT_STATUS)"
SRC_DIR := $(GOPATH)/src
DOCKER_DIR := $(SRC_DIR)/$(DOCKER_PACKAGE)
DOCKER_MAIN := $(DOCKER_DIR)/docker
DOCKER_BIN_RELATIVE := bin/docker
DOCKER_BIN := $(CURDIR)/$(DOCKER_BIN_RELATIVE)
.PHONY: all clean test
all: $(DOCKER_BIN)
$(DOCKER_BIN): $(DOCKER_DIR)
@mkdir -p $(dir $@)
@(cd $(DOCKER_MAIN); go get $(GO_OPTIONS); go build $(GO_OPTIONS) $(BUILD_OPTIONS) -o $@)
@echo $(DOCKER_BIN_RELATIVE) is created.
$(DOCKER_DIR):
@mkdir -p $(dir $@)
@ln -sf $(CURDIR)/ $@
deps:
GOPATH=${GOPATH} go get github.com/kr/pty
GOPATH=${GOPATH} go get github.com/mattn/go-sqlite3
GOPATH=${GOPATH} go get github.com/shykes/gorp
clean:
@rm -rf $(dir $(DOCKER_BIN))
ifeq ($(GOPATH), $(BUILD_DIR))
@rm -rf $(BUILD_DIR)
else ifneq ($(DOCKER_DIR), $(realpath $(DOCKER_DIR)))
@rm -f $(DOCKER_DIR)
endif
go clean
rm -rf env build
test: all
@(cd $(DOCKER_DIR); sudo -E go test $(GO_OPTIONS))
docker.o: env deps
GOPATH=${GOPATH} go build -o ${BUILDPATH}/docker docker/docker.go
fmt:
@gofmt -s -l -w .
dockerd.o: env deps
GOPATH=${GOPATH} go build -o ${BUILDPATH}/dockerd dockerd/dockerd.go
+30 -181
View File
@@ -1,17 +1,17 @@
Docker: the Linux container runtime
===================================
Docker is a process manager with superpowers
============================================
Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
It encapsulates heterogeneous payloads in Standard Containers, and runs them on any server with strong guarantees of isolation and repeatability.
Docker is a great building block for automating distributed systems: large-scale web deployments, database clusters, continuous deployment systems, private PaaS, service-oriented architectures, etc.
Is is a great building block for automating distributed systems: large-scale web deployments, database clusters, continuous deployment systems, private PaaS, service-oriented architectures, etc.
![Docker L](docs/sources/static_files/lego_docker.jpg "Docker")
<img src="http://bricks.argz.com/bricksfiles/lego/07000/7823/012.jpg"/>
* *Heterogeneous payloads*: any combination of binaries, libraries, configuration files, scripts, virtualenvs, jars, gems, tarballs, you name it. No more juggling between domain-specific tools. Docker can deploy and run them all.
* *Any server*: docker can run on any x64 machine with a modern linux kernel - whether it's a laptop, a bare metal server or a VM. This makes it perfect for multi-cloud deployments.
* *Isolation*: docker isolates processes from each other and from the underlying host, using lightweight containers.
* *Isolation*: docker isolates processes from each other and from the underlying host, using lightweight containers.
* *Repeatability*: because containers are isolated in their own filesystem, they behave the same regardless of where, when, and alongside what they run.
@@ -25,13 +25,13 @@ Notable features
* Network isolation: each process container runs in its own network namespace, with a virtual interface and IP address of its own.
* Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremely fast, memory-cheap and disk-cheap.
* Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremeley fast, memory-cheap and disk-cheap.
* Logging: the standard streams (stdout/stderr/stdin) of each process container are collected and logged for real-time or batch retrieval.
* Logging: the standard streams (stdout/stderr/stdin) of each process container is collected and logged for real-time or batch retrieval.
* Change management: changes to a container's filesystem can be committed into a new image and re-used to create more containers. No templating or manual configuration required.
* Interactive shell: docker can allocate a pseudo-tty and attach to the standard input of any container, for example to run a throwaway interactive shell.
* Interactive shell: docker can allocate a pseudo-tty and attach to the standard input of any container, for example to run a throaway interactive shell.
@@ -50,64 +50,11 @@ Under the hood, Docker is built on the following components:
* [lxc](http://lxc.sourceforge.net/), a set of convenience scripts to simplify the creation of linux containers.
Install instructions
Setup instructions
==================
Building from source
--------------------
1. Make sure you have a [Go language](http://golang.org) compiler.
On a Debian/wheezy or Ubuntu 12.10 install the package:
```bash
$ sudo apt-get install golang-go
```
2. Execute ``make``
This command will install all necessary dependencies and build the
executable that you can find in ``bin/docker``
3. Should you like to see what's happening, run ``make`` with ``VERBOSE=1`` parameter:
```bash
$ make VERBOSE=1
```
Installing on Ubuntu 12.04 and 12.10
------------------------------------
1. Install dependencies:
```bash
sudo apt-get install lxc wget bsdtar curl
sudo apt-get install linux-image-extra-`uname -r`
```
The `linux-image-extra` package is needed on standard Ubuntu EC2 AMIs in order to install the aufs kernel module.
2. Install the latest docker binary:
```bash
wget http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz
tar -xf docker-master.tgz
```
3. Run your first container!
```bash
cd docker-master
sudo ./docker pull base
sudo ./docker run -i -t base /bin/bash
```
Consider adding docker to your `PATH` for simplicity.
Installing on other Linux distributions
---------------------------------------
Requirements
------------
Right now, the officially supported distributions are:
@@ -116,132 +63,34 @@ Right now, the officially supported distributions are:
Docker probably works on other distributions featuring a recent kernel, the AUFS patch, and up-to-date lxc. However this has not been tested.
Some streamlined (but possibly outdated) installation paths' are available from the website: http://docker.io/documentation/
Installation
---------------
Usage examples
==============
1. Set up your host of choice on a physical / virtual machine
2. Assume root identity on your newly installed environment (`sudo -s`)
3. Type the following commands:
Running an interactive shell
----------------------------
apt-get update
apt-get install lxc wget bsdtar curl
```bash
# Download a base image
docker pull base
4. Download the latest docker binaries: `wget http://docker.io.s3.amazonaws.com/builds/$(uname -s)/$(uname -m)/docker-master.tgz` ([Or get the Linux/x86_64 binaries here](http://docker.io.s3.amazonaws.com/builds/Linux/x86_64/docker-master.tgz) )
5. Extract the contents of the tar file `tar -xf docker-master.tar.gz`
6. Launch the docker daemon in the background `./dockerd &`
7. Download a base image `./docker pull base`
8. Run your first container! `./docker run -i -a -t base /bin/bash`
9. Start exploring `./docker --help`
# Run an interactive shell in the base image,
# allocate a tty, attach stdin and stdout
docker run -i -t base /bin/bash
```
Starting a long-running worker process
--------------------------------------
```bash
# Run docker in daemon mode
(docker -d || echo "Docker daemon already running") &
# Start a very useful long-running process
JOB=$(docker run -d base /bin/sh -c "while true; do echo Hello world; sleep 1; done")
# Collect the output of the job so far
docker logs $JOB
# Kill the job
docker kill $JOB
```
Listing all running containers
------------------------------
```bash
docker ps
```
Share your own image!
---------------------
```bash
docker pull base
CONTAINER=$(docker run -d base apt-get install -y curl)
docker commit -m "Installed curl" $CONTAINER $USER/betterbase
docker push $USER/betterbase
```
Expose a service on a TCP port
------------------------------
```bash
# Expose port 4444 of this container, and tell netcat to listen on it
JOB=$(docker run -d -p 4444 base /bin/nc -l -p 4444)
# Which public port is NATed to my container?
PORT=$(docker port $JOB 4444)
# Connect to the public port via the host's public address
echo hello world | nc $(hostname) $PORT
# Verify that the network connection worked
echo "Daemon received: $(docker logs $JOB)"
```
Contributing to Docker
======================
Want to hack on Docker? Awesome! There are instructions to get you started on the website: http://docs.docker.io/en/latest/contributing/contributing/
They are probably not perfect, please let us know if anything feels wrong or incomplete.
Note
----
We also keep the documentation in this repository. The website documentation is generated using sphinx using these sources.
Please find it under docs/sources/ and read more about it https://github.com/dotcloud/docker/master/docs/README.md
Please feel free to fix / update the documentation and send us pull requests. More tutorials are also welcome.
Setting up a dev environment
----------------------------
Instructions that have been verified to work on Ubuntu 12.10,
```bash
sudo apt-get -y install lxc wget bsdtar curl golang git
export GOPATH=~/go/
export PATH=$GOPATH/bin:$PATH
mkdir -p $GOPATH/src/github.com/dotcloud
cd $GOPATH/src/github.com/dotcloud
git clone git@github.com:dotcloud/docker.git
cd docker
go get -v github.com/dotcloud/docker/...
go install -v github.com/dotcloud/docker/...
```
Then run the docker daemon,
```bash
sudo $GOPATH/bin/docker -d
```
Run the `go install` command (above) to recompile docker.
Consider adding docker and dockerd to your `PATH` for simplicity.
What is a Standard Container?
=============================
-----------------------------
Docker defines a unit of software delivery called a Standard Container. The goal of a Standard Container is to encapsulate a software component and all its dependencies in
a format that is self-describing and portable, so that any compliant runtime can run it without extra dependencies, regardless of the underlying machine and the contents of the container.
a format that is self-describing and portable, so that any compliant runtime can run it without extra dependency, regardless of the underlying machine and the contents of the container.
The spec for Standard Containers is currently a work in progress, but it is very straightforward. It mostly defines 1) an image format, 2) a set of standard operations, and 3) an execution environment.
The spec for Standard Containers is currently work in progress, but it is very straightforward. It mostly defines 1) an image format, 2) a set of standard operations, and 3) an execution environment.
A great analogy for this is the shipping container. Just like Standard Containers are a fundamental unit of software delivery, shipping containers (http://bricks.argz.com/ins/7823-1/12) are a fundamental unit of physical delivery.
@@ -252,7 +101,7 @@ Just like shipping containers, Standard Containers define a set of STANDARD OPER
### 2. CONTENT-AGNOSTIC
Just like shipping containers, Standard Containers are CONTENT-AGNOSTIC: all standard operations have the same effect regardless of the contents. A shipping container will be stacked in exactly the same way whether it contains Vietnamese powder coffee or spare Maserati parts. Similarly, Standard Containers are started or uploaded in the same way whether they contain a postgres database, a php application with its dependencies and application server, or Java build artifacts.
Just like shipping containers, Standard Containers are CONTENT-AGNOSTIC: all standard operations have the same effect regardless of the contents. A shipping container will be stacked in exactly the same way whether it contains Vietnamese powder coffe or spare Maserati parts. Similarly, Standard Containers are started or uploaded in the same way whether they contain a postgres database, a php application with its dependencies and application server, or Java build artifacts.
### 3. INFRASTRUCTURE-AGNOSTIC
Vendored
+86 -43
View File
@@ -1,57 +1,100 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
def v10(config)
Vagrant::Config.run do |config|
# All Vagrant configuration is done here. The most common configuration
# options are documented and commented below. For a complete reference,
# please see the online documentation at vagrantup.com.
# Every Vagrant virtual environment requires a box to build off of.
config.vm.box = "quantal64_3.5.0-25"
# The url from where the 'config.vm.box' box will be fetched if it
# doesn't already exist on the user's system.
config.vm.box_url = "http://get.docker.io/vbox/ubuntu/12.10/quantal64_3.5.0-25.box"
config.vm.share_folder "v-data", "/opt/go/src/github.com/dotcloud/docker", File.dirname(__FILE__)
# Boot with a GUI so you can see the screen. (Default is headless)
# config.vm.boot_mode = :gui
# Ensure puppet is installed on the instance
config.vm.provision :shell, :inline => "apt-get -qq update; apt-get install -y puppet"
# Assign this VM to a host-only network IP, allowing you to access it
# via the IP. Host-only networks can talk to the host machine as well as
# any other machines on the same network, but cannot be accessed (through this
# network interface) by any external networks.
# config.vm.network :hostonly, "192.168.33.10"
# Assign this VM to a bridged network, allowing you to connect directly to a
# network using the host's network device. This makes the VM appear as another
# physical device on your network.
# config.vm.network :bridged
# Forward a port from the guest to the host, which allows for outside
# computers to access the VM, whereas host only networking does not.
# config.vm.forward_port 80, 8080
# Share an additional folder to the guest VM. The first argument is
# an identifier, the second is the path on the guest to mount the
# folder, and the third is the path on the host to the actual folder.
# config.vm.share_folder "v-data", "/vagrant_data", "../data"
# Enable provisioning with Puppet stand alone. Puppet manifests
# are contained in a directory path relative to this Vagrantfile.
# You will need to create the manifests directory and a manifest in
# the file quantal64.pp in the manifests_path directory.
#
# An example Puppet manifest to provision the message of the day:
#
# # group { "puppet":
# # ensure => "present",
# # }
# #
# # File { owner => 0, group => 0, mode => 0644 }
# #
# # file { '/etc/motd':
# # content => "Welcome to your Vagrant-built virtual machine!
# # Managed by Puppet.\n"
# # }
#
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "puppet/manifests"
puppet.manifest_file = "quantal64.pp"
puppet.module_path = "puppet/modules"
end
end
Vagrant::VERSION < "1.1.0" and Vagrant::Config.run do |config|
v10(config)
end
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("1") do |config|
v10(config)
end
Vagrant::VERSION >= "1.1.0" and Vagrant.configure("2") do |config|
config.vm.provider :aws do |aws|
config.vm.box = "dummy"
config.vm.box_url = "https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box"
aws.access_key_id = ENV["AWS_ACCESS_KEY_ID"]
aws.secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
aws.keypair_name = ENV["AWS_KEYPAIR_NAME"]
aws.ssh_private_key_path = ENV["AWS_SSH_PRIVKEY"]
aws.region = "us-east-1"
aws.ami = "ami-ae9806c7"
aws.ssh_username = "ubuntu"
aws.instance_type = "t1.micro"
end
config.vm.provider :rackspace do |rs|
config.vm.box = "dummy"
config.vm.box_url = "https://github.com/mitchellh/vagrant-rackspace/raw/master/dummy.box"
config.ssh.private_key_path = ENV["RS_PRIVATE_KEY"]
rs.username = ENV["RS_USERNAME"]
rs.api_key = ENV["RS_API_KEY"]
rs.public_key_path = ENV["RS_PUBLIC_KEY"]
rs.flavor = /512MB/
rs.image = /Ubuntu/
end
config.vm.provider :virtualbox do |vb|
config.vm.box = "quantal64_3.5.0-25"
config.vm.box_url = "http://get.docker.io/vbox/ubuntu/12.10/quantal64_3.5.0-25.box"
end
# Enable provisioning with chef solo, specifying a cookbooks path, roles
# path, and data_bags path (all relative to this Vagrantfile), and adding
# some recipes and/or roles.
#
# config.vm.provision :chef_solo do |chef|
# chef.cookbooks_path = "../my-recipes/cookbooks"
# chef.roles_path = "../my-recipes/roles"
# chef.data_bags_path = "../my-recipes/data_bags"
# chef.add_recipe "mysql"
# chef.add_role "web"
#
# # You may also specify custom JSON attributes:
# chef.json = { :mysql_password => "foo" }
# end
# Enable provisioning with chef server, specifying the chef server URL,
# and the path to the validation key (relative to this Vagrantfile).
#
# The Opscode Platform uses HTTPS. Substitute your organization for
# ORGNAME in the URL and validation key.
#
# If you have your own Chef Server, use the appropriate URL, which may be
# HTTP instead of HTTPS depending on your configuration. Also change the
# validation key to validation.pem.
#
# config.vm.provision :chef_client do |chef|
# chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
# chef.validation_key_path = "ORGNAME-validator.pem"
# end
#
# If you're using the Opscode platform, your validator client is
# ORGNAME-validator, replacing ORGNAME with your organization name.
#
# IF you have your own Chef Server, the default validation client name is
# chef-validator, unless you changed the configuration.
#
# chef.validation_client_name = "ORGNAME-validator"
end
-168
View File
@@ -1,168 +0,0 @@
package auth
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
)
// Where we store the config file
const CONFIGFILE = ".dockercfg"
// the registry server we want to login against
const REGISTRY_SERVER = "https://registry.docker.io"
type AuthConfig struct {
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
rootPath string `json:-`
}
func NewAuthConfig(username, password, email, rootPath string) *AuthConfig {
return &AuthConfig{
Username: username,
Password: password,
Email: email,
rootPath: rootPath,
}
}
// create a base64 encoded auth string to store in config
func EncodeAuth(authConfig *AuthConfig) string {
authStr := authConfig.Username + ":" + authConfig.Password
msg := []byte(authStr)
encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
base64.StdEncoding.Encode(encoded, msg)
return string(encoded)
}
// decode the auth string
func DecodeAuth(authStr string) (*AuthConfig, error) {
decLen := base64.StdEncoding.DecodedLen(len(authStr))
decoded := make([]byte, decLen)
authByte := []byte(authStr)
n, err := base64.StdEncoding.Decode(decoded, authByte)
if err != nil {
return nil, err
}
if n > decLen {
return nil, fmt.Errorf("Something went wrong decoding auth config")
}
arr := strings.Split(string(decoded), ":")
if len(arr) != 2 {
return nil, fmt.Errorf("Invalid auth configuration file")
}
password := strings.Trim(arr[1], "\x00")
return &AuthConfig{Username: arr[0], Password: password}, nil
}
// load up the auth config information and return values
// FIXME: use the internal golang config parser
func LoadConfig(rootPath string) (*AuthConfig, error) {
confFile := path.Join(rootPath, CONFIGFILE)
if _, err := os.Stat(confFile); err != nil {
return &AuthConfig{}, fmt.Errorf("The Auth config file is missing")
}
b, err := ioutil.ReadFile(confFile)
if err != nil {
return nil, err
}
arr := strings.Split(string(b), "\n")
origAuth := strings.Split(arr[0], " = ")
origEmail := strings.Split(arr[1], " = ")
authConfig, err := DecodeAuth(origAuth[1])
if err != nil {
return nil, err
}
authConfig.Email = origEmail[1]
authConfig.rootPath = rootPath
return authConfig, nil
}
// save the auth config
func saveConfig(rootPath, authStr string, email string) error {
lines := "auth = " + authStr + "\n" + "email = " + email + "\n"
b := []byte(lines)
err := ioutil.WriteFile(path.Join(rootPath, CONFIGFILE), b, 0600)
if err != nil {
return err
}
return nil
}
// try to register/login to the registry server
func Login(authConfig *AuthConfig) (string, error) {
storeConfig := false
reqStatusCode := 0
var status string
var errMsg string
var reqBody []byte
jsonBody, err := json.Marshal(authConfig)
if err != nil {
errMsg = fmt.Sprintf("Config Error: %s", err)
return "", errors.New(errMsg)
}
// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
b := strings.NewReader(string(jsonBody))
req1, err := http.Post(REGISTRY_SERVER+"/v1/users", "application/json; charset=utf-8", b)
if err != nil {
errMsg = fmt.Sprintf("Server Error: %s", err)
return "", errors.New(errMsg)
}
reqStatusCode = req1.StatusCode
defer req1.Body.Close()
reqBody, err = ioutil.ReadAll(req1.Body)
if err != nil {
errMsg = fmt.Sprintf("Server Error: [%#v] %s", reqStatusCode, err)
return "", errors.New(errMsg)
}
if reqStatusCode == 201 {
status = "Account Created\n"
storeConfig = true
} else if reqStatusCode == 400 {
// FIXME: This should be 'exists', not 'exist'. Need to change on the server first.
if string(reqBody) == "Username or email already exist" {
client := &http.Client{}
req, err := http.NewRequest("GET", REGISTRY_SERVER+"/v1/users", nil)
req.SetBasicAuth(authConfig.Username, authConfig.Password)
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode == 200 {
status = "Login Succeeded\n"
storeConfig = true
} else {
status = fmt.Sprintf("Login: %s", body)
return "", errors.New(status)
}
} else {
status = fmt.Sprintf("Registration: %s", reqBody)
return "", errors.New(status)
}
} else {
status = fmt.Sprintf("[%s] : %s", reqStatusCode, reqBody)
return "", errors.New(status)
}
if storeConfig {
authStr := EncodeAuth(authConfig)
saveConfig(authConfig.rootPath, authStr, authConfig.Email)
}
return status, nil
}
-23
View File
@@ -1,23 +0,0 @@
package auth
import (
"testing"
)
func TestEncodeAuth(t *testing.T) {
newAuthConfig := &AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
authStr := EncodeAuth(newAuthConfig)
decAuthConfig, err := DecodeAuth(authStr)
if err != nil {
t.Fatal(err)
}
if newAuthConfig.Username != decAuthConfig.Username {
t.Fatal("Encode Username doesn't match decoded Username")
}
if newAuthConfig.Password != decAuthConfig.Password {
t.Fatal("Encode Password doesn't match decoded Password")
}
if authStr != "a2VuOnRlc3Q=" {
t.Fatal("AuthString encoding isn't correct.")
}
}
+126
View File
@@ -0,0 +1,126 @@
package client
import (
"../future"
"../rcli"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
)
// Run docker in "simple mode": run a single command and return.
func SimpleMode(args []string) error {
var oldState *State
var err error
if IsTerminal(0) && os.Getenv("NORAW") == "" {
oldState, err = MakeRaw(0)
if err != nil {
return err
}
defer Restore(0, oldState)
}
// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
// CloseWrite(), which we need to cleanly signal that stdin is closed without
// closing the connection.
// See http://code.google.com/p/go/issues/detail?id=3345
conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...)
if err != nil {
return err
}
receive_stdout := future.Go(func() error {
_, err := io.Copy(os.Stdout, conn)
return err
})
send_stdin := future.Go(func() error {
_, err := io.Copy(conn, os.Stdin)
if err := conn.CloseWrite(); err != nil {
log.Printf("Couldn't send EOF: " + err.Error())
}
return err
})
if err := <-receive_stdout; err != nil {
return err
}
if oldState != nil {
Restore(0, oldState)
}
if !IsTerminal(0) {
if err := <-send_stdin; err != nil {
return err
}
}
return nil
}
// Run docker in "interactive mode": run a bash-compatible shell capable of running docker commands.
func InteractiveMode(scripts ...string) error {
// Determine path of current docker binary
dockerPath, err := exec.LookPath(os.Args[0])
if err != nil {
return err
}
dockerPath, err = filepath.Abs(dockerPath)
if err != nil {
return err
}
// Create a temp directory
tmp, err := ioutil.TempDir("", "docker-shell")
if err != nil {
return err
}
defer os.RemoveAll(tmp)
// For each command, create an alias in temp directory
// FIXME: generate this list dynamically with introspection of some sort
// It might make sense to merge docker and dockerd to keep that introspection
// within a single binary.
for _, cmd := range []string{
"help",
"run",
"ps",
"pull",
"put",
"rm",
"kill",
"wait",
"stop",
"start",
"restart",
"logs",
"diff",
"commit",
"attach",
"info",
"tar",
"web",
"images",
"docker",
} {
if err := os.Symlink(dockerPath, path.Join(tmp, cmd)); err != nil {
return err
}
}
// Run $SHELL with PATH set to temp directory
rcfile, err := ioutil.TempFile("", "docker-shell-rc")
if err != nil {
return err
}
defer os.Remove(rcfile.Name())
io.WriteString(rcfile, "enable -n help\n")
os.Setenv("PATH", tmp+":"+os.Getenv("PATH"))
os.Setenv("PS1", "\\h docker> ")
shell := exec.Command("/bin/bash", append([]string{"--rcfile", rcfile.Name()}, scripts...)...)
shell.Stdin = os.Stdin
shell.Stdout = os.Stdout
shell.Stderr = os.Stderr
if err := shell.Run(); err != nil {
return err
}
return nil
}
+22 -1
View File
@@ -1,4 +1,4 @@
package term
package client
import (
"syscall"
@@ -114,6 +114,27 @@ func IsTerminal(fd int) bool {
return err == 0
}
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
var oldState State
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF
newState.Iflag |= ICRNL
newState.Oflag |= ONLCR
newState.Lflag &^= ECHO | ICANON | ISIG
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
return nil, err
}
return &oldState, nil
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
+8
View File
@@ -0,0 +1,8 @@
package client
import "syscall"
const (
getTermios = syscall.TIOCGETA
setTermios = syscall.TIOCSETA
)
+8
View File
@@ -0,0 +1,8 @@
package client
import "syscall"
const (
getTermios = syscall.TCGETS
setTermios = syscall.TCSETS
)
-944
View File
@@ -1,944 +0,0 @@
package docker
import (
"bytes"
"encoding/json"
"fmt"
"github.com/dotcloud/docker/auth"
"github.com/dotcloud/docker/rcli"
"io"
"log"
"net/http"
"net/url"
"runtime"
"strconv"
"strings"
"text/tabwriter"
"time"
"unicode"
)
const VERSION = "0.1.2"
var GIT_COMMIT string
func (srv *Server) Name() string {
return "docker"
}
// FIXME: Stop violating DRY by repeating usage here and in Subcmd declarations
func (srv *Server) Help() string {
help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
for _, cmd := range [][]string{
{"attach", "Attach to a running container"},
{"commit", "Create a new image from a container's changes"},
{"diff", "Inspect changes on a container's filesystem"},
{"export", "Stream the contents of a container as a tar archive"},
{"history", "Show the history of an image"},
{"images", "List images"},
{"import", "Create a new filesystem image from the contents of a tarball"},
{"info", "Display system-wide information"},
{"inspect", "Return low-level information on a container"},
{"kill", "Kill a running container"},
{"login", "Register or Login to the docker registry server"},
{"logs", "Fetch the logs of a container"},
{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
{"ps", "List containers"},
{"pull", "Pull an image or a repository from the docker registry server"},
{"push", "Push an image or a repository to the docker registry server"},
{"restart", "Restart a running container"},
{"rm", "Remove a container"},
{"rmi", "Remove an image"},
{"run", "Run a command in a new container"},
{"start", "Start a stopped container"},
{"stop", "Stop a running container"},
{"tag", "Tag an image into a repository"},
{"version", "Show the docker version information"},
{"wait", "Block until a container stops, then print its exit code"},
} {
help += fmt.Sprintf(" %-10.10s%s\n", cmd[0], cmd[1])
}
return help
}
// 'docker login': login / register a user to registry service.
func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
// Read a line on raw terminal with support for simple backspace
// sequences and echo.
//
// This function is necessary because the login command must be done in a
// raw terminal for two reasons:
// - we have to read a password (without echoing it);
// - the rcli "protocol" only supports cannonical and raw modes and you
// can't tune it once the command as been started.
var readStringOnRawTerminal = func(stdin io.Reader, stdout io.Writer, echo bool) string {
char := make([]byte, 1)
buffer := make([]byte, 64)
var i = 0
for i < len(buffer) {
n, err := stdin.Read(char)
if n > 0 {
if char[0] == '\r' || char[0] == '\n' {
stdout.Write([]byte{'\n'})
break
} else if char[0] == 127 || char[0] == '\b' {
if i > 0 {
if echo {
stdout.Write([]byte{'\b', ' ', '\b'})
}
i--
}
} else if !unicode.IsSpace(rune(char[0])) &&
!unicode.IsControl(rune(char[0])) {
if echo {
stdout.Write(char)
}
buffer[i] = char[0]
i++
}
}
if err != nil {
if err != io.EOF {
fmt.Fprint(stdout, "Read error: %v\n", err)
}
break
}
}
return string(buffer[:i])
}
var readAndEchoString = func(stdin io.Reader, stdout io.Writer) string {
return readStringOnRawTerminal(stdin, stdout, true)
}
var readString = func(stdin io.Reader, stdout io.Writer) string {
return readStringOnRawTerminal(stdin, stdout, false)
}
cmd := rcli.Subcmd(stdout, "login", "", "Register or Login to the docker registry server")
if err := cmd.Parse(args); err != nil {
return nil
}
var username string
var password string
var email string
fmt.Fprint(stdout, "Username (", srv.runtime.authConfig.Username, "): ")
username = readAndEchoString(stdin, stdout)
if username == "" {
username = srv.runtime.authConfig.Username
}
if username != srv.runtime.authConfig.Username {
fmt.Fprint(stdout, "Password: ")
password = readString(stdin, stdout)
if password == "" {
return fmt.Errorf("Error : Password Required")
}
fmt.Fprint(stdout, "Email (", srv.runtime.authConfig.Email, "): ")
email = readAndEchoString(stdin, stdout)
if email == "" {
email = srv.runtime.authConfig.Email
}
} else {
password = srv.runtime.authConfig.Password
email = srv.runtime.authConfig.Email
}
newAuthConfig := auth.NewAuthConfig(username, password, email, srv.runtime.root)
status, err := auth.Login(newAuthConfig)
if err != nil {
fmt.Fprintln(stdout, "Error:", err)
} else {
srv.runtime.authConfig = newAuthConfig
}
if status != "" {
fmt.Fprint(stdout, status)
}
return nil
}
// 'docker wait': block until a container stops
func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
for _, name := range cmd.Args() {
if container := srv.runtime.Get(name); container != nil {
fmt.Fprintln(stdout, container.Wait())
} else {
return fmt.Errorf("No such container: %s", name)
}
}
return nil
}
// 'docker version': show version information
func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
fmt.Fprintf(stdout, "Version:%s\n", VERSION)
fmt.Fprintf(stdout, "Git Commit:%s\n", GIT_COMMIT)
return nil
}
// 'docker info': display system-wide information.
func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
images, _ := srv.runtime.graph.All()
var imgcount int
if images == nil {
imgcount = 0
} else {
imgcount = len(images)
}
cmd := rcli.Subcmd(stdout, "info", "", "Display system-wide information.")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() > 0 {
cmd.Usage()
return nil
}
fmt.Fprintf(stdout, "containers: %d\nversion: %s\nimages: %d\n",
len(srv.runtime.List()),
VERSION,
imgcount)
if !rcli.DEBUG_FLAG {
return nil
}
fmt.Fprintln(stdout, "debug mode enabled")
fmt.Fprintf(stdout, "fds: %d\ngoroutines: %d\n", getTotalUsedFds(), runtime.NumGoroutine())
return nil
}
func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
for _, name := range cmd.Args() {
if container := srv.runtime.Get(name); container != nil {
if err := container.Stop(); err != nil {
return err
}
fmt.Fprintln(stdout, container.ShortId())
} else {
return fmt.Errorf("No such container: %s", name)
}
}
return nil
}
func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
for _, name := range cmd.Args() {
if container := srv.runtime.Get(name); container != nil {
if err := container.Restart(); err != nil {
return err
}
fmt.Fprintln(stdout, container.ShortId())
} else {
return fmt.Errorf("No such container: %s", name)
}
}
return nil
}
func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
for _, name := range cmd.Args() {
if container := srv.runtime.Get(name); container != nil {
if err := container.Start(); err != nil {
return err
}
fmt.Fprintln(stdout, container.ShortId())
} else {
return fmt.Errorf("No such container: %s", name)
}
}
return nil
}
func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
name := cmd.Arg(0)
var obj interface{}
if container := srv.runtime.Get(name); container != nil {
obj = container
} else if image, err := srv.runtime.repositories.LookupImage(name); err == nil && image != nil {
obj = image
} else {
// No output means the object does not exist
// (easier to script since stdout and stderr are not differentiated atm)
return nil
}
data, err := json.Marshal(obj)
if err != nil {
return err
}
indented := new(bytes.Buffer)
if err = json.Indent(indented, data, "", " "); err != nil {
return err
}
if _, err := io.Copy(stdout, indented); err != nil {
return err
}
stdout.Write([]byte{'\n'})
return nil
}
func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 2 {
cmd.Usage()
return nil
}
name := cmd.Arg(0)
privatePort := cmd.Arg(1)
if container := srv.runtime.Get(name); container == nil {
return fmt.Errorf("No such container: %s", name)
} else {
if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
return fmt.Errorf("No private port '%s' allocated on %s", privatePort, name)
} else {
fmt.Fprintln(stdout, frontend)
}
}
return nil
}
// 'docker rmi NAME' removes all images with the name NAME
func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) (err error) {
cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
cmd.Usage()
return nil
}
for _, name := range cmd.Args() {
if err := srv.runtime.graph.Delete(name); err != nil {
return err
}
}
return nil
}
func (srv *Server) CmdHistory(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "history", "[OPTIONS] IMAGE", "Show the history of an image")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
image, err := srv.runtime.repositories.LookupImage(cmd.Arg(0))
if err != nil {
return err
}
w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
defer w.Flush()
fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
return image.WalkHistory(func(img *Image) error {
fmt.Fprintf(w, "%s\t%s\t%s\n",
srv.runtime.repositories.ImageName(img.ShortId()),
HumanDuration(time.Now().Sub(img.Created))+" ago",
strings.Join(img.ContainerConfig.Cmd, " "),
)
return nil
})
}
func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container")
if err := cmd.Parse(args); err != nil {
return nil
}
for _, name := range cmd.Args() {
container := srv.runtime.Get(name)
if container == nil {
return fmt.Errorf("No such container: %s", name)
}
if err := srv.runtime.Destroy(container); err != nil {
fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error())
}
}
return nil
}
// 'docker kill NAME' kills a running container
func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container")
if err := cmd.Parse(args); err != nil {
return nil
}
for _, name := range cmd.Args() {
container := srv.runtime.Get(name)
if container == nil {
return fmt.Errorf("No such container: %s", name)
}
if err := container.Kill(); err != nil {
fmt.Fprintln(stdout, "Error killing container "+name+": "+err.Error())
}
}
return nil
}
func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball")
var archive io.Reader
var resp *http.Response
if err := cmd.Parse(args); err != nil {
return nil
}
src := cmd.Arg(0)
if src == "" {
return fmt.Errorf("Not enough arguments")
} else if src == "-" {
archive = stdin
} else {
u, err := url.Parse(src)
if err != nil {
return err
}
if u.Scheme == "" {
u.Scheme = "http"
u.Host = src
u.Path = ""
}
fmt.Fprintln(stdout, "Downloading from", u)
// Download with curl (pretty progress bar)
// If curl is not available, fallback to http.Get()
resp, err = Download(u.String(), stdout)
if err != nil {
return err
}
archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout)
}
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src)
if err != nil {
return err
}
// Optionally register the image at REPO/TAG
if repository := cmd.Arg(1); repository != "" {
tag := cmd.Arg(2) // Repository will handle an empty tag properly
if err := srv.runtime.repositories.Set(repository, tag, img.Id, true); err != nil {
return err
}
}
fmt.Fprintln(stdout, img.ShortId())
return nil
}
func (srv *Server) CmdPush(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "push", "NAME", "Push an image or a repository to the registry")
if err := cmd.Parse(args); err != nil {
return nil
}
local := cmd.Arg(0)
if local == "" {
cmd.Usage()
return nil
}
// If the login failed, abort
if srv.runtime.authConfig == nil || srv.runtime.authConfig.Username == "" {
if err := srv.CmdLogin(stdin, stdout, args...); err != nil {
return err
}
if srv.runtime.authConfig == nil || srv.runtime.authConfig.Username == "" {
return fmt.Errorf("Please login prior to push. ('docker login')")
}
}
var remote string
tmp := strings.SplitN(local, "/", 2)
if len(tmp) == 1 {
return fmt.Errorf(
"Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)",
srv.runtime.authConfig.Username, local)
} else {
remote = local
}
Debugf("Pushing [%s] to [%s]\n", local, remote)
// Try to get the image
// FIXME: Handle lookup
// FIXME: Also push the tags in case of ./docker push myrepo:mytag
// img, err := srv.runtime.LookupImage(cmd.Arg(0))
img, err := srv.runtime.graph.Get(local)
if err != nil {
Debugf("The push refers to a repository [%s] (len: %d)\n", local, len(srv.runtime.repositories.Repositories[local]))
// If it fails, try to get the repository
if localRepo, exists := srv.runtime.repositories.Repositories[local]; exists {
if err := srv.runtime.graph.PushRepository(stdout, remote, localRepo, srv.runtime.authConfig); err != nil {
return err
}
return nil
} else {
return err
}
return nil
}
err = srv.runtime.graph.PushImage(stdout, img, srv.runtime.authConfig)
if err != nil {
return err
}
return nil
}
func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "pull", "NAME", "Pull an image or a repository from the registry")
if err := cmd.Parse(args); err != nil {
return nil
}
remote := cmd.Arg(0)
if remote == "" {
cmd.Usage()
return nil
}
// FIXME: CmdPull should be a wrapper around Runtime.Pull()
if srv.runtime.graph.LookupRemoteImage(remote, srv.runtime.authConfig) {
if err := srv.runtime.graph.PullImage(stdout, remote, srv.runtime.authConfig); err != nil {
return err
}
return nil
}
// FIXME: Allow pull repo:tag
if err := srv.runtime.graph.PullRepository(stdout, remote, "", srv.runtime.repositories, srv.runtime.authConfig); err != nil {
return err
}
return nil
}
func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
//limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
quiet := cmd.Bool("q", false, "only show numeric IDs")
flAll := cmd.Bool("a", false, "show all images")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() > 1 {
cmd.Usage()
return nil
}
var nameFilter string
if cmd.NArg() == 1 {
nameFilter = cmd.Arg(0)
}
w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
if !*quiet {
fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tPARENT")
}
var allImages map[string]*Image
var err error
if *flAll {
allImages, err = srv.runtime.graph.Map()
} else {
allImages, err = srv.runtime.graph.Heads()
}
if err != nil {
return err
}
for name, repository := range srv.runtime.repositories.Repositories {
if nameFilter != "" && name != nameFilter {
continue
}
for tag, id := range repository {
image, err := srv.runtime.graph.Get(id)
if err != nil {
log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
continue
}
delete(allImages, id)
if !*quiet {
for idx, field := range []string{
/* REPOSITORY */ name,
/* TAG */ tag,
/* ID */ TruncateId(id),
/* CREATED */ HumanDuration(time.Now().Sub(image.Created)) + " ago",
/* PARENT */ srv.runtime.repositories.ImageName(image.Parent),
} {
if idx == 0 {
w.Write([]byte(field))
} else {
w.Write([]byte("\t" + field))
}
}
w.Write([]byte{'\n'})
} else {
stdout.Write([]byte(image.ShortId() + "\n"))
}
}
}
// Display images which aren't part of a
if nameFilter == "" {
for id, image := range allImages {
if !*quiet {
for idx, field := range []string{
/* REPOSITORY */ "<none>",
/* TAG */ "<none>",
/* ID */ TruncateId(id),
/* CREATED */ HumanDuration(time.Now().Sub(image.Created)) + " ago",
/* PARENT */ srv.runtime.repositories.ImageName(image.Parent),
} {
if idx == 0 {
w.Write([]byte(field))
} else {
w.Write([]byte("\t" + field))
}
}
w.Write([]byte{'\n'})
} else {
stdout.Write([]byte(image.ShortId() + "\n"))
}
}
}
if !*quiet {
w.Flush()
}
return nil
}
func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout,
"ps", "[OPTIONS]", "List containers")
quiet := cmd.Bool("q", false, "Only display numeric IDs")
flAll := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
flFull := cmd.Bool("notrunc", false, "Don't truncate output")
if err := cmd.Parse(args); err != nil {
return nil
}
w := tabwriter.NewWriter(stdout, 12, 1, 3, ' ', 0)
if !*quiet {
fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT")
}
for _, container := range srv.runtime.List() {
if !container.State.Running && !*flAll {
continue
}
if !*quiet {
command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
if !*flFull {
command = Trunc(command, 20)
}
for idx, field := range []string{
/* ID */ container.ShortId(),
/* IMAGE */ srv.runtime.repositories.ImageName(container.Image),
/* COMMAND */ command,
/* CREATED */ HumanDuration(time.Now().Sub(container.Created)) + " ago",
/* STATUS */ container.State.String(),
/* COMMENT */ "",
} {
if idx == 0 {
w.Write([]byte(field))
} else {
w.Write([]byte("\t" + field))
}
}
w.Write([]byte{'\n'})
} else {
stdout.Write([]byte(container.ShortId() + "\n"))
}
}
if !*quiet {
w.Flush()
}
return nil
}
func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout,
"commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]",
"Create a new image from a container's changes")
flComment := cmd.String("m", "", "Commit message")
if err := cmd.Parse(args); err != nil {
return nil
}
containerName, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2)
if containerName == "" {
cmd.Usage()
return nil
}
img, err := srv.runtime.Commit(containerName, repository, tag, *flComment)
if err != nil {
return err
}
fmt.Fprintln(stdout, img.ShortId())
return nil
}
func (srv *Server) CmdExport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout,
"export", "CONTAINER",
"Export the contents of a filesystem as a tar archive")
if err := cmd.Parse(args); err != nil {
return nil
}
name := cmd.Arg(0)
if container := srv.runtime.Get(name); container != nil {
data, err := container.Export()
if err != nil {
return err
}
// Stream the entire contents of the container (basically a volatile snapshot)
if _, err := io.Copy(stdout, data); err != nil {
return err
}
return nil
}
return fmt.Errorf("No such container: %s", name)
}
func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout,
"diff", "CONTAINER [OPTIONS]",
"Inspect changes on a container's filesystem")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 1 {
return fmt.Errorf("Not enough arguments")
}
if container := srv.runtime.Get(cmd.Arg(0)); container == nil {
return fmt.Errorf("No such container")
} else {
changes, err := container.Changes()
if err != nil {
return err
}
for _, change := range changes {
fmt.Fprintln(stdout, change.String())
}
}
return nil
}
func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
name := cmd.Arg(0)
if container := srv.runtime.Get(name); container != nil {
logStdout, err := container.ReadLog("stdout")
if err != nil {
return err
}
logStderr, err := container.ReadLog("stderr")
if err != nil {
return err
}
// FIXME: Interpolate stdout and stderr instead of concatenating them
// FIXME: Differentiate stdout and stderr in the remote protocol
if _, err := io.Copy(stdout, logStdout); err != nil {
return err
}
if _, err := io.Copy(stdout, logStderr); err != nil {
return err
}
return nil
}
return fmt.Errorf("No such container: %s", cmd.Arg(0))
}
func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "attach", "CONTAINER", "Attach to a running container")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 1 {
cmd.Usage()
return nil
}
name := cmd.Arg(0)
container := srv.runtime.Get(name)
if container == nil {
return fmt.Errorf("No such container: %s", name)
}
return <-container.Attach(stdin, stdout, stdout)
}
// Ports type - Used to parse multiple -p flags
type ports []int
func (p *ports) String() string {
return fmt.Sprint(*p)
}
func (p *ports) Set(value string) error {
port, err := strconv.Atoi(value)
if err != nil {
return fmt.Errorf("Invalid port: %v", value)
}
*p = append(*p, port)
return nil
}
// ListOpts type
type ListOpts []string
func (opts *ListOpts) String() string {
return fmt.Sprint(*opts)
}
func (opts *ListOpts) Set(value string) error {
*opts = append(*opts, value)
return nil
}
// AttachOpts stores arguments to 'docker run -a', eg. which streams to attach to
type AttachOpts map[string]bool
func NewAttachOpts() *AttachOpts {
opts := make(map[string]bool)
return (*AttachOpts)(&opts)
}
func (opts *AttachOpts) String() string {
return fmt.Sprint(*opts)
}
func (opts *AttachOpts) Set(val string) error {
if val != "stdin" && val != "stdout" && val != "stderr" {
return fmt.Errorf("Unsupported stream name: %s", val)
}
(*opts)[val] = true
return nil
}
func (opts *AttachOpts) Get(val string) bool {
if res, exists := (*opts)[val]; exists {
return res
}
return false
}
func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
force := cmd.Bool("f", false, "Force")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() < 2 {
cmd.Usage()
return nil
}
return srv.runtime.repositories.Set(cmd.Arg(1), cmd.Arg(2), cmd.Arg(0), *force)
}
func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
config, err := ParseRun(args, stdout)
if err != nil {
return err
}
if config.Image == "" {
fmt.Fprintln(stdout, "Error: Image not specified")
return fmt.Errorf("Image not specified")
}
if len(config.Cmd) == 0 {
fmt.Fprintln(stdout, "Error: Command not specified")
return fmt.Errorf("Command not specified")
}
// Create new container
container, err := srv.runtime.Create(config)
if err != nil {
// If container not found, try to pull it
if srv.runtime.graph.IsNotExist(err) {
fmt.Fprintf(stdout, "Image %s not found, trying to pull it from registry.\n", config.Image)
if err = srv.CmdPull(stdin, stdout, config.Image); err != nil {
return err
}
if container, err = srv.runtime.Create(config); err != nil {
return err
}
} else {
return err
}
}
var (
cStdin io.Reader
cStdout, cStderr io.Writer
)
if config.AttachStdin {
cStdin = stdin
}
if config.AttachStdout {
cStdout = stdout
}
if config.AttachStderr {
cStderr = stdout // FIXME: rcli can't differentiate stdout from stderr
}
attachErr := container.Attach(cStdin, cStdout, cStderr)
Debugf("Starting\n")
if err := container.Start(); err != nil {
return err
}
if cStdout == nil && cStderr == nil {
fmt.Fprintln(stdout, container.ShortId())
}
Debugf("Waiting for attach to return\n")
return <-attachErr
}
func NewServer() (*Server, error) {
if runtime.GOARCH != "amd64" {
log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
}
runtime, err := NewRuntime()
if err != nil {
return nil, err
}
srv := &Server{
runtime: runtime,
}
return srv, nil
}
type Server struct {
runtime *Runtime
}
-250
View File
@@ -1,250 +0,0 @@
package docker
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"strings"
"testing"
"time"
)
func closeWrap(args ...io.Closer) error {
e := false
ret := fmt.Errorf("Error closing elements")
for _, c := range args {
if err := c.Close(); err != nil {
e = true
ret = fmt.Errorf("%s\n%s", ret, err)
}
}
if e {
return ret
}
return nil
}
func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
c := make(chan bool)
// Make sure we are not too long
go func() {
time.Sleep(d)
c <- true
}()
go func() {
f()
c <- false
}()
if <-c {
t.Fatal(msg)
}
}
func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error {
for i := 0; i < count; i++ {
if _, err := w.Write([]byte(input)); err != nil {
return err
}
o, err := bufio.NewReader(r).ReadString('\n')
if err != nil {
return err
}
if strings.Trim(o, " \r\n") != output {
return fmt.Errorf("Unexpected output. Expected [%s], received [%s]", output, o)
}
}
return nil
}
// TestRunHostname checks that 'docker run -h' correctly sets a custom hostname
func TestRunHostname(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime}
var stdin, stdout bytes.Buffer
setTimeout(t, "CmdRun timed out", 2*time.Second, func() {
if err := srv.CmdRun(ioutil.NopCloser(&stdin), &nopWriteCloser{&stdout}, "-h", "foobar", GetTestImage(runtime).Id, "hostname"); err != nil {
t.Fatal(err)
}
})
if output := string(stdout.Bytes()); output != "foobar\n" {
t.Fatalf("'hostname' should display '%s', not '%s'", "foobar\n", output)
}
}
// Expected behaviour: the process dies when the client disconnects
func TestRunDisconnect(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime}
stdin, stdinPipe := io.Pipe()
stdout, stdoutPipe := io.Pipe()
c1 := make(chan struct{})
go func() {
// We're simulating a disconnect so the return value doesn't matter. What matters is the
// fact that CmdRun returns.
srv.CmdRun(stdin, stdoutPipe, "-i", GetTestImage(runtime).Id, "/bin/cat")
close(c1)
}()
setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() {
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
t.Fatal(err)
}
})
// Close pipes (simulate disconnect)
if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
t.Fatal(err)
}
// as the pipes are close, we expect the process to die,
// therefore CmdRun to unblock. Wait for CmdRun
setTimeout(t, "Waiting for CmdRun timed out", 2*time.Second, func() {
<-c1
})
// Client disconnect after run -i should cause stdin to be closed, which should
// cause /bin/cat to exit.
setTimeout(t, "Waiting for /bin/cat to exit timed out", 2*time.Second, func() {
container := runtime.List()[0]
container.Wait()
if container.State.Running {
t.Fatalf("/bin/cat is still running after closing stdin")
}
})
}
// TestAttachStdin checks attaching to stdin without stdout and stderr.
// 'docker run -i -a stdin' should sends the client's stdin to the command,
// then detach from it and print the container id.
func TestAttachStdin(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime}
stdinR, stdinW := io.Pipe()
var stdout bytes.Buffer
ch := make(chan struct{})
go func() {
srv.CmdRun(stdinR, &stdout, "-i", "-a", "stdin", GetTestImage(runtime).Id, "sh", "-c", "echo hello; cat")
close(ch)
}()
// Send input to the command, close stdin, wait for CmdRun to return
setTimeout(t, "Read/Write timed out", 2*time.Second, func() {
if _, err := stdinW.Write([]byte("hi there\n")); err != nil {
t.Fatal(err)
}
stdinW.Close()
<-ch
})
// Check output
cmdOutput := string(stdout.Bytes())
container := runtime.List()[0]
if cmdOutput != container.ShortId()+"\n" {
t.Fatalf("Wrong output: should be '%s', not '%s'\n", container.ShortId()+"\n", cmdOutput)
}
setTimeout(t, "Waiting for command to exit timed out", 2*time.Second, func() {
container.Wait()
})
// Check logs
if cmdLogs, err := container.ReadLog("stdout"); err != nil {
t.Fatal(err)
} else {
if output, err := ioutil.ReadAll(cmdLogs); err != nil {
t.Fatal(err)
} else {
expectedLog := "hello\nhi there\n"
if string(output) != expectedLog {
t.Fatalf("Unexpected logs: should be '%s', not '%s'\n", expectedLog, output)
}
}
}
}
// Expected behaviour, the process stays alive when the client disconnects
func TestAttachDisconnect(t *testing.T) {
runtime, err := newTestRuntime()
if err != nil {
t.Fatal(err)
}
defer nuke(runtime)
srv := &Server{runtime: runtime}
container, err := runtime.Create(
&Config{
Image: GetTestImage(runtime).Id,
Memory: 33554432,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
)
if err != nil {
t.Fatal(err)
}
defer runtime.Destroy(container)
// Start the process
if err := container.Start(); err != nil {
t.Fatal(err)
}
stdin, stdinPipe := io.Pipe()
stdout, stdoutPipe := io.Pipe()
// Attach to it
c1 := make(chan struct{})
go func() {
// We're simulating a disconnect so the return value doesn't matter. What matters is the
// fact that CmdAttach returns.
srv.CmdAttach(stdin, stdoutPipe, container.Id)
close(c1)
}()
setTimeout(t, "First read/write assertion timed out", 2*time.Second, func() {
if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
t.Fatal(err)
}
})
// Close pipes (client disconnects)
if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
t.Fatal(err)
}
// Wait for attach to finish, the client disconnected, therefore, Attach finished his job
setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() {
<-c1
})
// We closed stdin, expect /bin/cat to still be running
// Wait a little bit to make sure container.monitor() did his thing
err = container.WaitTimeout(500 * time.Millisecond)
if err == nil || !container.State.Running {
t.Fatalf("/bin/cat is not running after closing stdin")
}
// Try to avoid the timeoout in destroy. Best effort, don't check error
cStdin, _ := container.StdinPipe()
cStdin.Close()
}
+224 -394
View File
@@ -1,9 +1,9 @@
package docker
import (
"./fs"
"encoding/json"
"fmt"
"github.com/dotcloud/docker/rcli"
"errors"
"github.com/kr/pty"
"io"
"io/ioutil"
@@ -16,119 +16,50 @@ import (
"time"
)
type Container struct {
root string
var sysInitPath string
Id string
func init() {
sysInitPath = SelfPath()
}
type Container struct {
Id string
Root string
Created time.Time
Path string
Args []string
Config *Config
State State
Image string
Config *Config
Mountpoint *fs.Mountpoint
State *State
Image string
network *NetworkInterface
networkManager *NetworkManager
NetworkSettings *NetworkSettings
SysInitPath string
cmd *exec.Cmd
stdout *writeBroadcaster
stderr *writeBroadcaster
stdin io.ReadCloser
stdinPipe io.WriteCloser
SysInitPath string
lxcConfigPath string
cmd *exec.Cmd
stdout *writeBroadcaster
stderr *writeBroadcaster
stdin io.ReadCloser
stdinPipe io.WriteCloser
ptyStdinMaster io.Closer
ptyStdoutMaster io.Closer
ptyStderrMaster io.Closer
runtime *Runtime
stdoutLog *os.File
stderrLog *os.File
}
type Config struct {
Hostname string
User string
Memory int64 // Memory limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
AttachStdin bool
AttachStdout bool
AttachStderr bool
Ports []int
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
OpenStdin bool // Open stdin
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
Env []string
Cmd []string
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
}
func ParseRun(args []string, stdout io.Writer) (*Config, error) {
cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
if len(args) > 0 && args[0] != "--help" {
cmd.SetOutput(ioutil.Discard)
}
flHostname := cmd.String("h", "", "Container host name")
flUser := cmd.String("u", "", "Username or UID")
flDetach := cmd.Bool("d", false, "Detached mode: leave the container running in the background")
flAttach := NewAttachOpts()
cmd.Var(flAttach, "a", "Attach to stdin, stdout or stderr.")
flStdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
flTty := cmd.Bool("t", false, "Allocate a pseudo-tty")
flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)")
var flPorts ports
cmd.Var(&flPorts, "p", "Map a network port to the container")
var flEnv ListOpts
cmd.Var(&flEnv, "e", "Set environment variables")
if err := cmd.Parse(args); err != nil {
return nil, err
}
if *flDetach && len(*flAttach) > 0 {
return nil, fmt.Errorf("Conflicting options: -a and -d")
}
// If neither -d or -a are set, attach to everything by default
if len(*flAttach) == 0 && !*flDetach {
if !*flDetach {
flAttach.Set("stdout")
flAttach.Set("stderr")
if *flStdin {
flAttach.Set("stdin")
}
}
}
parsedArgs := cmd.Args()
runCmd := []string{}
image := ""
if len(parsedArgs) >= 1 {
image = cmd.Arg(0)
}
if len(parsedArgs) > 1 {
runCmd = parsedArgs[1:]
}
config := &Config{
Hostname: *flHostname,
Ports: flPorts,
User: *flUser,
Tty: *flTty,
OpenStdin: *flStdin,
Memory: *flMemory,
AttachStdin: flAttach.Get("stdin"),
AttachStdout: flAttach.Get("stdout"),
AttachStderr: flAttach.Get("stderr"),
Env: flEnv,
Cmd: runCmd,
Image: image,
}
// When allocating stdin in attached mode, close stdin at client disconnect
if config.OpenStdin && config.AttachStdin {
config.StdinOnce = true
}
return config, nil
Hostname string
User string
Memory int64 // Memory limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
Ports []int
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
OpenStdin bool // Open stdin
}
type NetworkSettings struct {
@@ -138,6 +69,106 @@ type NetworkSettings struct {
PortMapping map[string]string
}
func createContainer(id string, root string, command string, args []string, image *fs.Image, config *Config, netManager *NetworkManager) (*Container, error) {
mountpoint, err := image.Mountpoint(path.Join(root, "rootfs"), path.Join(root, "rw"))
if err != nil {
return nil, err
}
container := &Container{
Id: id,
Root: root,
Created: time.Now(),
Path: command,
Args: args,
Config: config,
Image: image.Id,
Mountpoint: mountpoint,
State: newState(),
networkManager: netManager,
NetworkSettings: &NetworkSettings{},
SysInitPath: sysInitPath,
lxcConfigPath: path.Join(root, "config.lxc"),
stdout: newWriteBroadcaster(),
stderr: newWriteBroadcaster(),
}
if err := os.Mkdir(root, 0700); err != nil {
return nil, err
}
// Setup logging of stdout and stderr to disk
if stdoutLog, err := os.OpenFile(path.Join(container.Root, id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
return nil, err
} else {
container.stdoutLog = stdoutLog
}
if stderrLog, err := os.OpenFile(path.Join(container.Root, id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
return nil, err
} else {
container.stderrLog = stderrLog
}
if container.Config.OpenStdin {
container.stdin, container.stdinPipe = io.Pipe()
} else {
container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
}
container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
if err := container.save(); err != nil {
return nil, err
}
return container, nil
}
func loadContainer(store *fs.Store, containerPath string, netManager *NetworkManager) (*Container, error) {
data, err := ioutil.ReadFile(path.Join(containerPath, "config.json"))
if err != nil {
return nil, err
}
mountpoint, err := store.FetchMountpoint(
path.Join(containerPath, "rootfs"),
path.Join(containerPath, "rw"),
)
if err != nil {
return nil, err
} else if mountpoint == nil {
return nil, errors.New("Couldn't load container: unregistered mountpoint.")
}
container := &Container{
stdout: newWriteBroadcaster(),
stderr: newWriteBroadcaster(),
lxcConfigPath: path.Join(containerPath, "config.lxc"),
networkManager: netManager,
NetworkSettings: &NetworkSettings{},
Mountpoint: mountpoint,
}
// Load container settings
if err := json.Unmarshal(data, container); err != nil {
return nil, err
}
// Setup logging of stdout and stderr to disk
if stdoutLog, err := os.OpenFile(path.Join(container.Root, container.Id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
return nil, err
} else {
container.stdoutLog = stdoutLog
}
if stderrLog, err := os.OpenFile(path.Join(container.Root, container.Id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
return nil, err
} else {
container.stderrLog = stderrLog
}
container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
if container.Config.OpenStdin {
container.stdin, container.stdinPipe = io.Pipe()
} else {
container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
}
container.State = newState()
return container, nil
}
func (container *Container) Cmd() *exec.Cmd {
return container.cmd
}
@@ -146,32 +177,64 @@ func (container *Container) When() time.Time {
return container.Created
}
func (container *Container) FromDisk() error {
data, err := ioutil.ReadFile(container.jsonPath())
func (container *Container) loadUserData() (map[string]string, error) {
jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
if err != nil {
if os.IsNotExist(err) {
return make(map[string]string), nil
}
return nil, err
}
data := make(map[string]string)
if err := json.Unmarshal(jsonData, &data); err != nil {
return nil, err
}
return data, nil
}
func (container *Container) saveUserData(data map[string]string) error {
jsonData, err := json.Marshal(data)
if err != nil {
return err
}
// Load container settings
if err := json.Unmarshal(data, container); err != nil {
return err
}
return nil
return ioutil.WriteFile(path.Join(container.Root, "userdata.json"), jsonData, 0700)
}
func (container *Container) ToDisk() (err error) {
func (container *Container) SetUserData(key, value string) error {
data, err := container.loadUserData()
if err != nil {
return err
}
data[key] = value
return container.saveUserData(data)
}
func (container *Container) GetUserData(key string) string {
data, err := container.loadUserData()
if err != nil {
return ""
}
if value, exists := data[key]; exists {
return value
}
return ""
}
func (container *Container) save() (err error) {
data, err := json.Marshal(container)
if err != nil {
return
}
return ioutil.WriteFile(container.jsonPath(), data, 0666)
return ioutil.WriteFile(path.Join(container.Root, "config.json"), data, 0666)
}
func (container *Container) generateLXCConfig() error {
fo, err := os.Create(container.lxcConfigPath())
fo, err := os.Create(container.lxcConfigPath)
if err != nil {
return err
}
defer fo.Close()
if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
return err
}
@@ -179,62 +242,52 @@ func (container *Container) generateLXCConfig() error {
}
func (container *Container) startPty() error {
stdoutMaster, stdoutSlave, err := pty.Open()
stdout_master, stdout_slave, err := pty.Open()
if err != nil {
return err
}
container.ptyStdoutMaster = stdoutMaster
container.cmd.Stdout = stdoutSlave
container.cmd.Stdout = stdout_slave
stderrMaster, stderrSlave, err := pty.Open()
stderr_master, stderr_slave, err := pty.Open()
if err != nil {
return err
}
container.ptyStderrMaster = stderrMaster
container.cmd.Stderr = stderrSlave
container.cmd.Stderr = stderr_slave
// Copy the PTYs to our broadcasters
go func() {
defer container.stdout.CloseWriters()
Debugf("[startPty] Begin of stdout pipe")
io.Copy(container.stdout, stdoutMaster)
Debugf("[startPty] End of stdout pipe")
defer container.stdout.Close()
io.Copy(container.stdout, stdout_master)
}()
go func() {
defer container.stderr.CloseWriters()
Debugf("[startPty] Begin of stderr pipe")
io.Copy(container.stderr, stderrMaster)
Debugf("[startPty] End of stderr pipe")
defer container.stderr.Close()
io.Copy(container.stderr, stderr_master)
}()
// stdin
var stdinSlave io.ReadCloser
var stdin_slave io.ReadCloser
if container.Config.OpenStdin {
var stdinMaster io.WriteCloser
stdinMaster, stdinSlave, err = pty.Open()
stdin_master, stdin_slave, err := pty.Open()
if err != nil {
return err
}
container.ptyStdinMaster = stdinMaster
container.cmd.Stdin = stdinSlave
container.cmd.Stdin = stdin_slave
// FIXME: The following appears to be broken.
// "cannot set terminal process group (-1): Inappropriate ioctl for device"
// container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
go func() {
defer container.stdin.Close()
Debugf("[startPty] Begin of stdin pipe")
io.Copy(stdinMaster, container.stdin)
Debugf("[startPty] End of stdin pipe")
io.Copy(stdin_master, container.stdin)
}()
}
if err := container.cmd.Start(); err != nil {
return err
}
stdoutSlave.Close()
stderrSlave.Close()
if stdinSlave != nil {
stdinSlave.Close()
stdout_slave.Close()
stderr_slave.Close()
if stdin_slave != nil {
stdin_slave.Close()
}
return nil
}
@@ -249,99 +302,14 @@ func (container *Container) start() error {
}
go func() {
defer stdin.Close()
Debugf("Begin of stdin pipe [start]")
io.Copy(stdin, container.stdin)
Debugf("End of stdin pipe [start]")
}()
}
return container.cmd.Start()
}
func (container *Container) Attach(stdin io.Reader, stdout io.Writer, stderr io.Writer) chan error {
var cStdout io.ReadCloser
var cStderr io.ReadCloser
var nJobs int
errors := make(chan error, 3)
if stdin != nil && container.Config.OpenStdin {
nJobs += 1
if cStdin, err := container.StdinPipe(); err != nil {
errors <- err
} else {
go func() {
Debugf("[start] attach stdin\n")
defer Debugf("[end] attach stdin\n")
if container.Config.StdinOnce {
defer cStdin.Close()
}
_, err := io.Copy(cStdin, stdin)
if err != nil {
Debugf("[error] attach stdout: %s\n", err)
}
errors <- err
}()
}
}
if stdout != nil {
nJobs += 1
if p, err := container.StdoutPipe(); err != nil {
errors <- err
} else {
cStdout = p
go func() {
Debugf("[start] attach stdout\n")
defer Debugf("[end] attach stdout\n")
_, err := io.Copy(stdout, cStdout)
if err != nil {
Debugf("[error] attach stdout: %s\n", err)
}
errors <- err
}()
}
}
if stderr != nil {
nJobs += 1
if p, err := container.StderrPipe(); err != nil {
errors <- err
} else {
cStderr = p
go func() {
Debugf("[start] attach stderr\n")
defer Debugf("[end] attach stderr\n")
_, err := io.Copy(stderr, cStderr)
if err != nil {
Debugf("[error] attach stderr: %s\n", err)
}
errors <- err
}()
}
}
return Go(func() error {
if cStdout != nil {
defer cStdout.Close()
}
if cStderr != nil {
defer cStderr.Close()
}
// FIXME: how do clean up the stdin goroutine without the unwanted side effect
// of closing the passed stdin? Add an intermediary io.Pipe?
for i := 0; i < nJobs; i += 1 {
Debugf("Waiting for job %d/%d\n", i+1, nJobs)
if err := <-errors; err != nil {
Debugf("Job %d returned error %s. Aborting all jobs\n", i+1, err)
return err
}
Debugf("Job %d completed successfully\n", i+1)
}
Debugf("All jobs completed successfully\n")
return nil
})
}
func (container *Container) Start() error {
if container.State.Running {
return fmt.Errorf("The container %s is already running.", container.Id)
}
if err := container.EnsureMounted(); err != nil {
if err := container.Mountpoint.EnsureMounted(); err != nil {
return err
}
if err := container.allocateNetwork(); err != nil {
@@ -352,7 +320,7 @@ func (container *Container) Start() error {
}
params := []string{
"-n", container.Id,
"-f", container.lxcConfigPath(),
"-f", container.lxcConfigPath,
"--",
"/sbin/init",
}
@@ -369,31 +337,10 @@ func (container *Container) Start() error {
params = append(params, "--", container.Path)
params = append(params, container.Args...)
container.cmd = exec.Command("lxc-start", params...)
// Setup environment
container.cmd.Env = append(
[]string{
"HOME=/",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
},
container.Config.Env...,
)
// Setup logging of stdout and stderr to disk
if err := container.runtime.LogToDisk(container.stdout, container.logPath("stdout")); err != nil {
return err
}
if err := container.runtime.LogToDisk(container.stderr, container.logPath("stderr")); err != nil {
return err
}
container.cmd = exec.Command("/usr/bin/lxc-start", params...)
var err error
if container.Config.Tty {
container.cmd.Env = append(
[]string{"TERM=xterm"},
container.cmd.Env...,
)
err = container.startPty()
} else {
err = container.start()
@@ -401,10 +348,8 @@ func (container *Container) Start() error {
if err != nil {
return err
}
// FIXME: save state on disk *first*, then converge
// this way disk state is used as a journal, eg. we can restore after crash etc.
container.State.setRunning(container.cmd.Process.Pid)
container.ToDisk()
container.save()
go container.monitor()
return nil
}
@@ -444,14 +389,30 @@ func (container *Container) StdoutPipe() (io.ReadCloser, error) {
return newBufReader(reader), nil
}
func (container *Container) StdoutLog() io.Reader {
r, err := os.Open(container.stdoutLog.Name())
if err != nil {
return nil
}
return r
}
func (container *Container) StderrPipe() (io.ReadCloser, error) {
reader, writer := io.Pipe()
container.stderr.AddWriter(writer)
return newBufReader(reader), nil
}
func (container *Container) StderrLog() io.Reader {
r, err := os.Open(container.stderrLog.Name())
if err != nil {
return nil
}
return r
}
func (container *Container) allocateNetwork() error {
iface, err := container.runtime.networkManager.Allocate()
iface, err := container.networkManager.Allocate()
if err != nil {
return err
}
@@ -471,54 +432,25 @@ func (container *Container) allocateNetwork() error {
return nil
}
func (container *Container) releaseNetwork() {
container.network.Release()
func (container *Container) releaseNetwork() error {
err := container.network.Release()
container.network = nil
container.NetworkSettings = &NetworkSettings{}
return err
}
func (container *Container) monitor() {
// Wait for the program to exit
Debugf("Waiting for process")
if err := container.cmd.Wait(); err != nil {
// Discard the error as any signals or non 0 returns will generate an error
Debugf("%s: Process: %s", container.Id, err)
}
Debugf("Process finished")
container.cmd.Wait()
exitCode := container.cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
// Cleanup
container.releaseNetwork()
if container.Config.OpenStdin {
if err := container.stdin.Close(); err != nil {
Debugf("%s: Error close stdin: %s", container.Id, err)
}
if err := container.releaseNetwork(); err != nil {
log.Printf("%v: Failed to release network: %v", container.Id, err)
}
if err := container.stdout.CloseWriters(); err != nil {
Debugf("%s: Error close stdout: %s", container.Id, err)
}
if err := container.stderr.CloseWriters(); err != nil {
Debugf("%s: Error close stderr: %s", container.Id, err)
}
if container.ptyStdinMaster != nil {
if err := container.ptyStdinMaster.Close(); err != nil {
Debugf("%s: Error close pty stdin master: %s", container.Id, err)
}
}
if container.ptyStdoutMaster != nil {
if err := container.ptyStdoutMaster.Close(); err != nil {
Debugf("%s: Error close pty stdout master: %s", container.Id, err)
}
}
if container.ptyStderrMaster != nil {
if err := container.ptyStderrMaster.Close(); err != nil {
Debugf("%s: Error close pty stderr master: %s", container.Id, err)
}
}
if err := container.Unmount(); err != nil {
container.stdout.Close()
container.stderr.Close()
if err := container.Mountpoint.Umount(); err != nil {
log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
}
@@ -529,21 +461,10 @@ func (container *Container) monitor() {
// Report status back
container.State.setStopped(exitCode)
if err := container.ToDisk(); err != nil {
// FIXME: there is a race condition here which causes this to fail during the unit tests.
// If another goroutine was waiting for Wait() to return before removing the container's root
// from the filesystem... At this point it may already have done so.
// This is because State.setStopped() has already been called, and has caused Wait()
// to return.
// FIXME: why are we serializing running state to disk in the first place?
//log.Printf("%s: Failed to dump configuration to the disk: %s", container.Id, err)
}
container.save()
}
func (container *Container) kill() error {
if container.cmd == nil {
return nil
}
if err := container.cmd.Process.Kill(); err != nil {
return err
}
@@ -565,9 +486,9 @@ func (container *Container) Stop() error {
}
// 1. Send a SIGTERM
if output, err := exec.Command("lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
log.Print(string(output))
log.Print("Failed to send SIGTERM to the process, force killing")
if output, err := exec.Command("/usr/bin/lxc-kill", "-n", container.Id, "15").CombinedOutput(); err != nil {
log.Printf(string(output))
log.Printf("Failed to send SIGTERM to the process, force killing")
if err := container.Kill(); err != nil {
return err
}
@@ -602,17 +523,6 @@ func (container *Container) Wait() int {
return container.State.ExitCode
}
func (container *Container) ExportRw() (Archive, error) {
return Tar(container.rwPath(), Uncompressed)
}
func (container *Container) Export() (Archive, error) {
if err := container.EnsureMounted(); err != nil {
return nil, err
}
return Tar(container.RootfsPath(), Uncompressed)
}
func (container *Container) WaitTimeout(timeout time.Duration) error {
done := make(chan bool)
go func() {
@@ -622,89 +532,9 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
select {
case <-time.After(timeout):
return fmt.Errorf("Timed Out")
return errors.New("Timed Out")
case <-done:
return nil
}
return nil
}
func (container *Container) EnsureMounted() error {
if mounted, err := container.Mounted(); err != nil {
return err
} else if mounted {
return nil
}
return container.Mount()
}
func (container *Container) Mount() error {
image, err := container.GetImage()
if err != nil {
return err
}
return image.Mount(container.RootfsPath(), container.rwPath())
}
func (container *Container) Changes() ([]Change, error) {
image, err := container.GetImage()
if err != nil {
return nil, err
}
return image.Changes(container.rwPath())
}
func (container *Container) GetImage() (*Image, error) {
if container.runtime == nil {
return nil, fmt.Errorf("Can't get image of unregistered container")
}
return container.runtime.graph.Get(container.Image)
}
func (container *Container) Mounted() (bool, error) {
return Mounted(container.RootfsPath())
}
func (container *Container) Unmount() error {
return Unmount(container.RootfsPath())
}
// ShortId returns a shorthand version of the container's id for convenience.
// A collision with other container shorthands is very unlikely, but possible.
// In case of a collision a lookup with Runtime.Get() will fail, and the caller
// will need to use a langer prefix, or the full-length container Id.
func (container *Container) ShortId() string {
return TruncateId(container.Id)
}
func (container *Container) logPath(name string) string {
return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name))
}
func (container *Container) ReadLog(name string) (io.Reader, error) {
return os.Open(container.logPath(name))
}
func (container *Container) jsonPath() string {
return path.Join(container.root, "config.json")
}
func (container *Container) lxcConfigPath() string {
return path.Join(container.root, "config.lxc")
}
// This method must be exported to be used from the lxc template
func (container *Container) RootfsPath() string {
return path.Join(container.root, "rootfs")
}
func (container *Container) rwPath() string {
return path.Join(container.root, "rw")
}
func validateId(id string) error {
if id == "" {
return fmt.Errorf("Invalid empty id")
}
return nil
}
+265 -457
View File
File diff suppressed because it is too large Load Diff
-4
View File
@@ -1,4 +0,0 @@
The `contrib` directory contains scripts, images, and other helpful things
which are not part of the core docker distribution. Please note that they
could be out of date, since they do not receive the same attention as the
rest of the repository.
-39
View File
@@ -1,39 +0,0 @@
#!/bin/bash
# Generate a very minimal filesystem based on busybox-static,
# and load it into the local docker under the name "busybox".
BUSYBOX=$(which busybox)
[ "$BUSYBOX" ] || {
echo "Sorry, I could not locate busybox."
echo "Try 'apt-get install busybox-static'?"
exit 1
}
set -e
ROOTFS=/tmp/rootfs-busybox-$$-$RANDOM
mkdir $ROOTFS
cd $ROOTFS
mkdir bin etc dev dev/pts lib proc sys tmp
touch etc/resolv.conf
cp /etc/nsswitch.conf etc/nsswitch.conf
echo root:x:0:0:root:/:/bin/sh > etc/passwd
echo root:x:0: > etc/group
ln -s lib lib64
ln -s bin sbin
cp $BUSYBOX bin
for X in $(busybox --list)
do
ln -s busybox bin/$X
done
rm bin/init
ln bin/busybox bin/init
cp /lib/x86_64-linux-gnu/lib{pthread,c,dl,nsl,nss_*}.so.* lib
cp /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 lib
for X in console null ptmx random stdin stdout stderr tty urandom zero
do
cp -a /dev/$X dev
done
tar -cf- . | docker import - busybox
docker run -i -u root busybox /bin/echo Success.
-1
View File
@@ -1 +0,0 @@
../Makefile
-73
View File
@@ -1,73 +0,0 @@
PKG_NAME=dotcloud-docker
PKG_ARCH=amd64
PKG_VERSION=1
ROOT_PATH:=$(PWD)
BUILD_PATH=build # Do not change, decided by dpkg-buildpackage
BUILD_SRC=build_src
GITHUB_PATH=src/github.com/dotcloud/docker
INSDIR=usr/bin
SOURCE_PACKAGE=$(PKG_NAME)_$(PKG_VERSION).orig.tar.gz
DEB_PACKAGE=$(PKG_NAME)_$(PKG_VERSION)_$(PKG_ARCH).deb
EXTRA_GO_PKG=./auth
TMPDIR=$(shell mktemp -d -t XXXXXX)
# Build a debian source package
all: clean build_in_deb
build_in_deb:
echo "GOPATH = " $(ROOT_PATH)
mkdir bin
cd $(GITHUB_PATH)/docker; GOPATH=$(ROOT_PATH) go build -o $(ROOT_PATH)/bin/docker
# DESTDIR provided by Debian packaging
install:
# Call this from a go environment (as packaged for deb source package)
mkdir -p $(DESTDIR)/$(INSDIR)
mkdir -p $(DESTDIR)/etc/init
install -m 0755 bin/docker $(DESTDIR)/$(INSDIR)
install -o root -m 0755 etc/docker.upstart $(DESTDIR)/etc/init/docker.conf
$(BUILD_SRC): clean
# Copy ourselves into $BUILD_SRC to comply with unusual golang constraints
tar --exclude=*.tar.gz --exclude=checkout.tgz -f checkout.tgz -cz *
mkdir -p $(BUILD_SRC)/$(GITHUB_PATH)
tar -f checkout.tgz -C $(BUILD_SRC)/$(GITHUB_PATH) -xz
cd $(BUILD_SRC)/$(GITHUB_PATH)/docker; GOPATH=$(ROOT_PATH)/$(BUILD_SRC) go get -d
for d in `find $(BUILD_SRC) -name '.git*'`; do rm -rf $$d; done
# Populate source build with debian stuff
cp -R -L ./deb/* $(BUILD_SRC)
$(SOURCE_PACKAGE): $(BUILD_SRC)
rm -f $(SOURCE_PACKAGE)
# Create the debian source package
tar -f $(SOURCE_PACKAGE) -C ${ROOT_PATH}/${BUILD_SRC} -cz .
# Build deb package fetching go dependencies and cleaning up git repositories
deb: $(DEB_PACKAGE)
$(DEB_PACKAGE): $(SOURCE_PACKAGE)
# dpkg-buildpackage looks for source package tarball in ../
cd $(BUILD_SRC); dpkg-buildpackage
rm -rf $(BUILD_PATH) debian/$(PKG_NAME)* debian/files
debsrc: $(SOURCE_PACKAGE)
# Build local sources
#$(PKG_NAME): build_local
build_local:
-@mkdir -p bin
cd docker && go build -o ../bin/docker
gotest:
@echo "\033[36m[Testing]\033[00m docker..."
@sudo -E GOPATH=$(ROOT_PATH)/$(BUILD_SRC) go test -v . $(EXTRA_GO_PKG) && \
echo -n "\033[32m[OK]\033[00m" || \
echo -n "\033[31m[FAIL]\033[00m"; \
echo " docker"
@sudo rm -rf /tmp/docker-*
clean:
rm -rf $(BUILD_PATH) debian/$(PKG_NAME)* debian/files $(BUILD_SRC) checkout.tgz bin
-1
View File
@@ -1 +0,0 @@
../README.md
-5
View File
@@ -1,5 +0,0 @@
dotcloud-docker (1) precise; urgency=low
* Initial release
-- dotCloud <ops@dotcloud.com> Mon, 14 Mar 2013 04:43:21 -0700
-20
View File
@@ -1,20 +0,0 @@
Source: dotcloud-docker
Section: misc
Priority: extra
Homepage: https://github.com/dotcloud/docker
Maintainer: Daniel Mizyrycki <daniel@dotcloud.com>
Build-Depends: debhelper (>= 8.0.0), git, golang
Vcs-Git: https://github.com/dotcloud/docker.git
Standards-Version: 3.9.2
Package: dotcloud-docker
Architecture: amd64
Provides: dotcloud-docker
Depends: lxc, wget, bsdtar, curl
Conflicts: docker
Description: A process manager with superpowers
It encapsulates heterogeneous payloads in Standard Containers, and runs
them on any server with strong guarantees of isolation and repeatability.
Is is a great building block for automating distributed systems:
large-scale web deployments, database clusters, continuous deployment
systems, private PaaS, service-oriented architectures, etc.
-209
View File
@@ -1,209 +0,0 @@
Format: http://dep.debian.net/deps/dep5
Upstream-Name: dotcloud-docker
Source: https://github.com/dotcloud/docker
Files: *
Copyright: 2012 DotCloud Inc (opensource@dotcloud.com)
License: Apache License Version 2.0
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2012 DotCloud Inc (opensource@dotcloud.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-10
View File
@@ -1,10 +0,0 @@
description "Run docker"
start on runlevel [2345]
stop on starting rc RUNLEVEL=[016]
respawn
script
test -f /etc/default/locale && . /etc/default/locale || true
LANG=$LANG LC_ALL=$LANG /usr/bin/docker -d
end script
+17
View File
@@ -0,0 +1,17 @@
dockerd (0.1.1-1) unstable; urgency=low
* fs branch was merged upstream
-- Jeremy Grosser <jeremy@synack.me> Wed, 13 Mar 2013 00:23:46 +0000
dockerd (0.1-2) unstable; urgency=low
* Fix upstart script
-- Jeremy Grosser <jeremy@synack.me> Tue, 12 Mar 2013 23:50:16 +0000
dockerd (0.1-1) unstable; urgency=low
* Initial release
-- Jeremy Grosser <jeremy@synack.me> Tue, 12 Mar 2013 03:24:45 +0000
View File
+14
View File
@@ -0,0 +1,14 @@
Source: dockerd
Section: misc
Priority: extra
Maintainer: Jeremy Grosser <jeremy@synack.me>
Build-Depends: debhelper (>= 8.0.0), golang, git, libsqlite3-dev, pkg-config
Standards-Version: 3.9.3
Homepage: https://github.com/dotcloud/docker/
Package: dockerd
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, bsdtar, lxc, curl
Description: Process manager with superpowers
Encapsulates heterogeneous payloads in Standard Containers, and runs them on
any server with strong guarantees of isolation and repeatability.
+16
View File
@@ -0,0 +1,16 @@
Format: http://dep.debian.net/deps/dep5
Upstream-Name: docker
Source: https://github.com/dotcloud/docker
Files: *
Copyright: 2012-2013 dotCloud, inc.
License: Apache 2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+2
View File
@@ -0,0 +1,2 @@
build/docker usr/bin
build/dockerd usr/sbin
+2 -2
View File
@@ -1,4 +1,4 @@
description "Run docker"
description "Run dockerd"
start on runlevel [2345]
stop on starting rc RUNLEVEL=[016]
@@ -6,5 +6,5 @@ respawn
script
test -f /etc/default/locale && . /etc/default/locale || true
LANG=$LANG LC_ALL=$LANG /usr/bin/docker -d
LANG=$LANG LC_ALL=$LANG /usr/sbin/dockerd
end script
View File
+1 -1
View File
@@ -10,4 +10,4 @@
#export DH_VERBOSE=1
%:
dh $@
dh $@
View File
+160
View File
@@ -0,0 +1,160 @@
package docker
import (
"./fs"
"container/list"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"sort"
)
type Docker struct {
root string
repository string
containers *list.List
networkManager *NetworkManager
Store *fs.Store
}
func (docker *Docker) List() []*Container {
containers := new(History)
for e := docker.containers.Front(); e != nil; e = e.Next() {
containers.Add(e.Value.(*Container))
}
return *containers
}
func (docker *Docker) getContainerElement(id string) *list.Element {
for e := docker.containers.Front(); e != nil; e = e.Next() {
container := e.Value.(*Container)
if container.Id == id {
return e
}
}
return nil
}
func (docker *Docker) Get(id string) *Container {
e := docker.getContainerElement(id)
if e == nil {
return nil
}
return e.Value.(*Container)
}
func (docker *Docker) Exists(id string) bool {
return docker.Get(id) != nil
}
func (docker *Docker) Create(id string, command string, args []string, image *fs.Image, config *Config) (*Container, error) {
if docker.Exists(id) {
return nil, fmt.Errorf("Container %v already exists", id)
}
root := path.Join(docker.repository, id)
container, err := createContainer(id, root, command, args, image, config, docker.networkManager)
if err != nil {
return nil, err
}
docker.containers.PushBack(container)
return container, nil
}
func (docker *Docker) Destroy(container *Container) error {
element := docker.getContainerElement(container.Id)
if element == nil {
return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.Id)
}
if err := container.Stop(); err != nil {
return err
}
if container.Mountpoint.Mounted() {
if err := container.Mountpoint.Umount(); err != nil {
log.Printf("Unable to umount container %v: %v", container.Id, err)
}
if err := container.Mountpoint.Deregister(); err != nil {
log.Printf("Unable to deregiser mountpoint %v: %v", container.Mountpoint.Root, err)
}
}
if err := os.RemoveAll(container.Root); err != nil {
log.Printf("Unable to remove filesystem for %v: %v", container.Id, err)
}
docker.containers.Remove(element)
return nil
}
func (docker *Docker) restore() error {
dir, err := ioutil.ReadDir(docker.repository)
if err != nil {
return err
}
for _, v := range dir {
container, err := loadContainer(docker.Store, path.Join(docker.repository, v.Name()), docker.networkManager)
if err != nil {
log.Printf("Failed to load container %v: %v", v.Name(), err)
continue
}
docker.containers.PushBack(container)
}
return nil
}
func New() (*Docker, error) {
return NewFromDirectory("/var/lib/docker")
}
func NewFromDirectory(root string) (*Docker, error) {
store, err := fs.New(path.Join(root, "images"))
if err != nil {
return nil, err
}
netManager, err := newNetworkManager(networkBridgeIface)
if err != nil {
return nil, err
}
docker := &Docker{
root: root,
repository: path.Join(root, "containers"),
containers: list.New(),
Store: store,
networkManager: netManager,
}
if err := os.MkdirAll(docker.repository, 0700); err != nil && !os.IsExist(err) {
return nil, err
}
if err := docker.restore(); err != nil {
return nil, err
}
return docker, nil
}
type History []*Container
func (history *History) Len() int {
return len(*history)
}
func (history *History) Less(i, j int) bool {
containers := *history
return containers[j].When().Before(containers[i].When())
}
func (history *History) Swap(i, j int) {
containers := *history
tmp := containers[i]
containers[i] = containers[j]
containers[j] = tmp
}
func (history *History) Add(container *Container) {
*history = append(*history, container)
sort.Sort(history)
}
+14 -94
View File
@@ -1,109 +1,29 @@
package main
import (
"../client"
"flag"
"github.com/dotcloud/docker"
"github.com/dotcloud/docker/rcli"
"github.com/dotcloud/docker/term"
"io"
"log"
"os"
"os/signal"
"path"
)
var GIT_COMMIT string
func main() {
if docker.SelfPath() == "/sbin/init" {
// Running in init mode
docker.SysInit()
return
}
// FIXME: Switch d and D ? (to be more sshd like)
flDaemon := flag.Bool("d", false, "Daemon mode")
flDebug := flag.Bool("D", false, "Debug mode")
flag.Parse()
if *flDebug {
os.Setenv("DEBUG", "1")
}
docker.GIT_COMMIT = GIT_COMMIT
if *flDaemon {
if flag.NArg() != 0 {
flag.Usage()
return
}
if err := daemon(); err != nil {
log.Fatal(err)
if cmd := path.Base(os.Args[0]); cmd == "docker" {
fl_shell := flag.Bool("i", false, "Interactive mode")
flag.Parse()
if *fl_shell {
if err := client.InteractiveMode(flag.Args()...); err != nil {
log.Fatal(err)
}
} else {
if err := client.SimpleMode(os.Args[1:]); err != nil {
log.Fatal(err)
}
}
} else {
if err := runCommand(flag.Args()); err != nil {
if err := client.SimpleMode(append([]string{cmd}, os.Args[1:]...)); err != nil {
log.Fatal(err)
}
}
}
func daemon() error {
service, err := docker.NewServer()
if err != nil {
return err
}
return rcli.ListenAndServe("tcp", "127.0.0.1:4242", service)
}
func runCommand(args []string) error {
var oldState *term.State
var err error
if term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" {
oldState, err = term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
return err
}
defer term.Restore(int(os.Stdin.Fd()), oldState)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for _ = range c {
term.Restore(int(os.Stdin.Fd()), oldState)
log.Printf("\nSIGINT received\n")
os.Exit(0)
}
}()
}
// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
// CloseWrite(), which we need to cleanly signal that stdin is closed without
// closing the connection.
// See http://code.google.com/p/go/issues/detail?id=3345
if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil {
receiveStdout := docker.Go(func() error {
_, err := io.Copy(os.Stdout, conn)
return err
})
sendStdin := docker.Go(func() error {
_, err := io.Copy(conn, os.Stdin)
if err := conn.CloseWrite(); err != nil {
log.Printf("Couldn't send EOF: " + err.Error())
}
return err
})
if err := <-receiveStdout; err != nil {
return err
}
if !term.IsTerminal(int(os.Stdin.Fd())) {
if err := <-sendStdin; err != nil {
return err
}
}
} else {
service, err := docker.NewServer()
if err != nil {
return err
}
if err := rcli.LocalCall(service, os.Stdin, os.Stdout, args...); err != nil {
return err
}
}
if oldState != nil {
term.Restore(int(os.Stdin.Fd()), oldState)
}
return nil
}
+290
View File
@@ -0,0 +1,290 @@
package docker
import (
"./fs"
"io"
"io/ioutil"
"log"
"os"
"testing"
)
const testLayerPath string = "/var/lib/docker/docker-ut.tar"
func nuke(docker *Docker) error {
return os.RemoveAll(docker.root)
}
func layerArchive(tarfile string) (io.Reader, error) {
// FIXME: need to close f somewhere
f, err := os.Open(tarfile)
if err != nil {
return nil, err
}
return f, nil
}
func init() {
// Hack to run sys init during unit testing
if SelfPath() == "/sbin/init" {
SysInit()
}
// Make sure the unit test image is there
if _, err := os.Stat(testLayerPath); err != nil {
if !os.IsNotExist(err) {
panic(err)
}
log.Fatalf("Unit test base image not found. Please fix the problem by running \"debootstrap --arch=amd64 quantal %v\"", testLayerPath)
return
}
}
func newTestDocker() (*Docker, error) {
root, err := ioutil.TempDir("", "docker-test")
if err != nil {
return nil, err
}
docker, err := NewFromDirectory(root)
if err != nil {
return nil, err
}
if layer, err := layerArchive(testLayerPath); err != nil {
panic(err)
} else {
_, err = docker.Store.Create(layer, nil, "docker-ut", "unit tests")
if err != nil {
panic(err)
}
}
return docker, nil
}
func GetTestImage(docker *Docker) *fs.Image {
imgs, err := docker.Store.Images()
if err != nil {
panic(err)
} else if len(imgs) < 1 {
panic("GASP")
}
return imgs[0]
}
func TestCreate(t *testing.T) {
docker, err := newTestDocker()
if err != nil {
t.Fatal(err)
}
defer nuke(docker)
// Make sure we start we 0 containers
if len(docker.List()) != 0 {
t.Errorf("Expected 0 containers, %v found", len(docker.List()))
}
container, err := docker.Create(
"test_create",
"ls",
[]string{"-al"},
GetTestImage(docker),
&Config{},
)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := docker.Destroy(container); err != nil {
t.Error(err)
}
}()
// Make sure we can find the newly created container with List()
if len(docker.List()) != 1 {
t.Errorf("Expected 1 container, %v found", len(docker.List()))
}
// Make sure the container List() returns is the right one
if docker.List()[0].Id != "test_create" {
t.Errorf("Unexpected container %v returned by List", docker.List()[0])
}
// Make sure we can get the container with Get()
if docker.Get("test_create") == nil {
t.Errorf("Unable to get newly created container")
}
// Make sure it is the right container
if docker.Get("test_create") != container {
t.Errorf("Get() returned the wrong container")
}
// Make sure Exists returns it as existing
if !docker.Exists("test_create") {
t.Errorf("Exists() returned false for a newly created container")
}
}
func TestDestroy(t *testing.T) {
docker, err := newTestDocker()
if err != nil {
t.Fatal(err)
}
defer nuke(docker)
container, err := docker.Create(
"test_destroy",
"ls",
[]string{"-al"},
GetTestImage(docker),
&Config{},
)
if err != nil {
t.Fatal(err)
}
// Destroy
if err := docker.Destroy(container); err != nil {
t.Error(err)
}
// Make sure docker.Exists() behaves correctly
if docker.Exists("test_destroy") {
t.Errorf("Exists() returned true")
}
// Make sure docker.List() doesn't list the destroyed container
if len(docker.List()) != 0 {
t.Errorf("Expected 0 container, %v found", len(docker.List()))
}
// Make sure docker.Get() refuses to return the unexisting container
if docker.Get("test_destroy") != nil {
t.Errorf("Unable to get newly created container")
}
// Make sure the container root directory does not exist anymore
_, err = os.Stat(container.Root)
if err == nil || !os.IsNotExist(err) {
t.Errorf("Container root directory still exists after destroy")
}
// Test double destroy
if err := docker.Destroy(container); err == nil {
// It should have failed
t.Errorf("Double destroy did not fail")
}
}
func TestGet(t *testing.T) {
docker, err := newTestDocker()
if err != nil {
t.Fatal(err)
}
defer nuke(docker)
container1, err := docker.Create(
"test1",
"ls",
[]string{"-al"},
GetTestImage(docker),
&Config{},
)
if err != nil {
t.Fatal(err)
}
defer docker.Destroy(container1)
container2, err := docker.Create(
"test2",
"ls",
[]string{"-al"},
GetTestImage(docker),
&Config{},
)
if err != nil {
t.Fatal(err)
}
defer docker.Destroy(container2)
container3, err := docker.Create(
"test3",
"ls",
[]string{"-al"},
GetTestImage(docker),
&Config{},
)
if err != nil {
t.Fatal(err)
}
defer docker.Destroy(container3)
if docker.Get("test1") != container1 {
t.Errorf("Get(test1) returned %v while expecting %v", docker.Get("test1"), container1)
}
if docker.Get("test2") != container2 {
t.Errorf("Get(test2) returned %v while expecting %v", docker.Get("test2"), container2)
}
if docker.Get("test3") != container3 {
t.Errorf("Get(test3) returned %v while expecting %v", docker.Get("test3"), container3)
}
}
func TestRestore(t *testing.T) {
root, err := ioutil.TempDir("", "docker-test")
if err != nil {
t.Fatal(err)
}
docker1, err := NewFromDirectory(root)
if err != nil {
t.Fatal(err)
}
defer nuke(docker1)
if layer, err := layerArchive(testLayerPath); err != nil {
panic(err)
} else {
_, err = docker1.Store.Create(layer, nil, "docker-ut", "unit tests")
if err != nil {
panic(err)
}
}
// Create a container with one instance of docker
container1, err := docker1.Create(
"restore_test",
"ls",
[]string{"-al"},
GetTestImage(docker1),
&Config{},
)
if err != nil {
t.Fatal(err)
}
defer docker1.Destroy(container1)
if len(docker1.List()) != 1 {
t.Errorf("Expected 1 container, %v found", len(docker1.List()))
}
if err := container1.Run(); err != nil {
t.Fatal(err)
}
// Here are are simulating a docker restart - that is, reloading all containers
// from scratch
docker2, err := NewFromDirectory(root)
if err != nil {
t.Fatal(err)
}
defer nuke(docker2)
if len(docker2.List()) != 1 {
t.Errorf("Expected 1 container, %v found", len(docker2.List()))
}
container2 := docker2.Get("restore_test")
if container2 == nil {
t.Fatal("Unable to Get container")
}
if err := container2.Run(); err != nil {
t.Fatal(err)
}
}
+24
View File
@@ -0,0 +1,24 @@
package main
import (
".."
"../server"
"flag"
"log"
)
func main() {
if docker.SelfPath() == "/sbin/init" {
// Running in init mode
docker.SysInit()
return
}
flag.Parse()
d, err := server.New()
if err != nil {
log.Fatal(err)
}
if err := d.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
+221
View File
File diff suppressed because one or more lines are too long
-184
View File
@@ -1,184 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) sources
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
# @echo " html to make standalone HTML files"
# @echo " dirhtml to make HTML files named index.html in directories"
# @echo " singlehtml to make a single large HTML file"
# @echo " pickle to make pickle files"
# @echo " json to make JSON files"
# @echo " htmlhelp to make HTML files and a HTML help project"
# @echo " qthelp to make HTML files and a qthelp project"
# @echo " devhelp to make HTML files and a Devhelp project"
# @echo " epub to make an epub"
# @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
# @echo " latexpdf to make LaTeX files and run them through pdflatex"
# @echo " text to make text files"
# @echo " man to make manual pages"
# @echo " texinfo to make Texinfo files"
# @echo " info to make Texinfo files and run them through makeinfo"
# @echo " gettext to make PO message catalogs"
# @echo " changes to make an overview of all changed/added/deprecated items"
# @echo " linkcheck to check all external links for integrity"
# @echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " docs to build the docs and copy the static files to the outputdir"
@echo " publish to publish the app to dotcloud"
clean:
-rm -rf $(BUILDDIR)/*
docs:
-rm -rf $(BUILDDIR)/*
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/html
cp sources/index.html $(BUILDDIR)/html/
cp -r sources/gettingstarted $(BUILDDIR)/html/
cp sources/dotcloud.yml $(BUILDDIR)/html/
cp sources/CNAME $(BUILDDIR)/html/
cp sources/.nojekyll $(BUILDDIR)/html/
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
connect:
@echo pushing changes to staging site
@cd _build/html/ ; \
@dotcloud list ; \
dotcloud connect dockerwebsite
push:
@cd _build/html/ ; \
dotcloud push
github-deploy: docs
rm -fr github-deploy
git clone ssh://git@github.com/dotcloud/docker github-deploy
cd github-deploy && git checkout -f gh-pages && git rm -r * && rsync -avH ../_build/html/ ./ && touch .nojekyll && echo "docker.io" > CNAME && git add * && git commit -m "Updating docs"
$(VERSIONS):
@echo "Hello world"
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Docker.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Docker.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Docker"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Docker"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
-42
View File
@@ -1,42 +0,0 @@
Docker documentation and website
================================
Documentation
-------------
This is your definite place to contribute to the docker documentation. The documentation is generated from the
.rst files under sources.
The folder also contains the other files to create the http://docker.io website, but you can generally ignore
most of those.
Installation
------------
* Work in your own fork of the code, we accept pull requests.
* Install sphinx: ``pip install sphinx``
* If pip is not available you can probably install it using your favorite package manager as **python-pip**
Usage
-----
* change the .rst files with your favorite editor to your liking
* run *make docs* to clean up old files and generate new ones
* your static website can now be found in the _build dir
* to preview what you have generated, cd into _build/html and then run 'python -m SimpleHTTPServer 8000'
Working using github's file editor
----------------------------------
Alternatively, for small changes and typo's you might want to use github's built in file editor. It allows
you to preview your changes right online. Just be carefull not to create many commits.
Images
------
When you need to add images, try to make them as small as possible (e.g. as gif).
Notes
-----
* The index.html and gettingstarted.html files are copied from the source dir to the output dir without modification.
So changes to those pages should be made directly in html
* For the template the css is compiled from less. When changes are needed they can be compiled using
lessc ``lessc main.less`` or watched using watch-lessc ``watch-lessc -i main.less -o main.css``
View File
-1
View File
@@ -1 +0,0 @@
docker.io
-100
View File
@@ -1,100 +0,0 @@
:title: Base commands
:description: Common usage and commands
:keywords: Examples, Usage
The basics
=============
Starting Docker
---------------
If you have used one of the quick install paths', Docker may have been installed with upstart, Ubuntu's
system for starting processes at boot time. You should be able to run ``docker help`` and get output.
If you get ``docker: command not found`` or something like ``/var/lib/docker/repositories: permission denied``
you will need to specify the path to it and manually start it.
.. code-block:: bash
# Run docker in daemon mode
sudo <path to>/docker -d &
Running an interactive shell
----------------------------
.. code-block:: bash
# Download a base image
docker pull base
# Run an interactive shell in the base image,
# allocate a tty, attach stdin and stdout
docker run -i -t base /bin/bash
Starting a long-running worker process
--------------------------------------
.. code-block:: bash
# Start a very useful long-running process
JOB=$(docker run -d base /bin/sh -c "while true; do echo Hello world; sleep 1; done")
# Collect the output of the job so far
docker logs $JOB
# Kill the job
docker kill $JOB
Listing all running containers
------------------------------
.. code-block:: bash
docker ps
Expose a service on a TCP port
------------------------------
.. code-block:: bash
# Expose port 4444 of this container, and tell netcat to listen on it
JOB=$(docker run -d -p 4444 base /bin/nc -l -p 4444)
# Which public port is NATed to my container?
PORT=$(docker port $JOB 4444)
# Connect to the public port via the host's public address
# Please note that because of how routing works connecting to localhost or 127.0.0.1 $PORT will not work.
echo hello world | nc $(hostname) $PORT
# Verify that the network connection worked
echo "Daemon received: $(docker logs $JOB)"
Committing (saving) an image
-----------------------------
Save your containers state to a container image, so the state can be re-used.
When you commit your container only the differences between the image the container was created from
and the current state of the container will be stored (as a diff). See which images you already have
using ``docker images``
.. code-block:: bash
# Commit your container to a new named image
docker commit <container_id> <some_name>
# List your containers
docker images
You now have a image state from which you can create new instances.
Read more about :ref:`working_with_the_repository` or continue to the complete :ref:`cli`
-321
View File
@@ -1,321 +0,0 @@
:title: Command Line Interface
:description: Docker's CLI command description and usage
:keywords: Docker, Docker documentation, CLI, command line
.. _cli:
Command Line Interface
======================
Docker Usage
~~~~~~~~~~~~
::
$ docker
Usage: docker COMMAND [arg...]
A self-sufficient runtime for linux containers.
Commands:
attach Attach to a running container
commit Create a new image from a container's changes
diff Inspect changes on a container's filesystem
export Stream the contents of a container as a tar archive
history Show the history of an image
images List images
import Create a new filesystem image from the contents of a tarball
info Display system-wide information
inspect Return low-level information on a container
kill Kill a running container
login Register or Login to the docker registry server
logs Fetch the logs of a container
port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
ps List containers
pull Pull an image or a repository to the docker registry server
push Push an image or a repository to the docker registry server
restart Restart a running container
rm Remove a container
rmi Remove an image
run Run a command in a new container
start Start a stopped container
stop Stop a running container
tag Tag an image into a repository
version Show the docker version information
wait Block until a container stops, then print its exit code
attach
~~~~~~
::
Usage: docker attach [OPTIONS]
Attach to a running container
-e=true: Attach to stderr
-i=false: Attach to stdin
-o=true: Attach to stdout
commit
~~~~~~
::
Usage: docker commit [OPTIONS] CONTAINER [DEST]
Create a new image from a container's changes
-m="": Commit message
diff
~~~~
::
Usage: docker diff CONTAINER [OPTIONS]
Inspect changes on a container's filesystem
export
~~~~~~
::
Usage: docker export CONTAINER
Export the contents of a filesystem as a tar archive
history
~~~~~~~
::
Usage: docker history [OPTIONS] IMAGE
Show the history of an image
images
~~~~~~
::
Usage: docker images [OPTIONS] [NAME]
List images
-a=false: show all images
-q=false: only show numeric IDs
import
~~~~~~
::
Usage: docker import [OPTIONS] URL|- [REPOSITORY [TAG]]
Create a new filesystem image from the contents of a tarball
info
~~~~
::
Usage: docker info
Display system-wide information.
inspect
~~~~~~~
::
Usage: docker inspect [OPTIONS] CONTAINER
Return low-level information on a container
kill
~~~~
::
Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
Kill a running container
login
~~~~~
::
Usage: docker login
Register or Login to the docker registry server
logs
~~~~
::
Usage: docker logs [OPTIONS] CONTAINER
Fetch the logs of a container
port
~~~~
::
Usage: docker port [OPTIONS] CONTAINER PRIVATE_PORT
Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
ps
~~
::
Usage: docker ps [OPTIONS]
List containers
-a=false: Show all containers. Only running containers are shown by default.
-notrunc=false: Don't truncate output
-q=false: Only display numeric IDs
pull
~~~~
::
Usage: docker pull NAME
Pull an image or a repository from the registry
push
~~~~
::
Usage: docker push NAME
Push an image or a repository to the registry
restart
~~~~~~~
::
Usage: docker restart [OPTIONS] NAME
Restart a running container
rm
~~
::
Usage: docker rm [OPTIONS] CONTAINER
Remove a container
rmi
~~~
::
Usage: docker rmi [OPTIONS] IMAGE
Remove an image
-a=false: Use IMAGE as a path and remove ALL images in this path
-r=false: Use IMAGE as a regular expression instead of an exact name
run
~~~
::
Usage: docker run [OPTIONS] IMAGE COMMAND [ARG...]
Run a command in a new container
-c="": Comment
-i=false: Keep stdin open even if not attached
-m=0: Memory limit (in bytes)
-p=[]: Map a network port to the container
-t=false: Allocate a pseudo-tty
-h="": Container host name
-u="": Username or UID
start
~~~~~
::
Usage: docker start [OPTIONS] NAME
Start a stopped container
stop
~~~~
::
Usage: docker stop [OPTIONS] NAME
Stop a running container
tag
~~~
::
Usage: docker tag [OPTIONS] IMAGE REPOSITORY [TAG]
Tag an image into a repository
-f=false: Force
version
~~~~~~~
::
Usage: docker version
Show the docker version information
wait
~~~~
::
Usage: docker wait [OPTIONS] NAME
Block until a container stops, then print its exit code.
-16
View File
@@ -1,16 +0,0 @@
:title: docker documentation
:description: -- todo: change me
:keywords: todo: change me
Commands
========
Contents:
.. toctree::
:maxdepth: 2
basics
workingwithrepository
cli
@@ -1,42 +0,0 @@
.. _working_with_the_repository:
Working with the repository
============================
Connecting to the repository
----------------------------
You create a user on the central docker repository by running
.. code-block:: bash
docker login
If your username does not exist it will prompt you to also enter a password and your e-mail address. It will then
automatically log you in.
Committing a container to a named image
---------------------------------------
In order to commit to the repository it is required to have committed your container to an image with your namespace.
.. code-block:: bash
# for example docker commit $CONTAINER_ID dhrp/kickassapp
docker commit <container_id> <your username>/<some_name>
Pushing a container to the repository
-----------------------------------------
In order to push an image to the repository you need to have committed your container to a named image (see above)
Now you can commit this image to the repository
.. code-block:: bash
# for example docker push dhrp/kickassapp
docker push <image-name>
-25
View File
@@ -1,25 +0,0 @@
:title: Building blocks
:description: An introduction to docker and standard containers?
:keywords: containers, lxc, concepts, explanation
Building blocks
===============
.. _images:
Images
------
An original container image. These are stored on disk and are comparable with what you normally expect from a stoppped virtual machine image. Images are stored (and retrieved from) repository
Images are stored on your local file system under /var/lib/docker/images
.. _containers:
Containers
----------
A container is a local version of an image. It can be running or stopped, The equivalent would be a virtual machine instance.
Containers are stored on your local file system under /var/lib/docker/containers
-128
View File
@@ -1,128 +0,0 @@
:title: Introduction
:description: An introduction to docker and standard containers?
:keywords: containers, lxc, concepts, explanation
:note: This version of the introduction is temporary, just to make sure we don't break the links from the website when the documentation is updated
Introduction
============
Docker - The Linux container runtime
------------------------------------
Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
Docker is a great building block for automating distributed systems: large-scale web deployments, database clusters, continuous deployment systems, private PaaS, service-oriented architectures, etc.
- **Heterogeneous payloads** Any combination of binaries, libraries, configuration files, scripts, virtualenvs, jars, gems, tarballs, you name it. No more juggling between domain-specific tools. Docker can deploy and run them all.
- **Any server** Docker can run on any x64 machine with a modern linux kernel - whether it's a laptop, a bare metal server or a VM. This makes it perfect for multi-cloud deployments.
- **Isolation** docker isolates processes from each other and from the underlying host, using lightweight containers.
- **Repeatability** Because containers are isolated in their own filesystem, they behave the same regardless of where, when, and alongside what they run.
What is a Standard Container?
-----------------------------
Docker defines a unit of software delivery called a Standard Container. The goal of a Standard Container is to encapsulate a software component and all its dependencies in
a format that is self-describing and portable, so that any compliant runtime can run it without extra dependency, regardless of the underlying machine and the contents of the container.
The spec for Standard Containers is currently work in progress, but it is very straightforward. It mostly defines 1) an image format, 2) a set of standard operations, and 3) an execution environment.
A great analogy for this is the shipping container. Just like Standard Containers are a fundamental unit of software delivery, shipping containers (http://bricks.argz.com/ins/7823-1/12) are a fundamental unit of physical delivery.
Standard operations
~~~~~~~~~~~~~~~~~~~
Just like shipping containers, Standard Containers define a set of STANDARD OPERATIONS. Shipping containers can be lifted, stacked, locked, loaded, unloaded and labelled. Similarly, standard containers can be started, stopped, copied, snapshotted, downloaded, uploaded and tagged.
Content-agnostic
~~~~~~~~~~~~~~~~~~~
Just like shipping containers, Standard Containers are CONTENT-AGNOSTIC: all standard operations have the same effect regardless of the contents. A shipping container will be stacked in exactly the same way whether it contains Vietnamese powder coffee or spare Maserati parts. Similarly, Standard Containers are started or uploaded in the same way whether they contain a postgres database, a php application with its dependencies and application server, or Java build artifacts.
Infrastructure-agnostic
~~~~~~~~~~~~~~~~~~~~~~~~~~
Both types of containers are INFRASTRUCTURE-AGNOSTIC: they can be transported to thousands of facilities around the world, and manipulated by a wide variety of equipment. A shipping container can be packed in a factory in Ukraine, transported by truck to the nearest routing center, stacked onto a train, loaded into a German boat by an Australian-built crane, stored in a warehouse at a US facility, etc. Similarly, a standard container can be bundled on my laptop, uploaded to S3, downloaded, run and snapshotted by a build server at Equinix in Virginia, uploaded to 10 staging servers in a home-made Openstack cluster, then sent to 30 production instances across 3 EC2 regions.
Designed for automation
~~~~~~~~~~~~~~~~~~~~~~~~~~
Because they offer the same standard operations regardless of content and infrastructure, Standard Containers, just like their physical counterpart, are extremely well-suited for automation. In fact, you could say automation is their secret weapon.
Many things that once required time-consuming and error-prone human effort can now be programmed. Before shipping containers, a bag of powder coffee was hauled, dragged, dropped, rolled and stacked by 10 different people in 10 different locations by the time it reached its destination. 1 out of 50 disappeared. 1 out of 20 was damaged. The process was slow, inefficient and cost a fortune - and was entirely different depending on the facility and the type of goods.
Similarly, before Standard Containers, by the time a software component ran in production, it had been individually built, configured, bundled, documented, patched, vendored, templated, tweaked and instrumented by 10 different people on 10 different computers. Builds failed, libraries conflicted, mirrors crashed, post-it notes were lost, logs were misplaced, cluster updates were half-broken. The process was slow, inefficient and cost a fortune - and was entirely different depending on the language and infrastructure provider.
Industrial-grade delivery
~~~~~~~~~~~~~~~~~~~~~~~~~~
There are 17 million shipping containers in existence, packed with every physical good imaginable. Every single one of them can be loaded on the same boats, by the same cranes, in the same facilities, and sent anywhere in the World with incredible efficiency. It is embarrassing to think that a 30 ton shipment of coffee can safely travel half-way across the World in *less time* than it takes a software team to deliver its code from one datacenter to another sitting 10 miles away.
With Standard Containers we can put an end to that embarrassment, by making INDUSTRIAL-GRADE DELIVERY of software a reality.
Standard Container Specification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(TODO)
Image format
~~~~~~~~~~~~
Standard operations
~~~~~~~~~~~~~~~~~~~
- Copy
- Run
- Stop
- Wait
- Commit
- Attach standard streams
- List filesystem changes
- ...
Execution environment
~~~~~~~~~~~~~~~~~~~~~
Root filesystem
^^^^^^^^^^^^^^^
Environment variables
^^^^^^^^^^^^^^^^^^^^^
Process arguments
^^^^^^^^^^^^^^^^^
Networking
^^^^^^^^^^
Process namespacing
^^^^^^^^^^^^^^^^^^^
Resource limits
^^^^^^^^^^^^^^^
Process monitoring
^^^^^^^^^^^^^^^^^^
Logging
^^^^^^^
Signals
^^^^^^^
Pseudo-terminal allocation
^^^^^^^^^^^^^^^^^^^^^^^^^^
Security
^^^^^^^^
-17
View File
@@ -1,17 +0,0 @@
:title: docker documentation
:description: -- todo: change me
:keywords: todo: change me
Concepts
========
Contents:
.. toctree::
:maxdepth: 1
introduction
buildingblocks
-127
View File
@@ -1,127 +0,0 @@
:title: Introduction
:description: An introduction to docker and standard containers?
:keywords: containers, lxc, concepts, explanation
Introduction
============
Docker - The Linux container runtime
------------------------------------
Docker complements LXC with a high-level API which operates at the process level. It runs unix processes with strong guarantees of isolation and repeatability across servers.
Docker is a great building block for automating distributed systems: large-scale web deployments, database clusters, continuous deployment systems, private PaaS, service-oriented architectures, etc.
- **Heterogeneous payloads** Any combination of binaries, libraries, configuration files, scripts, virtualenvs, jars, gems, tarballs, you name it. No more juggling between domain-specific tools. Docker can deploy and run them all.
- **Any server** Docker can run on any x64 machine with a modern linux kernel - whether it's a laptop, a bare metal server or a VM. This makes it perfect for multi-cloud deployments.
- **Isolation** docker isolates processes from each other and from the underlying host, using lightweight containers.
- **Repeatability** Because containers are isolated in their own filesystem, they behave the same regardless of where, when, and alongside what they run.
.. image:: http://www.docker.io/_static/lego_docker.jpg
What is a Standard Container?
-----------------------------
Docker defines a unit of software delivery called a Standard Container. The goal of a Standard Container is to encapsulate a software component and all its dependencies in
a format that is self-describing and portable, so that any compliant runtime can run it without extra dependency, regardless of the underlying machine and the contents of the container.
The spec for Standard Containers is currently work in progress, but it is very straightforward. It mostly defines 1) an image format, 2) a set of standard operations, and 3) an execution environment.
A great analogy for this is the shipping container. Just like Standard Containers are a fundamental unit of software delivery, shipping containers (http://bricks.argz.com/ins/7823-1/12) are a fundamental unit of physical delivery.
Standard operations
~~~~~~~~~~~~~~~~~~~
Just like shipping containers, Standard Containers define a set of STANDARD OPERATIONS. Shipping containers can be lifted, stacked, locked, loaded, unloaded and labelled. Similarly, standard containers can be started, stopped, copied, snapshotted, downloaded, uploaded and tagged.
Content-agnostic
~~~~~~~~~~~~~~~~~~~
Just like shipping containers, Standard Containers are CONTENT-AGNOSTIC: all standard operations have the same effect regardless of the contents. A shipping container will be stacked in exactly the same way whether it contains Vietnamese powder coffee or spare Maserati parts. Similarly, Standard Containers are started or uploaded in the same way whether they contain a postgres database, a php application with its dependencies and application server, or Java build artifacts.
Infrastructure-agnostic
~~~~~~~~~~~~~~~~~~~~~~~~~~
Both types of containers are INFRASTRUCTURE-AGNOSTIC: they can be transported to thousands of facilities around the world, and manipulated by a wide variety of equipment. A shipping container can be packed in a factory in Ukraine, transported by truck to the nearest routing center, stacked onto a train, loaded into a German boat by an Australian-built crane, stored in a warehouse at a US facility, etc. Similarly, a standard container can be bundled on my laptop, uploaded to S3, downloaded, run and snapshotted by a build server at Equinix in Virginia, uploaded to 10 staging servers in a home-made Openstack cluster, then sent to 30 production instances across 3 EC2 regions.
Designed for automation
~~~~~~~~~~~~~~~~~~~~~~~~~~
Because they offer the same standard operations regardless of content and infrastructure, Standard Containers, just like their physical counterpart, are extremely well-suited for automation. In fact, you could say automation is their secret weapon.
Many things that once required time-consuming and error-prone human effort can now be programmed. Before shipping containers, a bag of powder coffee was hauled, dragged, dropped, rolled and stacked by 10 different people in 10 different locations by the time it reached its destination. 1 out of 50 disappeared. 1 out of 20 was damaged. The process was slow, inefficient and cost a fortune - and was entirely different depending on the facility and the type of goods.
Similarly, before Standard Containers, by the time a software component ran in production, it had been individually built, configured, bundled, documented, patched, vendored, templated, tweaked and instrumented by 10 different people on 10 different computers. Builds failed, libraries conflicted, mirrors crashed, post-it notes were lost, logs were misplaced, cluster updates were half-broken. The process was slow, inefficient and cost a fortune - and was entirely different depending on the language and infrastructure provider.
Industrial-grade delivery
~~~~~~~~~~~~~~~~~~~~~~~~~~
There are 17 million shipping containers in existence, packed with every physical good imaginable. Every single one of them can be loaded on the same boats, by the same cranes, in the same facilities, and sent anywhere in the World with incredible efficiency. It is embarrassing to think that a 30 ton shipment of coffee can safely travel half-way across the World in *less time* than it takes a software team to deliver its code from one datacenter to another sitting 10 miles away.
With Standard Containers we can put an end to that embarrassment, by making INDUSTRIAL-GRADE DELIVERY of software a reality.
Standard Container Specification
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(TODO)
Image format
~~~~~~~~~~~~
Standard operations
~~~~~~~~~~~~~~~~~~~
- Copy
- Run
- Stop
- Wait
- Commit
- Attach standard streams
- List filesystem changes
- ...
Execution environment
~~~~~~~~~~~~~~~~~~~~~
Root filesystem
^^^^^^^^^^^^^^^
Environment variables
^^^^^^^^^^^^^^^^^^^^^
Process arguments
^^^^^^^^^^^^^^^^^
Networking
^^^^^^^^^^
Process namespacing
^^^^^^^^^^^^^^^^^^^
Resource limits
^^^^^^^^^^^^^^^
Process monitoring
^^^^^^^^^^^^^^^^^^
Logging
^^^^^^^
Signals
^^^^^^^
Pseudo-terminal allocation
^^^^^^^^^^^^^^^^^^^^^^^^^^
Security
^^^^^^^^
-247
View File
@@ -1,247 +0,0 @@
# -*- coding: utf-8 -*-
#
# Docker documentation build configuration file, created by
# sphinx-quickstart on Tue Mar 19 12:34:07 2013.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
#disable the parmalinks on headers, I find them really annoying
html_add_permalinks = None
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'Docker'
copyright = u'2013, Team Docker'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.1'
# The full version, including alpha/beta/rc tags.
release = '0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'docker'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
html_theme_path = ['../theme']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['static_files']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
html_show_sourcelink = False
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Dockerdoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Docker.tex', u'Docker Documentation',
u'Team Docker', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'docker', u'Docker Documentation',
[u'Team Docker'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Docker', u'Docker Documentation',
u'Team Docker', 'Docker', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
-101
View File
@@ -1,101 +0,0 @@
Contributing to Docker
======================
Want to hack on Docker? Awesome! There are instructions to get you
started on the website: http://docker.io/gettingstarted.html
They are probably not perfect, please let us know if anything feels
wrong or incomplete.
Contribution guidelines
-----------------------
Pull requests are always welcome
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We are always thrilled to receive pull requests, and do our best to
process them as fast as possible. Not sure if that typo is worth a pull
request? Do it! We will appreciate it.
If your pull request is not accepted on the first try, don't be
discouraged! If there's a problem with the implementation, hopefully you
received feedback on what to improve.
We're trying very hard to keep Docker lean and focused. We don't want it
to do everything for everybody. This means that we might decide against
incorporating a new feature. However, there might be a way to implement
that feature *on top of* docker.
Discuss your design on the mailing list
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We recommend discussing your plans `on the mailing
list <https://groups.google.com/forum/?fromgroups#!forum/docker-club>`__
before starting to code - especially for more ambitious contributions.
This gives other contributors a chance to point you in the right
direction, give feedback on your design, and maybe point out if someone
else is working on the same thing.
Create issues...
~~~~~~~~~~~~~~~~
Any significant improvement should be documented as `a github
issue <https://github.com/dotcloud/docker/issues>`__ before anybody
starts working on it.
...but check for existing issues first!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please take a moment to check that an issue doesn't already exist
documenting your bug report or improvement proposal. If it does, it
never hurts to add a quick "+1" or "I have this problem too". This will
help prioritize the most common problems and requests.
Conventions
~~~~~~~~~~~
Fork the repo and make changes on your fork in a feature branch:
- If it's a bugfix branch, name it XXX-something where XXX is the number of the
issue
- If it's a feature branch, create an enhancement issue to announce your
intentions, and name it XXX-something where XXX is the number of the issue.
Submit unit tests for your changes. Go has a great test framework built in; use
it! Take a look at existing tests for inspiration. Run the full test suite on
your branch before submitting a pull request.
Make sure you include relevant updates or additions to documentation when
creating or modifying features.
Write clean code. Universally formatted code promotes ease of writing, reading,
and maintenance. Always run ``go fmt`` before committing your changes. Most
editors have plugins that do this automatically, and there's also a git
pre-commit hook:
.. code-block:: bash
curl -o .git/hooks/pre-commit https://raw.github.com/edsrzf/gofmt-git-hook/master/fmt-check && chmod +x .git/hooks/pre-commit
Pull requests descriptions should be as clear as possible and include a
reference to all the issues that they address.
Code review comments may be added to your pull request. Discuss, then make the
suggested modifications and push additional commits to your feature branch. Be
sure to post a comment after pushing. The new commits will show up in the pull
request automatically, but the reviewers will not be notified unless you
comment.
Before the pull request is merged, make sure that you squash your commits into
logical units of work using ``git rebase -i`` and ``git push -f``. After every
commit the test suite should be passing. Include documentation changes in the
same commit so that a revert would remove all traces of the feature or fix.
Commits that fix or close an issue should include a reference like ``Closes #XXX``
or ``Fixes #XXX``, which will automatically close the issue when merged.
Add your name to the AUTHORS file, but make sure the list is sorted and your
name and email address match your git configuration. The AUTHORS file is
regenerated occasionally from the git commit history, so a mismatch may result
in your changes being overwritten.
@@ -1,33 +0,0 @@
:title: Setting up a dev environment
:description: Guides on how to contribute to docker
:keywords: Docker, documentation, developers, contributing, dev environment
Setting up a dev environment
============================
Instructions that have been verified to work on Ubuntu 12.10,
.. code-block:: bash
sudo apt-get -y install lxc wget bsdtar curl golang git
export GOPATH=~/go/
export PATH=$GOPATH/bin:$PATH
mkdir -p $GOPATH/src/github.com/dotcloud
cd $GOPATH/src/github.com/dotcloud
git clone git@github.com:dotcloud/docker.git
cd docker
go get -v github.com/dotcloud/docker/...
go install -v github.com/dotcloud/docker/...
Then run the docker daemon,
.. code-block:: bash
sudo $GOPATH/bin/docker -d
Run the ``go install`` command (above) to recompile docker.
-14
View File
@@ -1,14 +0,0 @@
:title: Contributing to Docker
:description: Guides on how to contribute to docker
:keywords: Docker, documentation, developers, contributing, dev environment
Contributing
============
.. toctree::
:maxdepth: 1
contributing
devenvironment
-2
View File
@@ -1,2 +0,0 @@
www:
type: static
-48
View File
@@ -1,48 +0,0 @@
:title: Hello world example
:description: A simple hello world example with Docker
:keywords: docker, example, hello world
.. _hello_world:
Hello World
===========
This is the most basic example available for using Docker. The example assumes you have Docker installed.
Download the base container
.. code-block:: bash
# Download a base image
docker pull base
The *base* image is a minimal *ubuntu* based container, alternatively you can select *busybox*, a bare
minimal linux system. The images are retrieved from the docker repository.
.. code-block:: bash
#run a simple echo command, that will echo hello world back to the console over standard out.
docker run base /bin/echo hello world
**Explanation:**
- **"docker run"** run a command in a new container
- **"base"** is the image we want to run the command inside of.
- **"/bin/echo"** is the command we want to run in the container
- **"hello world"** is the input for the echo command
**Video:**
See the example in action
.. raw:: html
<div style="margin-top:10px;">
<iframe width="560" height="350" src="http://ascii.io/a/2603/raw" frameborder="0"></iframe>
</div>
Continue to the :ref:`hello_world_daemon` example.
@@ -1,81 +0,0 @@
:title: Hello world daemon example
:description: A simple hello world daemon example with Docker
:keywords: docker, example, hello world, daemon
.. _hello_world_daemon:
Hello World Daemon
==================
The most boring daemon ever written.
This example assumes you have Docker installed and with the base image already imported ``docker pull base``.
We will use the base image to run a simple hello world daemon that will just print hello world to standard
out every second. It will continue to do this until we stop it.
**Steps:**
.. code-block:: bash
CONTAINER_ID=$(docker run -d base /bin/sh -c "while true; do echo hello world; sleep 1; done")
We are going to run a simple hello world daemon in a new container made from the busybox daemon.
- **"docker run -d "** run a command in a new container. We pass "-d" so it runs as a daemon.
- **"base"** is the image we want to run the command inside of.
- **"/bin/sh -c"** is the command we want to run in the container
- **"while true; do echo hello world; sleep 1; done"** is the mini script we want to run, that will just print hello world once a second until we stop it.
- **$CONTAINER_ID** the output of the run command will return a container id, we can use in future commands to see what is going on with this process.
.. code-block:: bash
docker logs $CONTAINER_ID
Check the logs make sure it is working correctly.
- **"docker logs**" This will return the logs for a container
- **$CONTAINER_ID** The Id of the container we want the logs for.
.. code-block:: bash
docker attach $CONTAINER_ID
Attach to the container to see the results in realtime.
- **"docker attach**" This will allow us to attach to a background process to see what is going on.
- **$CONTAINER_ID** The Id of the container we want to attach too.
.. code-block:: bash
docker ps
Check the process list to make sure it is running.
- **"docker ps"** this shows all running process managed by docker
.. code-block:: bash
docker stop $CONTAINER_ID
Stop the container, since we don't need it anymore.
- **"docker stop"** This stops a container
- **$CONTAINER_ID** The Id of the container we want to stop.
.. code-block:: bash
docker ps
Make sure it is really stopped.
**Video:**
See the example in action
.. raw:: html
<div style="margin-top:10px;">
<iframe width="560" height="350" src="http://ascii.io/a/2562/raw" frameborder="0"></iframe>
</div>
Continue to the :ref:`python_web_app` example.
-18
View File
@@ -1,18 +0,0 @@
:title: Docker Examples
:description: Examples on how to use Docker
:keywords: docker, hello world, examples
Examples
============
Contents:
.. toctree::
:maxdepth: 1
hello_world
hello_world_daemon
python_web_app
runningsshservice
-70
View File
@@ -1,70 +0,0 @@
:title: Python Web app example
:description: Building your own python web app using docker
:keywords: docker, example, python, web app
.. _python_web_app:
Building a python web app
=========================
The goal of this example is to show you how you can author your own docker images using a parent image, making changes to it, and then saving the results as a new image. We will do that by making a simple hello flask web application image.
**Steps:**
.. code-block:: bash
docker pull shykes/pybuilder
We are downloading the "shykes/pybuilder" docker image
.. code-block:: bash
URL=http://github.com/shykes/helloflask/archive/master.tar.gz
We set a URL variable that points to a tarball of a simple helloflask web app
.. code-block:: bash
BUILD_JOB=$(docker run -d -t shykes/pybuilder:latest /usr/local/bin/buildapp $URL)
Inside of the "shykes/pybuilder" image there is a command called buildapp, we are running that command and passing the $URL variable from step 2 to it, and running the whole thing inside of a new container. BUILD_JOB will be set with the new container_id.
.. code-block:: bash
docker attach $BUILD_JOB
[...]
We attach to the new container to see what is going on. Ctrl-C to disconnect
.. code-block:: bash
BUILD_IMG=$(docker commit $BUILD_JOB _/builds/github.com/hykes/helloflask/master)
Save the changed we just made in the container to a new image called "_/builds/github.com/hykes/helloflask/master" and save the image id in the BUILD_IMG variable name.
.. code-block:: bash
WEB_WORKER=$(docker run -d -p 5000 $BUILD_IMG /usr/local/bin/runapp)
Use the new image we just created and create a new container with network port 5000, and return the container id and store in the WEB_WORKER variable.
.. code-block:: bash
docker logs $WEB_WORKER
* Running on http://0.0.0.0:5000/
view the logs for the new container using the WEB_WORKER variable, and if everything worked as planned you should see the line "Running on http://0.0.0.0:5000/" in the log output.
**Video:**
See the example in action
.. raw:: html
<div style="margin-top:10px;">
<iframe width="720" height="350" src="http://ascii.io/a/2573/raw" frameborder="0"></iframe>
</div>
Continue to the `base commands`_
.. _base commands: ../commandline/basecommands.html
@@ -1,27 +0,0 @@
Create an ssh daemon service
============================
**Video:**
I've create a little screencast to show how to create a sshd service and connect to it. It is something like 11
minutes and not entirely smooth, but gives you a good idea.
.. raw:: html
<div style="margin-top:10px;">
<iframe width="800" height="400" src="http://ascii.io/a/2637/raw" frameborder="0"></iframe>
</div>
You can also get this sshd container by using
::
docker pull dhrp/sshd
The password is 'screencast'
-47
View File
@@ -1,47 +0,0 @@
FAQ
===
Most frequently asked questions.
--------------------------------
1. **How much does Docker cost?**
Docker is 100% free, it is open source, so you can use it without paying.
2. **What open source license are you using?**
We are using the Apache License Version 2.0, see it here: https://github.com/dotcloud/docker/blob/master/LICENSE
3. **Does Docker run on Mac OS X or Windows?**
Not at this time, Docker currently only runs on Linux, but you can use VirtualBox to run Docker in a virtual machine on your box, and get the best of both worlds. Check out the MacOSX_ and Windows_ intallation guides.
4. **How do containers compare to virtual machines?**
They are complementary. VMs are best used to allocate chunks of hardware resources. Containers operate at the process level, which makes them very lightweight and perfect as a unit of software delivery.
5. **Can I help by adding some questions and answers?**
Definitely! You can fork `the repo`_ and edit the documentation sources.
42. **Where can I find more answers?**
You can find more answers on:
* `IRC: docker on freenode`_
* `Github`_
* `Ask questions on Stackoverflow`_
* `Join the conversation on Twitter`_
.. _Windows: ../documentation/installation/windows.html
.. _MacOSX: ../documentation/installation/macos.html
.. _the repo: http://www.github.com/dotcloud/docker
.. _IRC\: docker on freenode: irc://chat.freenode.net#docker
.. _Github: http://www.github.com/dotcloud/docker
.. _Ask questions on Stackoverflow: http://stackoverflow.com/search?q=docker
.. _Join the conversation on Twitter: http://twitter.com/getdocker
Looking for something else to read? Checkout the :ref:`hello_world` example.
-204
View File
@@ -1,204 +0,0 @@
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js" xmlns="http://www.w3.org/1999/html" xmlns="http://www.w3.org/1999/html"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Docker - the Linux container runtime</title>
<meta name="description" content="Docker encapsulates heterogeneous payloads in standard containers">
<meta name="viewport" content="width=device-width">
<!-- twitter bootstrap -->
<link rel="stylesheet" href="../_static/css/bootstrap.min.css">
<link rel="stylesheet" href="../_static/css/bootstrap-responsive.min.css">
<!-- main style file -->
<link rel="stylesheet" href="../_static/css/main.css">
<!-- vendor scripts -->
<script src="../_static/js/vendor/jquery-1.9.1.min.js" type="text/javascript" ></script>
<script src="../_static/js/vendor/modernizr-2.6.2-respond-1.1.0.min.js" type="text/javascript" ></script>
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-dotcloud">
<div class="container" style="text-align: center;">
<div style="float: right" class="pull-right">
<ul class="nav">
<li><a href="../">Introduction</a></li>
<li class="active"><a href="./">Getting started</a></li>
<li class=""><a href="http://docs.docker.io/en/latest/concepts/containers/">Documentation</a></li>
</ul>
<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
</div>
<div style="margin-left: -12px; float: left;">
<a href="../index.html"><img style="margin-top: 12px; height: 38px" src="../_static/img/docker-letters-logo.gif"></a>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="span12 titlebar"><h1 class="pageheader">GETTING STARTED</h1>
</div>
</div>
</div>
<div class="container">
<div class="alert alert-info">
<strong>Docker is still under heavy development.</strong> It should not yet be used in production. Check <a href="http://github.com/dotcloud/docker">the repo</a> for recent progress.
</div>
<div class="row">
<div class="span6">
<section class="contentblock">
<h2>
<a name="installing-on-ubuntu-1204-and-1210" class="anchor" href="#installing-on-ubuntu-1204-and-1210"><span class="mini-icon mini-icon-link"></span>
</a>Installing on Ubuntu</h2>
<ol>
<li>
<p>Install dependencies:</p>
<div class="highlight">
<pre>sudo apt-get install lxc wget bsdtar curl</pre>
<pre>sudo apt-get install linux-image-extra-<span class="sb">`</span>uname -r<span class="sb">`</span></pre></div>
<p>The <code>linux-image-extra</code> package is needed on standard Ubuntu EC2 AMIs in order to install the aufs kernel module.</p>
</li>
<li>
<p>Install the latest docker binary:</p>
<div class="highlight">
<pre>wget http://get.docker.io/builds/<span class="k">$(</span>uname -s<span class="k">)</span>/<span class="k">$(</span>uname -m<span class="k">)</span>/docker-master.tgz</pre>
<pre>tar -xf docker-master.tgz</pre>
</div>
</li>
<li>
<p>Run your first container!</p>
<div class="highlight"><pre><span class="nb">cd </span>docker-master</pre>
<pre>sudo ./docker run -i -t base /bin/bash</pre>
</div>
<p>Done!</p>
<p>Consider adding docker to your <code>PATH</code> for simplicity.</p>
</li>
Continue with the <a href="http://docs.docker.io/en/latest/examples/hello_world/">Hello world</a> example.
</ol>
</section>
<section class="contentblock">
<h2>Contributing to Docker</h2>
<p>Want to hack on Docker? Awesome! We have some <a href="http://docs.docker.io/en/latest/contributing/contributing/">instructions to get you started</a>. They are probably not perfect, please let us know if anything feels wrong or incomplete.</p>
</section>
</div>
<div class="span6">
<section class="contentblock">
<h2>Quick install on other operating systems</h2>
<p><strong>For other operating systems we recommend and provide a streamlined install with virtualbox,
vagrant and an Ubuntu virtual machine.</strong></p>
<ul>
<li><a href="http://docs.docker.io/en/latest/installation/macos/">Mac OS X and other linuxes</a></li>
<li><a href="http://docs.docker.io/en/latest/installation/windows/">Windows</a></li>
</ul>
</section>
<section class="contentblock">
<h2>More resources</h2>
<ul>
<li><a href="irc://chat.freenode.net#docker">IRC: docker on freenode</a></li>
<li><a href="http://www.github.com/dotcloud/docker">Github</a></li>
<li><a href="http://stackoverflow.com/tags/docker/">Ask questions on Stackoverflow</a></li>
<li><a href="http://twitter.com/getdocker/">Join the conversation on Twitter</a></li>
</ul>
</section>
<section class="contentblock">
<div id="wufoo-z7x3p3">
Fill out my <a href="http://dotclouddocker.wufoo.com/forms/z7x3p3">online form</a>.
</div>
<script type="text/javascript">var z7x3p3;(function(d, t) {
var s = d.createElement(t), options = {
'userName':'dotclouddocker',
'formHash':'z7x3p3',
'autoResize':true,
'height':'577',
'async':true,
'header':'show'};
s.src = ('https:' == d.location.protocol ? 'https://' : 'http://') + 'wufoo.com/scripts/embed/form.js';
s.onload = s.onreadystatechange = function() {
var rs = this.readyState; if (rs) if (rs != 'complete') if (rs != 'loaded') return;
try { z7x3p3 = new WufooForm();z7x3p3.initialize(options);z7x3p3.display(); } catch (e) {}};
var scr = d.getElementsByTagName(t)[0], par = scr.parentNode; par.insertBefore(s, scr);
})(document, 'script');</script>
</section>
</div>
</div>
</div>
<div class="container">
<footer id="footer" class="footer">
<div class="row">
<div class="span12 social">
Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
</div>
</div>
<div class="row">
<div class="emptyspace" style="height: 40px">
</div>
</div>
</footer>
</div>
<!-- bootstrap javascipts -->
<script src="../_static/js/vendor/bootstrap.min.js" type="text/javascript"></script>
<!-- Google analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-6096819-11']);
_gaq.push(['_setDomainName', 'docker.io']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>
-289
View File
@@ -1,289 +0,0 @@
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!-->
<html class="no-js" xmlns="http://www.w3.org/1999/html"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Docker - the Linux container runtime</title>
<meta name="description" content="Docker encapsulates heterogeneous payloads in standard containers">
<meta name="viewport" content="width=device-width">
<!-- twitter bootstrap -->
<link rel="stylesheet" href="_static/css/bootstrap.min.css">
<link rel="stylesheet" href="_static/css/bootstrap-responsive.min.css">
<!-- main style file -->
<link rel="stylesheet" href="_static/css/main.css">
<!-- vendor scripts -->
<script src="_static/js/vendor/jquery-1.9.1.min.js" type="text/javascript" ></script>
<script src="_static/js/vendor/modernizr-2.6.2-respond-1.1.0.min.js" type="text/javascript" ></script>
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-dotcloud">
<div class="container" style="text-align: center;">
<div class="pull-right" >
<ul class="nav">
<li class="active"><a href="./">Introduction</a></li>
<li ><a href="gettingstarted/">Getting started</a></li>
<li class=""><a href="http://docs.docker.io/en/latest/concepts/containers/">Documentation</a></li>
</ul>
<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="text-center">
<img src="_static/img/docker-letters-logo.gif">
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="span12">
<section class="contentblock header">
<div class="span6" style="margin:10px 0px 0px 30px; float: right; ">
<div class="js-video" >
<iframe width="640" height="360" src="http://www.youtube.com/embed/wW9CAH9nSLs?feature=player_detailpage&rel=0&modestbranding=1&start=11" frameborder="0" allowfullscreen></iframe>
</div>
</div>
<div style="text-align: center; padding: 50px 30px 50px 30px;">
<h1>Docker</h1>
<h2>The Linux container runtime</h2>
</div>
<div style="display: block; text-align: center; padding: 10px 30px 50px 30px;">
<p>
Docker complements LXC with a high-level API which operates at the process level.
It runs unix processes with strong guarantees of isolation and repeatability across servers.
</p>
<p>
Docker is a great building block for automating distributed systems: large-scale web deployments, database clusters, continuous deployment systems, private PaaS, service-oriented architectures, etc.
</p>
</div>
<div style="display: block; text-align: center;">
<a class="btn btn-custom btn-large" href="gettingstarted/">Let's get started</a>
</div>
<br style="clear: both"/>
</section>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="span3">
<section class="contentblock">
<h4>Heterogeneous payloads</h4>
<p>Any combination of binaries, libraries, configuration files, scripts, virtualenvs, jars, gems, tarballs, you name it. No more juggling between domain-specific tools. Docker can deploy and run them all.</p>
</section>
</div>
<div class="span3">
<section class="contentblock">
<h4>Any server</h4>
<p>Docker can run on any x64 machine with a modern linux kernel - whether it's a laptop, a bare metal server or a VM. This makes it perfect for multi-cloud deployments.</p>
</section>
</div>
<div class="span3">
<section class="contentblock">
<h4>Isolation</h4>
<p>docker isolates processes from each other and from the underlying host, using lightweight containers.</p>
</section>
</div>
<div class="span3">
<section class="contentblock">
<h4>Repeatability</h4>
<p>Because containers are isolated in their own filesystem, they behave the same regardless of where, when, and alongside what they run.</p>
</section>
</div>
</div>
</div>
<style>
.twitterblock {
min-height: 75px;
}
.twitterblock img {
float: left;
margin-right: 10px;
}
</style>
<div class="container">
<div class="row">
<div class="span6">
<section class="contentblock twitterblock">
<img src="https://twimg0-a.akamaihd.net/profile_images/2491994496/rbevyyq6ykp6bnoby2je_bigger.jpeg">
<em>John Willis @botchagalupe:</em> IMHO docker is to paas what chef was to Iaas 4 years ago
</section>
</div>
<div class="span6">
<section class="contentblock twitterblock">
<img src="https://twimg0-a.akamaihd.net/profile_images/3348427561/9d7f08f1e103a16c8debd169301b9944_bigger.jpeg">
<em>John Feminella @superninjarobot:</em> So, @getdocker is pure excellence. If you've ever wished for arbitrary, PaaS-agnostic, lxc/aufs Linux containers, this is your jam!
</section>
</div>
</div>
<div class="row">
<div class="span6">
<section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/3408403010/4496ccdd14e9b7285eca04c31a740207_bigger.jpeg">
<em>David Romulan @destructuring:</em> I haven't had this much fun since AWS
</section>
</div>
<div class="span6">
<section class="contentblock twitterblock">
<img src="https://si0.twimg.com/profile_images/780893320/My_Avatar_bigger.jpg">
<em>Ricardo Gladwell @rgladwell:</em> wow @getdocker is either amazing or totally stupid
</section>
</div>
</div>
</div>
<!-- <p>Docker encapsulates heterogeneous payloads in <a href="#container">Standard Containers</a>, and runs them on any server with strong guarantees of isolation and repeatability.</p>
<p>It is a great building block for automating distributed systems: large-scale web deployments, database clusters, continuous deployment systems, private PaaS, service-oriented architectures, etc.</p>
-->
<div class="container">
<div class="row">
<div class="span6">
<section class="contentblock">
<!-- <img src="_static/lego_docker.jpg" width="600px" style="float:right; margin-left: 10px"> -->
<h2>Notable features</h2>
<ul>
<li>Filesystem isolation: each process container runs in a completely separate root filesystem.</li>
<li>Resource isolation: system resources like cpu and memory can be allocated differently to each process container, using cgroups.</li>
<li>Network isolation: each process container runs in its own network namespace, with a virtual interface and IP address of its own.</li>
<li>Copy-on-write: root filesystems are created using copy-on-write, which makes deployment extremeley fast, memory-cheap and disk-cheap.</li>
<li>Logging: the standard streams (stdout/stderr/stdin) of each process container is collected and logged for real-time or batch retrieval.</li>
<li>Change management: changes to a container's filesystem can be committed into a new image and re-used to create more containers. No templating or manual configuration required.</li>
<li>Interactive shell: docker can allocate a pseudo-tty and attach to the standard input of any container, for example to run a throwaway interactive shell.</li>
</ul>
<h2>Under the hood</h2>
<p>Under the hood, Docker is built on the following components:</p>
<ul>
<li>The <a href="http://blog.dotcloud.com/kernel-secrets-from-the-paas-garage-part-24-c">cgroup</a> and <a href="http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part">namespacing</a> capabilities of the Linux kernel;</li>
<li><a href="http://aufs.sourceforge.net/aufs.html">AUFS</a>, a powerful union filesystem with copy-on-write capabilities;</li>
<li>The <a href="http://golang.org">Go</a> programming language;</li>
<li><a href="http://lxc.sourceforge.net/">lxc</a>, a set of convenience scripts to simplify the creation of linux containers.</li>
</ul>
</section>
</div>
<div class="span6">
<section class="contentblock">
<div id="wufoo-z7x3p3">
Fill out my <a href="http://dotclouddocker.wufoo.com/forms/z7x3p3">online form</a>.
</div>
<script type="text/javascript">var z7x3p3;(function(d, t) {
var s = d.createElement(t), options = {
'userName':'dotclouddocker',
'formHash':'z7x3p3',
'autoResize':true,
'height':'577',
'async':true,
'header':'show'};
s.src = ('https:' == d.location.protocol ? 'https://' : 'http://') + 'wufoo.com/scripts/embed/form.js';
s.onload = s.onreadystatechange = function() {
var rs = this.readyState; if (rs) if (rs != 'complete') if (rs != 'loaded') return;
try { z7x3p3 = new WufooForm();z7x3p3.initialize(options);z7x3p3.display(); } catch (e) {}};
var scr = d.getElementsByTagName(t)[0], par = scr.parentNode; par.insertBefore(s, scr);
})(document, 'script');</script>
</section>
<section class="contentblock">
<h3 id="twitter">Twitter</h3>
<a class="twitter-timeline" href="https://twitter.com/getdocker" data-widget-id="312730839718957056">Tweets by @getdocker</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</section>
</div>
</div>
</div> <!-- end container -->
<div class="container">
<footer id="footer" class="footer">
<div class="row">
<div class="span12">
Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
</div>
</div>
<div class="row">
<div class="emptyspace" style="height: 40px">
</div>
</div>
</footer>
</div>
<!-- bootstrap javascipts -->
<script src="_static/js/vendor/bootstrap.min.js" type="text/javascript"></script>
<!-- Google analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-6096819-11']);
_gaq.push(['_setDomainName', 'docker.io']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>
-21
View File
@@ -1,21 +0,0 @@
:title: docker documentation
:description: docker documentation
:keywords:
Documentation
=============
This documentation has the following resources:
.. toctree::
:maxdepth: 1
concepts/index
installation/index
examples/index
contributing/index
commandline/index
faq
.. image:: http://www.docker.io/_static/lego_docker.jpg
-87
View File
@@ -1,87 +0,0 @@
Amazon EC2
==========
Please note this is a community contributed installation path. The only 'official' installation is using the :ref:`ubuntu_linux` installation path. This version
may be out of date because it depends on some binaries to be updated and published
Installation
------------
Docker can now be installed on Amazon EC2 with a single vagrant command. Vagrant 1.1 or higher is required.
1. Install vagrant from http://www.vagrantup.com/ (or use your package manager)
2. Install the vagrant aws plugin
::
vagrant plugin install vagrant-aws
3. Get the docker sources, this will give you the latest Vagrantfile and puppet manifests.
::
git clone https://github.com/dotcloud/docker.git
4. Check your AWS environment.
Create a keypair specifically for EC2, give it a name and save it to your disk. *I usually store these in my ~/.ssh/ folder*.
Check that your default security group has an inbound rule to accept SSH (port 22) connections.
5. Inform Vagrant of your settings
Vagrant will read your access credentials from your environment, so we need to set them there first. Make sure
you have everything on amazon aws setup so you can (manually) deploy a new image to EC2.
::
export AWS_ACCESS_KEY_ID=xxx
export AWS_SECRET_ACCESS_KEY=xxx
export AWS_KEYPAIR_NAME=xxx
export AWS_SSH_PRIVKEY=xxx
The environment variables are:
* ``AWS_ACCESS_KEY_ID`` - The API key used to make requests to AWS
* ``AWS_SECRET_ACCESS_KEY`` - The secret key to make AWS API requests
* ``AWS_KEYPAIR_NAME`` - The name of the keypair used for this EC2 instance
* ``AWS_SSH_PRIVKEY`` - The path to the private key for the named keypair, for example ``~/.ssh/docker.pem``
You can check if they are set correctly by doing something like
::
echo $AWS_ACCESS_KEY_ID
6. Do the magic!
::
vagrant up --provider=aws
If it stalls indefinitely on ``[default] Waiting for SSH to become available...``, Double check your default security
zone on AWS includes rights to SSH (port 22) to your container.
If you have an advanced AWS setup, you might want to have a look at the https://github.com/mitchellh/vagrant-aws
7. Connect to your machine
.. code-block:: bash
vagrant ssh
8. Your first command
Now you are in the VM, run docker
.. code-block:: bash
docker
Continue with the :ref:`hello_world` example.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

-19
View File
@@ -1,19 +0,0 @@
:title: docker documentation
:description: -- todo: change me
:keywords: todo: change me
Installation
============
Contents:
.. toctree::
:maxdepth: 1
ubuntulinux
macos
windows
amazon
upgrading
-66
View File
@@ -1,66 +0,0 @@
Mac OS X and other linux
========================
Please note this is a community contributed installation path. The only 'official' installation is using the :ref:`ubuntu_linux` installation path. This version
may be out of date because it depends on some binaries to be updated and published
Requirements
------------
We currently rely on some Ubuntu-linux specific packages, this will change in the future, but for now we provide a
streamlined path to install Virtualbox with a Ubuntu 12.10 image using Vagrant.
1. Install virtualbox from https://www.virtualbox.org/ (or use your package manager)
2. Install vagrant from http://www.vagrantup.com/ (or use your package manager)
3. Install git if you had not installed it before, check if it is installed by running
``git`` in a terminal window
We recommend having at least about 2Gb of free disk space and 2Gb RAM (or more).
Installation
------------
1. Fetch the docker sources
.. code-block:: bash
git clone https://github.com/dotcloud/docker.git
2. Run vagrant from the sources directory
.. code-block:: bash
vagrant up
Vagrant will:
* Download the Quantal64 base ubuntu virtual machine image from get.docker.io/
* Boot this image in virtualbox
Then it will use Puppet to perform an initial setup in this machine:
* Download & untar the most recent docker binary tarball to vagrant homedir.
* Debootstrap to /var/lib/docker/images/ubuntu.
* Install & run dockerd as service.
* Put docker in /usr/local/bin.
* Put latest Go toolchain in /usr/local/go.
You now have a Ubuntu Virtual Machine running with docker pre-installed.
To access the VM and use Docker, Run ``vagrant ssh`` from the same directory as where you ran
``vagrant up``. Vagrant will make sure to connect you to the correct VM.
.. code-block:: bash
vagrant ssh
Now you are in the VM, run docker
.. code-block:: bash
docker
Continue with the :ref:`hello_world` example.
-56
View File
@@ -1,56 +0,0 @@
.. _ubuntu_linux:
Ubuntu Linux
============
**Please note this project is currently under heavy development. It should not be used in production.**
Installing on Ubuntu 12.04 and 12.10
Right now, the officially supported distributions are:
Ubuntu 12.04 (precise LTS)
Ubuntu 12.10 (quantal)
Docker probably works on other distributions featuring a recent kernel, the AUFS patch, and up-to-date lxc. However this has not been tested.
Install dependencies:
---------------------
::
sudo apt-get install lxc wget bsdtar curl
sudo apt-get install linux-image-extra-`uname -r`
The linux-image-extra package is needed on standard Ubuntu EC2 AMIs in order to install the aufs kernel module.
Install the latest docker binary:
::
wget http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz
tar -xf docker-master.tgz
Run your first container!
::
cd docker-master
::
sudo ./docker run -i -t base /bin/bash
To run docker as a daemon, in the background, and allow non-root users to run ``docker`` start
docker -d
::
sudo ./docker -d &
Consider adding docker to your PATH for simplicity.
Continue with the :ref:`hello_world` example.
-40
View File
@@ -1,40 +0,0 @@
.. _upgrading:
Upgrading
============
We assume you are upgrading from within the operating system which runs your docker daemon.
Get the latest docker binary:
::
wget http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz
Unpack it to your current dir
::
tar -xf docker-master.tgz
Stop your current daemon. How you stop your daemon depends on how you started it.
- If you started the daemon manually (``sudo docker -d``), you can just kill the process: ``killall docker``
- If the process was started using upstart (the ubuntu startup daemon), you may need to use that to stop it
Start docker in daemon mode (-d) and disconnect (&) starting ./docker will start the version in your current dir rather
than the one in your PATH.
Now start the daemon
::
sudo ./docker -d &
Alternatively you can replace the docker binary in ``/usr/local/bin``
-167
View File
@@ -1,167 +0,0 @@
:title: Requirements and Installation on Windows
:description: Docker's tutorial to run docker on Windows
:keywords: Docker, Docker documentation, Windows, requirements, virtualbox, vagrant, git, ssh, putty, cygwin
Windows
=========
Please note this is a community contributed installation path. The only 'official' installation is using the :ref:`ubuntu_linux` installation path. This version
may be out of date because it depends on some binaries to be updated and published
Requirements
------------
1. Install virtualbox from https://www.virtualbox.org - or follow this tutorial__
.. __: http://www.slideshare.net/julienbarbier42/install-virtualbox-on-windows-7
2. Install vagrant from http://www.vagrantup.com - or follow this tutorial__
.. __: http://www.slideshare.net/julienbarbier42/install-vagrant-on-windows-7
3. Install git with ssh from http://git-scm.com/downloads - or follow this tutorial__
.. __: http://www.slideshare.net/julienbarbier42/install-git-with-ssh-on-windows-7
We recommend having at least 2Gb of free disk space and 2Gb of RAM (or more).
Opening a command prompt
------------------------
First open a cmd prompt. Press Windows key and then press “R” key. This will open the RUN dialog box for you. Type “cmd” and press Enter. Or you can click on Start, type “cmd” in the “Search programs and files” field, and click on cmd.exe.
.. image:: images/win/_01.gif
:alt: Git install
:align: center
This should open a cmd prompt window.
.. image:: images/win/_02.gif
:alt: run docker
:align: center
Alternatively, you can also use a Cygwin terminal, or Git Bash (or any other command line program you are usually using). The next steps would be the same.
Launch an Ubuntu virtual server
-------------------------------
Lets download and run an Ubuntu image with docker binaries already installed.
.. code-block:: bash
git clone https://github.com/dotcloud/docker.git
cd docker
vagrant up
.. image:: images/win/run_02_.gif
:alt: run docker
:align: center
Congratulations! You are running an Ubuntu server with docker installed on it. You do not see it though, because it is running in the background.
Log onto your Ubuntu server
---------------------------
Lets log into your Ubuntu server now. To do so you have two choices:
- Use Vagrant on Windows command prompt OR
- Use SSH
Using Vagrant on Windows Command Prompt
```````````````````````````````````````
Run the following command
.. code-block:: bash
vagrant ssh
You may see an error message starting with “`ssh` executable not found”. In this case it means that you do not have SSH in your PATH. If you do not have SSH in your PATH you can set it up with the “set” command. For instance, if your ssh.exe is in the folder named “C:\Program Files (x86)\Git\bin”, then you can run the following command:
.. code-block:: bash
set PATH=%PATH%;C:\Program Files (x86)\Git\bin
.. image:: images/win/run_03.gif
:alt: run docker
:align: center
Using SSH
`````````
First step is to get the IP and port of your Ubuntu server. Simply run:
.. code-block:: bash
vagrant ssh-config
You should see an output with HostName and Port information. In this example, HostName is 127.0.0.1 and port is 2222. And the User is “vagrant”. The password is not shown, but it is also “vagrant”.
.. image:: images/win/ssh-config.gif
:alt: run docker
:align: center
You can now use this information for connecting via SSH to your server. To do so you can:
- Use putty.exe OR
- Use SSH from a terminal
Use putty.exe
'''''''''''''
You can download putty.exe from this page http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
Launch putty.exe and simply enter the information you got from last step.
.. image:: images/win/putty.gif
:alt: run docker
:align: center
Open, and enter user = vagrant and password = vagrant.
.. image:: images/win/putty_2.gif
:alt: run docker
:align: center
SSH from a terminal
'''''''''''''''''''
You can also run this command on your favorite terminal (windows prompt, cygwin, git-bash, …). Make sure to adapt the IP and port from what you got from the vagrant ssh-config command.
.. code-block:: bash
ssh vagrant@127.0.0.1 p 2222
Enter user = vagrant and password = vagrant.
.. image:: images/win/cygwin.gif
:alt: run docker
:align: center
Congratulations, you are now logged onto your Ubuntu Server, running on top of your Windows machine !
Running Docker
--------------
First you have to be root in order to run docker. Simply run the following command:
.. code-block:: bash
sudo su
You are now ready for the dockers “hello world” example. Run
.. code-block:: bash
docker run busybox echo hello world
.. image:: images/win/run_04.gif
:alt: run docker
:align: center
All done!
Now you can continue with the :ref:`hello_world` example.
-11
View File
@@ -1,11 +0,0 @@
Static files dir
================
Files you put in /sources/static_files/ will be copied to the web visible /_static/
Be careful not to override pre-existing static files from the template.
Generally, layout related files should go in the /theme directory.
If you want to add images to your particular documentation page. Just put them next to
your .rst source file and reference them relatively.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

-239
View File
@@ -1,239 +0,0 @@
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Docker - {{ meta['title'] if meta and meta['title'] else title }}</title>
<meta name="description" content="{{ meta['description'] if meta }}" />
<meta name="keywords" content="{{ meta['keywords'] if meta }}" />
{%- set url_root = pathto('', 1) %}
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
<script type="text/javascript">
// This is probably used by the search engine
var DOCUMENTATION_OPTIONS = {
URL_ROOT: '{{ url_root }}',
VERSION: '{{ release|e }}',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
HAS_SOURCE: {{ has_source|lower }}
};
</script>
{%- set css_files = css_files + ['_static/css/bootstrap.css'] %}
{%- set css_files = css_files + ['_static/css/bootstrap-responsive.css'] %}
{%- set css_files = css_files + ['_static/pygments.css'] %}
{%- set css_files = css_files + ['_static/css/main.css'] %}
{%- set script_files =
['https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.js']
+ ['https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.12/jquery-ui.min.js']
+ script_files
%}
{%- set script_files = script_files + ['_static/js/docs.js'] %}
{%- for cssfile in css_files %}
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
{%- endfor %}
{%- for scriptfile in script_files if scriptfile != '_static/jquery.js' %}
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
{%- endfor %}
{%- if favicon %}
<link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
{%- endif %}
{%- block extrahead %}{% endblock %}
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-dotcloud">
<div class="container" style="text-align: center;">
<div style="float: right" class="pull-right">
<ul class="nav">
<li><a href="http://www.docker.io/">Introduction</a></li>
<li><a href="http://www.docker.io/gettingstarted/">Getting started</a></li>
<li class="active"><a href="{{ pathto('concepts/containers/', 1) }}">Documentation</a></li>
</ul>
<div class="social links" style="float: right; margin-top: 14px; margin-left: 12px">
<a class="twitter" href="http://twitter.com/getdocker">Twitter</a>
<a class="github" href="https://github.com/dotcloud/docker/">GitHub</a>
</div>
</div>
<div style="margin-left: -12px; float: left;">
<a href="{{ pathto('./', 1) }}"><img style="margin-top: 12px; height: 38px" src="{{ pathto('_static/img/docker-letters-logo.gif', 1) }}"></a>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="span12 titlebar"><h1 class="pageheader">DOCUMENTATION</h1>
<!--<span class="pull-right" style="margin-left: 20px; font-size: 20px">{{version}}</span>-->
</div>
</div>
</div>
<div class="container">
<!-- Docs nav
================================================== -->
<div class="row" style="position: relative">
<div class="span3" style="height:100%;" >
</div>
<div class="span3 sidebar bs-docs-sidebar" style="position: absolute">
{{ toctree(collapse=False, maxdepth=3) }}
</div>
<!-- body block -->
<div class="span9">
<!-- Main section
================================================== -->
<section id="global" class="containerblock">
{% block body %}{% endblock %}
</section>
</div>
</div>
</div>
<div id="footer" class="container" >
<div class="row">
<div class="span12 footer">
Docker is a project by <a href="http://www.dotcloud.com">dotCloud</a>
{# {%- if show_source and has_source and sourcename %}#}
{# ·#}
{# <a href="{{ pathto('_sources/' + sourcename, true)|e }}"#}
{# rel="nofollow">View the RST source of this page</a>#}
{# {%- endif %}#}
{# {%- if pagename != "search" %}#}
{#TODO: Make a proper location for the search #}
{# Search:#}
{# <form#}
{# style="display: inline;"#}
{# class="search" action="{{ pathto('search') }}" method="get">#}
{# <input type="text" name="q" size="18" />#}
{# <input type="hidden" name="check_keywords" value="yes" />#}
{# <input type="hidden" name="area" value="default" />#}
{# </form>#}
{# {%- endif %}#}
{##}
{# <div class="links" style="float: right;">#}
{# <a class="twitter" href="http://twitter.com/getdocker">Twitter</a>#}
{# <a class="github" href="http://github.com/dotcloud/docker/">GitHub</a>#}
{# </div>#}
</div>
</div>
</div>
<!-- script which should be loaded after everything else -->
<script type="text/javascript">
var shiftWindow = function() {
scrollBy(0, -70);
console.log("window shifted")
};
window.addEventListener("hashchange", shiftWindow);
function loadShift() {
if (window.location.hash) {
console.log("window has hash");
shiftWindow();
}
}
$(window).load(function() {
loadShift();
console.log("late loadshift");
});
$(function(){
// sidebar accordian-ing
// don't apply on last object (it should be the FAQ)
var elements = $('.toctree-l2');
for (var i = 0; i < elements.length; i += 1) { var current = $(elements[i]); console.log(current); current.children('ul').hide();}
// set initial collapsed state
var elements = $('.toctree-l1');
for (var i = 0; i < elements.length; i += 1) {
var current = $(elements[i]);
if (current.hasClass('current')) {
// do nothing
} else {
// collapse children
current.children('ul').hide();
}
}
// attached handler on click
$('.sidebar > ul > li > a').not(':last').click(function(){
if ($(this).parent().hasClass('current')) {
$(this).parent().children('ul').slideUp(200, function() {
$(this).parent().removeClass('current'); // toggle after effect
});
} else {
//$('.sidebar > ul > li > ul').slideUp(100);
var current = $(this);
setTimeout(function() {
$('.sidebar > ul > li').removeClass('current');
current.parent().addClass('current'); // toggle before effect
current.parent().children('ul').hide();
current.parent().children('ul').slideDown(200);
}, 100);
}
return false;
});
});
</script>
<!-- Google analytics -->
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-6096819-11']);
_gaq.push(['_setDomainName', 'docker.io']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
-311
View File
@@ -1,311 +0,0 @@
/* ==========================================================================
Author's custom styles
========================================================================== */
.red {
background-color: red;
}
.blue {
background-color: blue;
}
.orange {
background-color: orange;
}
.gray {
background-color: grey;
}
body {
padding-top: 58px;
font-family: Arial, Helvetica, sans-serif;
}
h1,
h2,
h3,
h4 {
font-family: Arial, Helvetica, sans-serif;
font-weight: 900;
}
/* ===================
Top navigation
===================== */
.navbar {
z-index: 999;
background-color: white;
}
.navbar .nav li a {
padding: 22px 15px 22px;
}
.navbar .brand {
padding: 13px 10px 13px 28px ;
}
.navbar-dotcloud .container {
border-bottom: 2px #000000 solid;
}
/*
* Responsive YouTube, Vimeo, Embed, and HTML5 Videos with CSS
* http://www.jonsuh.com
*
* Copyright (c) 2012 Jonathan Suh
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
.js-video {
height: 0;
padding-top: 25px;
padding-bottom: 67.5%;
margin-bottom: 10px;
position: relative;
overflow: hidden;
}
.js-video.vimeo {
padding-top: 0;
}
.js-video.widescreen {
padding-bottom: 57.25%;
}
.js-video embed,
.js-video iframe,
.js-video object,
.js-video video {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
}
/* Responsive */
@media (max-width: 767px) {
.js-video {
padding-top: 0;
}
}
/* button style from http://charliepark.org/bootstrap_buttons/ */
.btn-custom {
background-color: #292929 !important;
background-repeat: repeat-x;
filter: progid:dximagetransform.microsoft.gradient(startColorstr="#515151", endColorstr="#282828");
background-image: -khtml-gradient(linear, left top, left bottom, from(#515151), to(#282828));
background-image: -moz-linear-gradient(top, #515151, #282828);
background-image: -ms-linear-gradient(top, #515151, #282828);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #515151), color-stop(100%, #282828));
background-image: -webkit-linear-gradient(top, #515151, #282828);
background-image: -o-linear-gradient(top, #515151, #282828);
background-image: linear-gradient(#515151, #282828);
border-color: #282828 #282828 #1f1f1f;
color: #fff !important;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.26);
-webkit-font-smoothing: antialiased;
}
/* ===================
Page title bar
===================== */
h1.pageheader {
color: #ffffff;
font-size: 20px;
font-family: "Arial Black", Tahoma, sans-serif;
margin: 8px;
margin-left: 22px;
}
/* ===================
Hero unit
===================== */
section.header {
margin-top: 0;
}
.hero-unit {
background-color: #292e33;
}
.hero-unit h5 {
color: #ffffff;
}
/* ===================
Main content layout
===================== */
.contentblock {
margin-top: 20px;
border-width: 3px;
background-color: #eeeeee;
box-sizing: content-box;
padding: 20px;
}
.section img {
margin: 15px 15px 15px 0;
border: 2px solid gray;
}
/* ===================
left navigation
===================== */
.dotcloudsidebar {
float: left;
height: 100%;
top: 0px;
bottom: 0px;
position: relative;
min-height: 100%;
margin-top: 78px;
margin-bottom: 22px;
}
.sidebar {
font-weight: normal;
float: left;
min-height: 475px;
background: #ececec;
border-left: 1px solid #bbbbbb;
border-right: 1px solid #cccccc;
position: relative;
}
.sidebar ul {
padding: 0px;
}
.sidebar ul li {
font-size: 14px;
list-style-type: none;
list-style-position: outside;
list-style-image: none;
margin-left: -25px;
padding: 0px;
}
.sidebar ul li a {
display: block;
color: #443331;
outline: 1px solid #dddddd;
padding: 12px 12px 10px 12px;
margin-top: 1px;
background-color: #d2d2d2;
}
.sidebar ul li .toctree-l1 {
font-size: larger;
}
.sidebar ul li .toctree-l1 a {
background-color: #dfdfdf;
}
.sidebar ul li .toctree-l1 .current {
font-weight: bold;
}
.sidebar ul li .toctree-l2 a {
padding-left: 18px;
background-color: #ffffff;
}
.sidebar ul li .toctree-l2 .current {
font-weight: bold;
}
.sidebar ul li .toctree-l3 {
font-size: smaller;
}
.sidebar ul li .toctree-l3 a {
padding-left: 36px;
background-color: #ffffff;
}
.sidebar ul li .toctree-l3 .current {
font-weight: bold;
}
.brand img {
height: 38px;
margin-left: -6px;
}
.border-box {
box-sizing: border-box;
padding: 20px;
background-color: #111188;
color: white;
}
.titlebar {
background-color: #000000;
margin-top: 0px;
margin-bottom: 20px;
min-height: 40px;
color: white;
padding-top: 8px;
padding-bottom: 8px;
}
.footer {
border-top: 2px solid black;
margin-top: 15px;
margin-bottom: 20px;
min-height: 40px;
padding-left: 8px;
padding-top: 8px;
padding-bottom: 8px;
}
/* This is the default */
.span6.with-padding {
background-color: #111188;
height: 200px;
color: white;
padding: 10px;
}
#global {
min-height: 500px;
}
/* =======================
Row size
======================= */
.row1 {
background-color: #999999;
height: 100%;
position: relative;
}
/* =======================
Social footer
======================= */
.social .twitter,
.social .github,
.social .googleplus {
background: url("../img/footer-links.png") no-repeat transparent;
display: inline-block;
height: 35px;
overflow: hidden;
text-indent: 9999px;
width: 35px;
margin-right: 10px;
}
.social .twitter {
background-position: 0px 2px;
}
.social .github {
background-position: -59px 2px;
}
/* =======================
Media size overrides
======================= */
/* Large desktop */
@media (min-width: 1200px) {
.span6.with-padding {
background-color: #dc143c;
width: 540px;
padding: 15px;
}
}
/* Normal desktop */
@media (min-width: 980px) and (max-width: 1199px) {
.span6.with-padding {
background-color: #ee1111;
width: 440px;
padding: 10px;
}
}
/* Portrait tablet to landscape and desktop */
@media (min-width: 768px) and (max-width: 979px) {
body {
padding-top: 0px;
}
.span6.with-padding {
background-color: #292e33;
width: 332px;
padding: 10px;
}
}
/* Landscape phone to portrait tablet */
@media (max-width: 767px) {
body {
padding-top: 0px;
}
#global {
/* TODO: Fix this to be relative to the navigation size */
padding-top: 600px;
}
}
/* Landscape phones and down */
@media (max-width: 480px) {
}
-426
View File
@@ -1,426 +0,0 @@
/* ==========================================================================
Author's custom styles
========================================================================== */
@import "variables.less";
@red: crimson;
@lightblue: #118;
@lightred: #e11;
@darkblue: #292E33;
@borderGray: #888;
.red {
background-color: red;
}
.blue {
background-color: blue;
}
.orange {
background-color: orange;
}
.gray {
background-color: grey;
}
body {
padding-top: 58px;
font-family: Arial, Helvetica, sans-serif;
}
h1, h2, h3, h4 {
font-family: Arial, Helvetica, sans-serif;
// font-weight: bold;
font-weight: 900;
}
/* ===================
Top navigation
===================== */
.navbar {
z-index: 999;
.nav {
// float: right;
li a{
padding: 22px 15px 22px;
}
}
.brand {
padding: 13px 10px 13px 28px ;
// padding-left: 30px;
}
background-color: white;
}
.navbar-dotcloud .container {
border-bottom: 2px @black solid;
}
/*
* Responsive YouTube, Vimeo, Embed, and HTML5 Videos with CSS
* http://www.jonsuh.com
*
* Copyright (c) 2012 Jonathan Suh
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*/
.js-video {
height: 0;
padding-top: 25px;
padding-bottom: 67.5%;
margin-bottom: 10px;
position: relative;
overflow: hidden;
}
.js-video.vimeo {
padding-top: 0;
}
.js-video.widescreen {
padding-bottom: 57.25%;
}
.js-video embed, .js-video iframe, .js-video object, .js-video video {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
}
/* Responsive */
@media (max-width: 767px) {
.js-video {
padding-top: 0;
}
}
/* button style from http://charliepark.org/bootstrap_buttons/ */
.btn-custom {
background-color: hsl(0, 0%, 16%) !important;
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#515151", endColorstr="#282828");
background-image: -khtml-gradient(linear, left top, left bottom, from(#515151), to(#282828));
background-image: -moz-linear-gradient(top, #515151, #282828);
background-image: -ms-linear-gradient(top, #515151, #282828);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #515151), color-stop(100%, #282828));
background-image: -webkit-linear-gradient(top, #515151, #282828);
background-image: -o-linear-gradient(top, #515151, #282828);
background-image: linear-gradient(#515151, #282828);
border-color: #282828 #282828 hsl(0, 0%, 12%);
color: #fff !important;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.26);
-webkit-font-smoothing: antialiased;
}
/* ===================
Page title bar
===================== */
h1.pageheader {
color: @white;
font-size: 20px;
font-family: "Arial Black", Tahoma, sans-serif;
margin: 8px;
margin-left: 22px;
}
/* ===================
Hero unit
===================== */
section.header {
margin-top:0;
}
.hero-unit {
background-color: @darkblue;
h5 {
color: @white;
}
.subtitle {
}
}
/* ===================
Main content layout
===================== */
.contentblock {
margin-top: 20px;
border-width: 3px;
// border-color: #E00;
// border-style:solid;
// border-color: @borderGray;
// box-sizing: border-box;
background-color: @grayLighter;
box-sizing: content-box;
padding: 20px;
}
.section img {
margin: 15px 15px 15px 0;
border: 2px solid gray;
}
/* ===================
left navigation
===================== */
.dotcloudsidebar {
// background-color: #ee3;
// border: 1px red dotted;
float: left;
height: 100%;
top: 0px;
bottom: 0px;
position: relative;
// margin: 0px;
min-height: 100%;
margin-top: 78px;
margin-bottom: 22px;
}
.sidebar {
// font-family: "Maven Pro";
font-weight: normal;
// margin-top: 38px;
float: left;
// width: 220px;
min-height: 475px;
// margin-bottom: 28px;
// padding-bottom: 120px;
background: #ececec;
border-left: 1px solid #bbbbbb;
border-right: 1px solid #cccccc;
position: relative;
ul {
padding: 0px;
li {
font-size: 14px;
// list-style: none;
list-style-type: none;
list-style-position: outside;
list-style-image: none;
margin-left: -25px;
padding: 0px;
a {
display: block;
color: #443331;
outline: 1px solid #dddddd;
padding: 12px 12px 10px 12px;
margin-top: 1px;
background-color: #d2d2d2;
}
.toctree-l1, .toctree-l2 {
}
.toctree-l1 {
font-size: larger;
a {
background-color: rgb(223, 223, 223);
}
.current {
font-weight: bold;
}
// margin-left: -25px;
}
.toctree-l2 {
a {
padding-left: 18px;
background-color: rgb(255, 255, 255);
}
.current {
font-weight: bold;
}
}
.toctree-l3 {
font-size: smaller;
a {
padding-left: 36px;
background-color: rgb(255, 255, 255);
}
.current {
font-weight: bold;
}
}
}
}
}
.brand img {
height: 38px;
margin-left: -6px;
}
.border-box {
box-sizing: border-box;
padding: 20px;
background-color: @lightblue;
color: white;
}
.titlebar {
background-color: @black;
margin-top: 0px;
margin-bottom: 20px;
min-height: 40px;
color: white;
// box-sizing: border-box;
padding-top: 8px;
padding-bottom: 8px;
}
.footer {
border-top: 2px solid black;
// background-color: #d2d2d2;
margin-top: 15px;
margin-bottom: 20px;
min-height: 40px;
padding-left: 8px;
padding-top: 8px;
padding-bottom: 8px;
}
/* This is the default */
.span6.with-padding {
background-color: @lightblue;
height: 200px;
color: white;
padding: 10px;
}
#global {
min-height: 500px;
}
/* =======================
Row size
======================= */
.row1 {
background-color: @grayLight;
height: 100%;
position: relative;
}
/* =======================
Social footer
======================= */
.social .twitter, .social .github, .social .googleplus {
background: url("../img/footer-links.png") no-repeat transparent;
display: inline-block;
height: 35px;
overflow: hidden;
text-indent: 9999px;
width: 35px;
margin-right: 10px;
}
.social .twitter {
background-position: 0px 2px;
}
.social .github {
background-position: -59px 2px;
}
/* =======================
Media size overrides
======================= */
/* Large desktop */
@media (min-width: 1200px) {
.span6.with-padding {
background-color: @red;
width: (@gridColumnWidth1200 * 6) + (@gridGutterWidth1200 * 5) - @gridGutterWidth1200;
padding: @gridGutterWidth1200/2;
}
}
/* Normal desktop */
@media (min-width: 980px) and (max-width: 1199px) {
.span6.with-padding {
background-color: @lightred;
width: (@gridColumnWidth * 6) + (@gridGutterWidth * 5) - @gridGutterWidth;
padding: @gridGutterWidth/2;
}
}
/* Portrait tablet to landscape and desktop */
@media (min-width: 768px) and (max-width: 979px) {
body {
padding-top: 0px;
}
.span6.with-padding {
background-color: @darkblue;
width: (@gridColumnWidth768 * 6) + (@gridGutterWidth768 * 5) - @gridGutterWidth768;
padding: @gridGutterWidth768/2;
}
}
/* Landscape phone to portrait tablet */
@media (max-width: 767px) {
body {
padding-top: 0px;
}
#global {
/* TODO: Fix this to be relative to the navigation size */
padding-top: 600px;
}
}
/* Landscape phones and down */
@media (max-width: 480px) {
}
View File
-301
View File
@@ -1,301 +0,0 @@
//
// Variables
// --------------------------------------------------
// Global values
// --------------------------------------------------
// Grays
// -------------------------
@black: #000;
@grayDarker: #222;
@grayDark: #333;
@gray: #555;
@grayLight: #999;
@grayLighter: #eee;
@white: #fff;
// Accent colors
// -------------------------
@blue: #049cdb;
@blueDark: #0064cd;
@green: #46a546;
@red: #9d261d;
@yellow: #ffc40d;
@orange: #f89406;
@pink: #c3325f;
@purple: #7a43b6;
// Scaffolding
// -------------------------
@bodyBackground: @white;
@textColor: @grayDark;
// Links
// -------------------------
@linkColor: #08c;
@linkColorHover: darken(@linkColor, 15%);
// Typography
// -------------------------
@sansFontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif;
@serifFontFamily: Georgia, "Times New Roman", Times, serif;
@monoFontFamily: Monaco, Menlo, Consolas, "Courier New", monospace;
@baseFontSize: 14px;
@baseFontFamily: @sansFontFamily;
@baseLineHeight: 20px;
@altFontFamily: @serifFontFamily;
@headingsFontFamily: inherit; // empty to use BS default, @baseFontFamily
@headingsFontWeight: bold; // instead of browser default, bold
@headingsColor: inherit; // empty to use BS default, @textColor
// Component sizing
// -------------------------
// Based on 14px font-size and 20px line-height
@fontSizeLarge: @baseFontSize * 1.25; // ~18px
@fontSizeSmall: @baseFontSize * 0.85; // ~12px
@fontSizeMini: @baseFontSize * 0.75; // ~11px
@paddingLarge: 11px 19px; // 44px
@paddingSmall: 2px 10px; // 26px
@paddingMini: 0 6px; // 22px
@baseBorderRadius: 4px;
@borderRadiusLarge: 6px;
@borderRadiusSmall: 3px;
// Tables
// -------------------------
@tableBackground: transparent; // overall background-color
@tableBackgroundAccent: #f9f9f9; // for striping
@tableBackgroundHover: #f5f5f5; // for hover
@tableBorder: #ddd; // table and cell border
// Buttons
// -------------------------
@btnBackground: @white;
@btnBackgroundHighlight: darken(@white, 10%);
@btnBorder: #ccc;
@btnPrimaryBackground: @linkColor;
@btnPrimaryBackgroundHighlight: spin(@btnPrimaryBackground, 20%);
@btnInfoBackground: #5bc0de;
@btnInfoBackgroundHighlight: #2f96b4;
@btnSuccessBackground: #62c462;
@btnSuccessBackgroundHighlight: #51a351;
@btnWarningBackground: lighten(@orange, 15%);
@btnWarningBackgroundHighlight: @orange;
@btnDangerBackground: #ee5f5b;
@btnDangerBackgroundHighlight: #bd362f;
@btnInverseBackground: #444;
@btnInverseBackgroundHighlight: @grayDarker;
// Forms
// -------------------------
@inputBackground: @white;
@inputBorder: #ccc;
@inputBorderRadius: @baseBorderRadius;
@inputDisabledBackground: @grayLighter;
@formActionsBackground: #f5f5f5;
@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border
// Dropdowns
// -------------------------
@dropdownBackground: @white;
@dropdownBorder: rgba(0,0,0,.2);
@dropdownDividerTop: #e5e5e5;
@dropdownDividerBottom: @white;
@dropdownLinkColor: @grayDark;
@dropdownLinkColorHover: @white;
@dropdownLinkColorActive: @white;
@dropdownLinkBackgroundActive: @linkColor;
@dropdownLinkBackgroundHover: @dropdownLinkBackgroundActive;
// COMPONENT VARIABLES
// --------------------------------------------------
// Z-index master list
// -------------------------
// Used for a bird's eye view of components dependent on the z-axis
// Try to avoid customizing these :)
@zindexDropdown: 1000;
@zindexPopover: 1010;
@zindexTooltip: 1030;
@zindexFixedNavbar: 1030;
@zindexModalBackdrop: 1040;
@zindexModal: 1050;
// Sprite icons path
// -------------------------
@iconSpritePath: "../img/glyphicons-halflings.png";
@iconWhiteSpritePath: "../img/glyphicons-halflings-white.png";
// Input placeholder text color
// -------------------------
@placeholderText: @grayLight;
// Hr border color
// -------------------------
@hrBorder: @grayLighter;
// Horizontal forms & lists
// -------------------------
@horizontalComponentOffset: 180px;
// Wells
// -------------------------
@wellBackground: #f5f5f5;
// Navbar
// -------------------------
@navbarCollapseWidth: 979px;
@navbarCollapseDesktopWidth: @navbarCollapseWidth + 1;
@navbarHeight: 40px;
@navbarBackgroundHighlight: #ffffff;
@navbarBackground: darken(@navbarBackgroundHighlight, 5%);
@navbarBorder: darken(@navbarBackground, 12%);
@navbarText: #777;
@navbarLinkColor: #777;
@navbarLinkColorHover: @grayDark;
@navbarLinkColorActive: @gray;
@navbarLinkBackgroundHover: transparent;
@navbarLinkBackgroundActive: darken(@navbarBackground, 5%);
@navbarBrandColor: @navbarLinkColor;
// Inverted navbar
@navbarInverseBackground: #111111;
@navbarInverseBackgroundHighlight: #222222;
@navbarInverseBorder: #252525;
@navbarInverseText: @grayLight;
@navbarInverseLinkColor: @grayLight;
@navbarInverseLinkColorHover: @white;
@navbarInverseLinkColorActive: @navbarInverseLinkColorHover;
@navbarInverseLinkBackgroundHover: transparent;
@navbarInverseLinkBackgroundActive: @navbarInverseBackground;
@navbarInverseSearchBackground: lighten(@navbarInverseBackground, 25%);
@navbarInverseSearchBackgroundFocus: @white;
@navbarInverseSearchBorder: @navbarInverseBackground;
@navbarInverseSearchPlaceholderColor: #ccc;
@navbarInverseBrandColor: @navbarInverseLinkColor;
// Pagination
// -------------------------
@paginationBackground: #fff;
@paginationBorder: #ddd;
@paginationActiveBackground: #f5f5f5;
// Hero unit
// -------------------------
@heroUnitBackground: @grayLighter;
@heroUnitHeadingColor: inherit;
@heroUnitLeadColor: inherit;
// Form states and alerts
// -------------------------
@warningText: #c09853;
@warningBackground: #fcf8e3;
@warningBorder: darken(spin(@warningBackground, -10), 3%);
@errorText: #b94a48;
@errorBackground: #f2dede;
@errorBorder: darken(spin(@errorBackground, -10), 3%);
@successText: #468847;
@successBackground: #dff0d8;
@successBorder: darken(spin(@successBackground, -10), 5%);
@infoText: #3a87ad;
@infoBackground: #d9edf7;
@infoBorder: darken(spin(@infoBackground, -10), 7%);
// Tooltips and popovers
// -------------------------
@tooltipColor: #fff;
@tooltipBackground: #000;
@tooltipArrowWidth: 5px;
@tooltipArrowColor: @tooltipBackground;
@popoverBackground: #fff;
@popoverArrowWidth: 10px;
@popoverArrowColor: #fff;
@popoverTitleBackground: darken(@popoverBackground, 3%);
// Special enhancement for popovers
@popoverArrowOuterWidth: @popoverArrowWidth + 1;
@popoverArrowOuterColor: rgba(0,0,0,.25);
// GRID
// --------------------------------------------------
// Default 940px grid
// -------------------------
@gridColumns: 12;
@gridColumnWidth: 60px;
@gridGutterWidth: 20px;
@gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
// 1200px min
@gridColumnWidth1200: 70px;
@gridGutterWidth1200: 30px;
@gridRowWidth1200: (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1));
// 768px-979px
@gridColumnWidth768: 42px;
@gridGutterWidth768: 20px;
@gridRowWidth768: (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1));
// Fluid grid
// -------------------------
@fluidGridColumnWidth: percentage(@gridColumnWidth/@gridRowWidth);
@fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth);
// 1200px min
@fluidGridColumnWidth1200: percentage(@gridColumnWidth1200/@gridRowWidth1200);
@fluidGridGutterWidth1200: percentage(@gridGutterWidth1200/@gridRowWidth1200);
// 768px-979px
@fluidGridColumnWidth768: percentage(@gridColumnWidth768/@gridRowWidth768);
@fluidGridGutterWidth768: percentage(@gridGutterWidth768/@gridRowWidth768);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Some files were not shown because too many files have changed in this diff Show More