bump to 0.2.2

This commit is contained in:
Victor Vieux 2013-05-06 11:52:15 +02:00
commit 6f9b574f25
14 changed files with 217 additions and 19 deletions

View file

@ -1,6 +1,13 @@
# Changelog
## 0.2.1 (2012-05-01)
## 0.2.2 (2013-05-03)
+ Support for data volumes ('docker run -v=PATH')
+ Share data volumes between containers ('docker run -volumes-from')
+ Improved documentation
* Upgrade to Go 1.0.3
* Various upgrades to the dev environment for contributors
## 0.2.1 (2013-05-01)
+ 'docker commit -run' bundles a layer with default runtime options: command, ports etc.
* Improve install process on Vagrant
+ New Dockerfile operation: "maintainer"
@ -10,7 +17,7 @@
+ 'docker -d -r': restart crashed containers at daemon startup
* Runtime: improve test coverage
## 0.2.0 (2012-04-23)
## 0.2.0 (2013-04-23)
- Runtime: ghost containers can be killed and waited for
* Documentation: update install intructions
- Packaging: fix Vagrantfile

6
Vagrantfile vendored
View file

@ -1,8 +1,8 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
BOX_NAME = "ubuntu"
BOX_URI = "http://files.vagrantup.com/precise64.box"
BOX_NAME = ENV['BOX_NAME'] || "ubuntu"
BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box"
PPA_KEY = "E61D797F63561DC6"
Vagrant::Config.run do |config|
@ -11,7 +11,7 @@ Vagrant::Config.run do |config|
config.vm.box_url = BOX_URI
# Add docker PPA key to the local repository and install docker
pkg_cmd = "apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys #{PPA_KEY}; "
pkg_cmd << "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >>/etc/apt/sources.list; "
pkg_cmd << "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >/etc/apt/sources.list.d/lxc-docker.list; "
pkg_cmd << "apt-get update -qq; apt-get install -q -y lxc-docker"
if ARGV.include?("--provider=aws".downcase)
# Add AUFS dependency to amazon's VM

9
api.go
View file

