diff --git a/.gitignore b/.gitignore index 00d66de3ed..a40d9e067c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Docker project generated files to ignore +# if you want to ignore files created by your editor/tools, +# please consider a global .gitignore https://help.github.com/articles/ignoring-files .vagrant* bin docker/docker diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dbcbab6695..6fed5c61b7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,13 @@ Want to hack on Docker? Awesome! Here are instructions to get you started. They are probably not perfect, please let us know if anything feels wrong or incomplete. +## Reporting Issues + +When reporting [issues](https://github.com/dotcloud/docker/issues) +on Github please include your host OS ( Ubuntu 12.04, Fedora 19, etc... ) +and the output of `docker version` along with the output of `docker info` if possible. +This information will help us review and fix your issue faster. + ## Build Environment For instructions on setting up your development environment, please diff --git a/archive/changes.go b/archive/changes.go index 83bdcae7cf..a4076fc0ad 100644 --- a/archive/changes.go +++ b/archive/changes.go @@ -181,7 +181,7 @@ func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) { oldStat.Rdev != newStat.Rdev || // Don't look at size for dirs, its not a good measure of change (oldStat.Size != newStat.Size && oldStat.Mode&syscall.S_IFDIR != syscall.S_IFDIR) || - oldStat.Mtim != newStat.Mtim { + getLastModification(oldStat) != getLastModification(newStat) { change := Change{ Path: newChild.path(), Kind: ChangeModify, diff --git a/archive/diff.go b/archive/diff.go index 58d30466e3..f44991ecb5 100644 --- a/archive/diff.go +++ b/archive/diff.go @@ -83,8 +83,10 @@ func ApplyLayer(dest string, layer Archive) error { } for k, v := range modifiedDirs { - aTime := time.Unix(v.Atim.Unix()) - mTime := time.Unix(v.Mtim.Unix()) + lastAccess := getLastAccess(v) + lastModification := getLastModification(v) + aTime := time.Unix(lastAccess.Unix()) + mTime := time.Unix(lastModification.Unix()) if err := os.Chtimes(k, aTime, mTime); err != nil { return err diff --git a/archive/stat_darwin.go b/archive/stat_darwin.go new file mode 100644 index 0000000000..53ae9dee2f --- /dev/null +++ b/archive/stat_darwin.go @@ -0,0 +1,11 @@ +package archive + +import "syscall" + +func getLastAccess(stat *syscall.Stat_t) syscall.Timespec { + return stat.Atimespec +} + +func getLastModification(stat *syscall.Stat_t) syscall.Timespec { + return stat.Mtimespec +} diff --git a/archive/stat_linux.go b/archive/stat_linux.go new file mode 100644 index 0000000000..50b4627c4a --- /dev/null +++ b/archive/stat_linux.go @@ -0,0 +1,11 @@ +package archive + +import "syscall" + +func getLastAccess(stat *syscall.Stat_t) syscall.Timespec { + return stat.Atim +} + +func getLastModification(stat *syscall.Stat_t) syscall.Timespec { + return stat.Mtim +} diff --git a/contrib/mkseccomp.pl b/contrib/mkseccomp.pl new file mode 100755 index 0000000000..44088f952c --- /dev/null +++ b/contrib/mkseccomp.pl @@ -0,0 +1,77 @@ +#!/usr/bin/perl +# +# A simple helper script to help people build seccomp profiles for +# Docker/LXC. The goal is mostly to reduce the attack surface to the +# kernel, by restricting access to rarely used, recently added or not used +# syscalls. +# +# This script processes one or more files which contain the list of system +# calls to be allowed. See mkseccomp.sample for more information how you +# can configure the list of syscalls. When run, this script produces output +# which, when stored in a file, can be passed to docker as follows: +# +# docker run -lxc-conf="lxc.seccomp=$file" +# +# The included sample file shows how to cut about a quarter of all syscalls, +# which affecting most applications. +# +# For specific situations it is possible to reduce the list further. By +# reducing the list to just those syscalls required by a certain application +# you can make it difficult for unknown/unexpected code to run. +# +# Run this script as follows: +# +# ./mkseccomp.pl < mkseccomp.sample >syscalls.list +# or +# ./mkseccomp.pl mkseccomp.sample >syscalls.list +# +# Multiple files can be specified, in which case the lists of syscalls are +# combined. +# +# By Martijn van Oosterhout Nov 2013 + +# How it works: +# +# This program basically spawns two processes to form a chain like: +# +# | cpp | + +use strict; +use warnings; + +if( -t ) { + print STDERR "Helper script to make seccomp filters for Docker/LXC.\n"; + print STDERR "Usage: mkseccomp.pl [files...]\n"; + exit 1; +} + +my $pid = open(my $in, "-|") // die "Couldn't fork1 ($!)\n"; + +if($pid == 0) { # Child + $pid = open(my $out, "|-") // die "Couldn't fork2 ($!)\n"; + + if($pid == 0) { # Child, which execs cpp + exec "cpp" or die "Couldn't exec cpp ($!)\n"; + exit 1; + } + + # Process the DATA section and output to cpp + print $out "#include \n"; + while(<>) { + if(/^\w/) { + print $out "__NR_$_"; + } + } + close $out; + exit 0; + +} + +# Print header and then process output from cpp. +print "1\n"; +print "whitelist\n"; + +while(<$in>) { + print if( /^[0-9]/ ); +} + diff --git a/contrib/mkseccomp.sample b/contrib/mkseccomp.sample new file mode 100644 index 0000000000..25bf4822dc --- /dev/null +++ b/contrib/mkseccomp.sample @@ -0,0 +1,444 @@ +/* This sample file is an example for mkseccomp.pl to produce a seccomp file + * which restricts syscalls that are only useful for an admin but allows the + * vast majority of normal userspace programs to run normally. + * + * The format of this file is one line per syscall. This is then processed + * and passed to 'cpp' to convert the names to numbers using whatever is + * correct for your platform. As such C-style comments are permitted. Note + * this also means that C preprocessor macros are also allowed. So it is + * possible to create groups surrounded by #ifdef/#endif and control their + * inclusion via #define (not #include). + * + * Syscalls that don't exist on your architecture are silently filtered out. + * Syscalls marked with (*) are required for a container to spawn a bash + * shell successfully (not necessarily full featured). Listing the same + * syscall multiple times is no problem. + * + * If you want to make a list specifically for one application the easiest + * way is to run the application under strace, like so: + * + * $ strace -f -q -c -o strace.out application args... + * + * Once you have a reasonable sample of the execution of the program, exit + * it. The file strace.out will have a summary of the syscalls used. Copy + * that list into this file, comment out everything else except the starred + * syscalls (which you need for the container to start) and you're done. + * + * To get the list of syscalls from the strace output this works well for + * me + * + * $ cut -c52 < strace.out + * + * This sample list was compiled as a combination of all the syscalls + * available on i386 and amd64 on Ubuntu Precise, as such it may not contain + * everything and not everything may be relevent for your system. This + * shouldn't be a problem. + */ + +// Filesystem/File descriptor related +access // (*) +chdir // (*) +chmod +chown +chown32 +close // (*) +creat +dup // (*) +dup2 // (*) +dup3 +epoll_create +epoll_create1 +epoll_ctl +epoll_ctl_old +epoll_pwait +epoll_wait +epoll_wait_old +eventfd +eventfd2 +faccessat // (*) +fadvise64 +fadvise64_64 +fallocate +fanotify_init +fanotify_mark +ioctl // (*) +fchdir +fchmod +fchmodat +fchown +fchown32 +fchownat +fcntl // (*) +fcntl64 +fdatasync +fgetxattr +flistxattr +flock +fremovexattr +fsetxattr +fstat // (*) +fstat64 +fstatat64 +fstatfs +fstatfs64 +fsync +ftruncate +ftruncate64 +getcwd // (*) +getdents // (*) +getdents64 +getxattr +inotify_add_watch +inotify_init +inotify_init1 +inotify_rm_watch +io_cancel +io_destroy +io_getevents +io_setup +io_submit +lchown +lchown32 +lgetxattr +link +linkat +listxattr +llistxattr +llseek +_llseek +lremovexattr +lseek // (*) +lsetxattr +lstat +lstat64 +mkdir +mkdirat +mknod +mknodat +newfstatat +_newselect +oldfstat +oldlstat +oldolduname +oldstat +olduname +oldwait4 +open // (*) +openat // (*) +pipe // (*) +pipe2 +poll +ppoll +pread64 +preadv +futimesat +pselect6 +pwrite64 +pwritev +read // (*) +readahead +readdir +readlink +readlinkat +readv +removexattr +rename +renameat +rmdir +select +sendfile +sendfile64 +setxattr +splice +stat // (*) +stat64 +statfs // (*) +statfs64 +symlink +symlinkat +sync +sync_file_range +sync_file_range2 +syncfs +tee +truncate +truncate64 +umask +unlink +unlinkat +ustat +utime +utimensat +utimes +write // (*) +writev + +// Network related +accept +accept4 +bind // (*) +connect // (*) +getpeername +getsockname // (*) +getsockopt +listen +recv +recvfrom // (*) +recvmmsg +recvmsg +send +sendmmsg +sendmsg +sendto // (*) +setsockopt +shutdown +socket // (*) +socketcall +socketpair + +// Signal related +pause +rt_sigaction // (*) +rt_sigpending +rt_sigprocmask // (*) +rt_sigqueueinfo +rt_sigreturn // (*) +rt_sigsuspend +rt_sigtimedwait +rt_tgsigqueueinfo +sigaction +sigaltstack // (*) +signal +signalfd +signalfd4 +sigpending +sigprocmask +sigreturn +sigsuspend + +// Other needed POSIX +alarm +brk // (*) +clock_adjtime +clock_getres +clock_gettime +clock_nanosleep +//clock_settime +gettimeofday +nanosleep +nice +sysinfo +syslog +time +timer_create +timer_delete +timerfd_create +timerfd_gettime +timerfd_settime +timer_getoverrun +timer_gettime +timer_settime +times +uname // (*) + +// Memory control +madvise +mbind +mincore +mlock +mlockall +mmap // (*) +mmap2 +mprotect // (*) +mremap +msync +munlock +munlockall +munmap // (*) +remap_file_pages +set_mempolicy +vmsplice + +// Process control +capget +//capset +clone // (*) +execve // (*) +exit // (*) +exit_group // (*) +fork +getcpu +getpgid +getpgrp // (*) +getpid // (*) +getppid // (*) +getpriority +getresgid +getresgid32 +getresuid +getresuid32 +getrlimit // (*) +getrusage +getsid +getuid // (*) +getuid32 +getegid // (*) +getegid32 +geteuid // (*) +geteuid32 +getgid // (*) +getgid32 +getgroups +getgroups32 +getitimer +get_mempolicy +kill +//personality +prctl +prlimit64 +sched_getaffinity +sched_getparam +sched_get_priority_max +sched_get_priority_min +sched_getscheduler +sched_rr_get_interval +//sched_setaffinity +//sched_setparam +//sched_setscheduler +sched_yield +setfsgid +setfsgid32 +setfsuid +setfsuid32 +setgid +setgid32 +setgroups +setgroups32 +setitimer +setpgid // (*) +setpriority +setregid +setregid32 +setresgid +setresgid32 +setresuid +setresuid32 +setreuid +setreuid32 +setrlimit +setsid +setuid +setuid32 +ugetrlimit +vfork +wait4 // (*) +waitid +waitpid + +// IPC +ipc +mq_getsetattr +mq_notify +mq_open +mq_timedreceive +mq_timedsend +mq_unlink +msgctl +msgget +msgrcv +msgsnd +semctl +semget +semop +semtimedop +shmat +shmctl +shmdt +shmget + +// Linux specific, mostly needed for thread-related stuff +arch_prctl // (*) +get_robust_list +get_thread_area +gettid +futex // (*) +restart_syscall // (*) +set_robust_list // (*) +set_thread_area +set_tid_address // (*) +tgkill +tkill + +// Admin syscalls, these are blocked +//acct +//adjtimex +//bdflush +//chroot +//create_module +//delete_module +//get_kernel_syms // Obsolete +//idle // Obsolete +//init_module +//ioperm +//iopl +//ioprio_get +//ioprio_set +//kexec_load +//lookup_dcookie // oprofile only? +//migrate_pages // NUMA +//modify_ldt +//mount +//move_pages // NUMA +//name_to_handle_at // NFS server +//nfsservctl // NFS server +//open_by_handle_at // NFS server +//perf_event_open +//pivot_root +//process_vm_readv // For debugger +//process_vm_writev // For debugger +//ptrace // For debugger +//query_module +//quotactl +//reboot +//setdomainname +//sethostname +//setns +//settimeofday +//sgetmask // Obsolete +//ssetmask // Obsolete +//stime +//swapoff +//swapon +//_sysctl +//sysfs +//sys_setaltroot +//umount +//umount2 +//unshare +//uselib +//vhangup +//vm86 +//vm86old + +// Kernel key management +//add_key +//keyctl +//request_key + +// Unimplemented +//afs_syscall +//break +//ftime +//getpmsg +//gtty +//lock +//madvise1 +//mpx +//prof +//profil +//putpmsg +//security +//stty +//tuxcall +//ulimit +//vserver diff --git a/hack/RELEASE-CHECKLIST.md b/hack/RELEASE-CHECKLIST.md index 7d68f9bdf5..8723d3c567 100644 --- a/hack/RELEASE-CHECKLIST.md +++ b/hack/RELEASE-CHECKLIST.md @@ -5,7 +5,6 @@ So you're in charge of a Docker release? Cool. Here's what to do. If your experience deviates from this document, please document the changes to keep it up-to-date. - ### 1. Pull from master and create a release branch ```bash @@ -13,6 +12,7 @@ export VERSION=vXXX git checkout release git pull git checkout -b bump_$VERSION +git merge origin/master ``` ### 2. Update CHANGELOG.md @@ -54,10 +54,14 @@ EXAMPLES: ### 3. Change the contents of the VERSION file +```bash +echo ${VERSION#v} > VERSION +``` + ### 4. Run all tests ```bash -docker run -privileged -lxc-conf=lxc.aa_profile=unconfined docker hack/make.sh test +docker run -privileged docker hack/make.sh test ``` ### 5. Test the docs @@ -79,8 +83,8 @@ git push origin bump_$VERSION ### 8. Apply tag ```bash -git tag -a v$VERSION # Don't forget the v! -git push --tags +git tag -a $VERSION -m $VERSION bump_$VERSION +git push origin $VERSION ``` Merging the pull request to the release branch will automatically @@ -91,6 +95,9 @@ documentation releases, see ``docs/README.md`` ### 9. Go to github to merge the bump_$VERSION into release +Don't forget to push that pretty blue button to delete the leftover +branch afterwards! + ### 10. Publish binaries To run this you will need access to the release credentials. @@ -107,17 +114,19 @@ docker run \ -e AWS_ACCESS_KEY=$(cat ~/.aws/access_key) \ -e AWS_SECRET_KEY=$(cat ~/.aws/secret_key) \ -e GPG_PASSPHRASE=supersecretsesame \ - -privileged -lxc-conf=lxc.aa_profile=unconfined \ - -t -i \ + -i -t -privileged \ docker \ hack/release.sh ``` -It will build and upload the binaries on the specified bucket (you should -use test.docker.io for general testing, and once everything is fine, -switch to get.docker.io). +It will run the test suite one more time, build the binaries and packages, +and upload to the specified bucket (you should use test.docker.io for +general testing, and once everything is fine, switch to get.docker.io). - -### 11. Rejoice! +### 11. Rejoice and Evangelize! Congratulations! You're done. + +Go forth and announce the glad tidings of the new release in `#docker`, +`#docker-dev`, on the [mailing list](https://groups.google.com/forum/#!forum/docker-dev), +and on Twitter! diff --git a/runtime.go b/runtime.go index f58be836bd..a7c2659b00 100644 --- a/runtime.go +++ b/runtime.go @@ -422,7 +422,8 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin if _, err := runtime.containerGraph.Set(name, id); err != nil { if strings.HasSuffix(err.Error(), "name are not unique") { conflictingContainer, _ := runtime.GetByName(name) - return nil, nil, fmt.Errorf("Conflict, The name %s is already assigned to %s. You have to delete (or rename) that container to be able to assign %s to a container again.", name, utils.TruncateID(conflictingContainer.ID), name) + nameAsKnownByUser := strings.TrimPrefix(name, "/") + return nil, nil, fmt.Errorf("Conflict, The name %s is already assigned to %s. You have to delete (or rename) that container to be able to assign %s to a container again.", nameAsKnownByUser, utils.TruncateID(conflictingContainer.ID), nameAsKnownByUser) } return nil, nil, err } diff --git a/server.go b/server.go index a988d2133d..3641e2fdc8 100644 --- a/server.go +++ b/server.go @@ -985,7 +985,17 @@ func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *ut if err != nil { return err } - if _, err := srv.poolAdd("pull", localName+":"+tag); err != nil { + + out = utils.NewWriteFlusher(out) + + c, err := srv.poolAdd("pull", localName+":"+tag) + if err != nil { + if c != nil { + // Another pull of the same repository is already taking place; just wait for it to finish + out.Write(sf.FormatStatus("", "Repository %s already being pulled by another client. Waiting.", localName)) + <-c + return nil + } return err } defer srv.poolRemove("pull", localName+":"+tag) @@ -1001,7 +1011,6 @@ func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *ut localName = remoteName } - out = utils.NewWriteFlusher(out) err = srv.pullRepository(r, out, localName, remoteName, tag, endpoint, sf, parallel) if err == registry.ErrLoginRequired { return err