@ -320,9 +320,16 @@ func ListenAndServe(addr string, srv *Server) error {
r.Path("/containers/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println(r.Method, r.RequestURI)
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
vars := mux.Vars(r)
name := vars["name"]
if err := srv.ContainerDestroy(name); err != nil {
var v bool
if r.Form.Get("v") == "1" {
v = true
}
if err := srv.ContainerDestroy(name, v); err != nil {
httpError(w, err)
} else {
w.WriteHeader(http.StatusOK)

View file

@ -13,12 +13,13 @@ import (
"net/http/httputil"
"net/url"
"os"
"path/filepath"
"strconv"
"text/tabwriter"
"time"
)
const VERSION = "0.2.1"
const VERSION = "0.2.2"
var (
GIT_COMMIT string
@ -434,7 +435,7 @@ func CmdRmi(args ...string) error {
return nil
}
for _, name := range args {
for _, name := range cmd.Args() {
_, _, err := call("DELETE", "/images/"+name, nil)
if err != nil {
fmt.Printf("%s", err)
@ -476,7 +477,8 @@ func CmdHistory(args ...string) error {
}
func CmdRm(args ...string) error {
cmd := Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove a container")
cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove a container")
v := cmd.Bool("v", false, "Remove the volumes associated to the container")
if err := cmd.Parse(args); err != nil {
return nil
}
@ -484,9 +486,12 @@ func CmdRm(args ...string) error {
cmd.Usage()
return nil
}
for _, name := range args {
_, _, err := call("DELETE", "/containers/"+name, nil)
val := url.Values{}
if *v {
val.Set("v", "1")
}
for _, name := range cmd.Args() {
_, _, err := call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
if err != nil {
fmt.Printf("%s", err)
} else {
@ -930,6 +935,25 @@ func (opts AttachOpts) Get(val string) bool {
return false
}
// PathOpts stores a unique set of absolute paths
type PathOpts map[string]struct{}
func NewPathOpts() PathOpts {
return make(PathOpts)
}
func (opts PathOpts) String() string {
return fmt.Sprintf("%v", map[string]struct{}(opts))
}
func (opts PathOpts) Set(val string) error {
if !filepath.IsAbs(val) {
return fmt.Errorf("%s is not an absolute path", val)
}
opts[filepath.Clean(val)] = struct{}{}
return nil
}
func CmdTag(args ...string) error {
cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
force := cmd.Bool("f", false, "Force")

View file

@ -48,6 +48,7 @@ type Container struct {
runtime *Runtime
waitLock chan struct{}
Volumes map[string]string
}
type Config struct {
@ -66,6 +67,8 @@ type Config struct {
Cmd []string
Dns []string
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
Volumes map[string]struct{}
VolumesFrom string
}
func ParseRun(args []string) (*Config, *flag.FlagSet, error) {
@ -92,6 +95,11 @@ func ParseRun(args []string) (*Config, *flag.FlagSet, error) {
var flDns ListOpts
cmd.Var(&flDns, "dns", "Set custom dns servers")
flVolumes := NewPathOpts()
cmd.Var(flVolumes, "v", "Attach a data volume")
flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
if err := cmd.Parse(args); err != nil {
return nil, cmd, err
}
@ -131,6 +139,8 @@ func ParseRun(args []string) (*Config, *flag.FlagSet, error) {
Cmd: runCmd,
Dns: flDns,
Image: image,
Volumes: flVolumes,
VolumesFrom: *flVolumesFrom,
}
// When allocating stdin in attached mode, close stdin at client disconnect
@ -384,10 +394,40 @@ func (container *Container) Start() error {
log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
container.Config.MemorySwap = -1
}
container.Volumes = make(map[string]string)
// Create the requested volumes volumes
for volPath := range container.Config.Volumes {
if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil {
return err
} else {
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil
}
container.Volumes[volPath] = c.Id
}
}
if container.Config.VolumesFrom != "" {
c := container.runtime.Get(container.Config.VolumesFrom)
if c == nil {
return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.Id)
}
for volPath, id := range c.Volumes {
if _, exists := container.Volumes[volPath]; exists {
return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.Id)
}
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil
}
container.Volumes[volPath] = id
}
}
if err := container.generateLXCConfig(); err != nil {
return err
}
params := []string{
"-n", container.Id,
"-f", container.lxcConfigPath(),
@ -446,6 +486,7 @@ func (container *Container) Start() error {
// Init the lock
container.waitLock = make(chan struct{})
container.ToDisk()
go container.monitor()
return nil
@ -777,6 +818,22 @@ func (container *Container) RootfsPath() string {
return path.Join(container.root, "rootfs")
}
func (container *Container) GetVolumes() (map[string]string, error) {
ret := make(map[string]string)
for volPath, id := range container.Volumes {
volume, err := container.runtime.volumes.Get(id)
if err != nil {
return nil, err
}
root, err := volume.root()
if err != nil {
return nil, err
}
ret[volPath] = path.Join(root, "layer")
}
return ret, nil
}
func (container *Container) rwPath() string {
return path.Join(container.root, "rw")
}

View file

@ -17,3 +17,6 @@
-p=[]: Map a network port to the container
-t=false: Allocate a pseudo-tty
-u="": Username or UID
-d=[]: Set custom dns servers for the container
-v=[]: Creates a new volumes and mount it at the specified path.
-volumes-from="": Mount all volumes from the given container.

View file

@ -12,7 +12,7 @@ Images
------
An original container image. These are stored on disk and are comparable with what you normally expect from a stopped virtual machine image. Images are stored (and retrieved from) repository
Images are stored on your local file system under /var/lib/docker/images
Images are stored on your local file system under /var/lib/docker/graph
.. _containers:

View file

@ -0,0 +1,53 @@
:title: Sharing data between 2 couchdb databases
:description: Sharing data between 2 couchdb databases
:keywords: docker, example, package installation, networking, couchdb, data volumes
.. _running_redis_service:
Create a redis service
======================
.. include:: example_header.inc
Here's an example of using data volumes to share the same data between 2 couchdb containers.
This could be used for hot upgrades, testing different versions of couchdb on the same data, etc.
Create first database
---------------------
Note that we're marking /var/lib/couchdb as a data volume.
.. code-block:: bash
COUCH1=$(docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03)
Add data to the first database
------------------------------
We're assuming your docker host is reachable at `localhost`. If not, replace `localhost` with the public IP of your docker host.
.. code-block:: bash
HOST=localhost
URL="http://$HOST:$(docker port $COUCH1 5984)/_utils/"
echo "Navigate to $URL in your browser, and use the couch interface to add data"
Create second database
----------------------
This time, we're requesting shared access to $COUCH1's volumes.
.. code-block:: bash
COUCH2=$(docker run -d -volumes-from $COUCH1) shykes/couchdb:2013-05-03)
Browse data on the second database
----------------------------------
.. code-block:: bash
HOST=localhost
URL="http://$HOST:$(docker port $COUCH2 5984)/_utils/"
echo "Navigate to $URL in your browser. You should see the same data as in the first database!"
Congratulations, you are running 2 Couchdb containers, completely isolated from each other *except* for their data.

View file

@ -18,3 +18,4 @@ Contents:
python_web_app
running_redis_service
running_ssh_service
couchdb_data_volumes

View file

@ -79,7 +79,11 @@ lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/sbin/init none bind,ro 0 0
# In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
{{if .Volumes}}
{{range $virtualPath, $realPath := .GetVolumes}}
lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,rw 0 0
{{end}}
{{end}}
# drop linux capabilities (apply mainly to the user root in the container)
lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config

View file

@ -1,3 +1,12 @@
lxc-docker (0.2.2-1) precise; urgency=low
- Support for data volumes ('docker run -v=PATH')
- Share data volumes between containers ('docker run -volumes-from')
- Improved documentation
- Upgrade to Go 1.0.3
- Various upgrades to the dev environment for contributors
-- dotCloud <ops@dotcloud.com> Fri, 3 May 2013 00:00:00 -0700
lxc-docker (0.2.1-1) precise; urgency=low

View file

@ -32,6 +32,7 @@ type Runtime struct {
capabilities *Capabilities
kernelVersion *KernelVersionInfo
autoRestart bool
volumes *Graph
}
var sysInitPath string
@ -79,10 +80,10 @@ func (runtime *Runtime) containerRoot(id string) string {
}
func (runtime *Runtime) mergeConfig(userConf, imageConf *Config) {
if userConf.Hostname != "" {
if userConf.Hostname == "" {
userConf.Hostname = imageConf.Hostname
}
if userConf.User != "" {
if userConf.User == "" {
userConf.User = imageConf.User
}
if userConf.Memory == 0 {
@ -126,7 +127,7 @@ func (runtime *Runtime) Create(config *Config) (*Container, error) {
runtime.mergeConfig(config, img.Config)
}
if config.Cmd == nil {
if config.Cmd == nil || len(config.Cmd) == 0 {
return nil, fmt.Errorf("No command specified")
}
@ -405,6 +406,10 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
if err != nil {
return nil, err
}
volumes, err := NewGraph(path.Join(root, "volumes"))
if err != nil {
return nil, err
}
repositories, err := NewTagStore(path.Join(root, "repositories"), g)
if err != nil {
return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
@ -432,6 +437,7 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
idIndex: NewTruncIndex(),
capabilities: &Capabilities{},
autoRestart: autoRestart,
volumes: volumes,
}
if err := runtime.restore(); err != nil {

View file

@ -294,11 +294,38 @@ func (srv *Server) ContainerRestart(name string, t int) error {
return nil
}
func (srv *Server) ContainerDestroy(name string) error {
func (srv *Server) ContainerDestroy(name string, v bool) error {
if container := srv.runtime.Get(name); container != nil {
volumes := make(map[string]struct{})
// Store all the deleted containers volumes
for _, volumeId := range container.Volumes {
volumes[volumeId] = struct{}{}
}
if err := srv.runtime.Destroy(container); err != nil {
return fmt.Errorf("Error destroying container %s: %s", name, err.Error())
}
if v {
// Retrieve all volumes from all remaining containers
usedVolumes := make(map[string]*Container)
for _, container := range srv.runtime.List() {
for _, containerVolumeId := range container.Volumes {
usedVolumes[containerVolumeId] = container
}
}
for volumeId := range volumes {
// If the requested volu
if c, exists := usedVolumes[volumeId]; exists {
log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.Id)
continue
}
if err := srv.runtime.volumes.Delete(volumeId); err != nil {
return err
}
}
}
} else {
return fmt.Errorf("No such container: %s", name)
}

View file

@ -466,7 +466,7 @@ func FindCgroupMountpoint(cgroupType string) (string, error) {
// cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0
for _, line := range strings.Split(string(output), "\n") {
parts := strings.Split(line, " ")
if parts[2] == "cgroup" {
if len(parts) == 6 && parts[2] == "cgroup" {
for _, opt := range strings.Split(parts[3], ",") {
if opt == cgroupType {
return parts[1], nil