diff --git a/samba-share/Dockerfile b/samba-share/Dockerfile new file mode 100644 index 0000000..a70f705 --- /dev/null +++ b/samba-share/Dockerfile @@ -0,0 +1,14 @@ +FROM debian:stable +LABEL CMDBUILD="docker build -t niccokunzmann/samba-share https://raw.githubusercontent.com/niccokunzmann/dockerfiles/master/samba-share/Dockerfile" +LABEL CMDRUN="docker run niccokunzmann/samba-share" + +MAINTAINER Nicco Kunzmann github.com/niccokunzmann @dannhaltohneson + +# gettext for envsubst +RUN apt-get update && \ + apt-get install -yq samba gettext +ADD run.sh /run.sh +ADD setup-samba-share.sh /setup-samba-share.sh +ADD samba-share.sh /samba-share.sh + +ENTRYPOINT ["/run.sh"] diff --git a/samba-share/README.md b/samba-share/README.md new file mode 100644 index 0000000..e20a483 --- /dev/null +++ b/samba-share/README.md @@ -0,0 +1,77 @@ + + +Samba Docker volume sharing plugin +================================== + +Sharing a Docker container's volume should be as simple as `docker run niccokunzmann/samba-share | sh`. + +This 'plugin' will create and configure a samba server container that auto-creates shares for all +the volumes attached to the specified container. + +Usage +----- + +Possible scenarios are + +- `docker run niccokunzmann/samba-share | sh` shares the volumes of ``. +- `docker run niccokunzmann/samba-share` reminds the user what the options are. +- Additional parameters can be [passed as environment variable](https://docs.docker.com/engine/reference/run/#env-environment-variables) and can be combined. Possible names are USER PASSWORD USERID GROUP READONLY RUN_ARGUMENTS. Examples: + + # run a samba server in read only mode + docker run -e READONLY=yes niccokunzmann/samba-share | sh + # run a samba server on the host and share the content to other computers + docker run -e RUN_ARGUMENTS="--net=host" niccokunzmann/samba-share | sh + + - USER is the samba user (default: "root") + - PASSWORD is USER's password (default: "tcuser") + - USERID to use for the samba USER (default: "1000") + - GROUP user group (default: "root") + - READONLY "yes" or "no" whether write access is denied (default: "no") + - RUN_ARGUMENTS which additional arguments to pass to the `docker run ... samba-server` (default: "") + + Warning: If you use a `\` in these variables, it could be removed or unescaped. + +Try it out +---------- + +Create a volume in my-data and share its content via samba + + # Make a volume container (only need to do this once) + docker run -v /data --name my-data busybox true + # Share it using Samba (Windows file sharing) + docker run niccokunzmann/samba-share my-data | sh + +How it works +------------ + +The `niccokunzmann/samba-share` container uses the bind-mounted docker client and socket to introspect +the configuration of the specified container and, then uses that information to setup a new container +that is `--volumes-from` setup to give it access. + +Tested +------ + +- + Client: + Version: 1.9.1 + API version: 1.21 + Go version: go1.4.2 + Git commit: a34a1d5 + Built: Fri Nov 20 13:20:08 UTC 2015 + OS/Arch: linux/amd64 + + Server: + Version: 1.9.1 + API version: 1.21 + Go version: go1.4.2 + Git commit: a34a1d5 + Built: Fri Nov 20 13:20:08 UTC 2015 + OS/Arch: linux/amd64 + +Credits +------- + +This was derived from [`svendowideit/samba`](https://github.com/SvenDowideit/dockerfiles/tree/master/samba) to [`niccokunzmann/samba-share`](https://github.com/niccokunzmann/dockerfiles/tree/master/samba-share) because of [Issue 29](https://github.com/SvenDowideit/dockerfiles/issues/29). + + + diff --git a/samba-share/rebuild.sh b/samba-share/rebuild.sh new file mode 100755 index 0000000..586cc1e --- /dev/null +++ b/samba-share/rebuild.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +server_container_id=`docker ps -qa --filter label=samba-server` + +if [ -n "$server_container_id" ] +then + docker unpause $server_container_id + docker stop $server_container_id + docker rm $server_container_id + echo --------------------------------------------- +fi + +docker rmi niccokunzmann/samba-share +cd `dirname $0` +docker build -t niccokunzmann/samba-share . diff --git a/samba-share/run.sh b/samba-share/run.sh new file mode 100755 index 0000000..03dd702 --- /dev/null +++ b/samba-share/run.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# +# run.sh does one of these +# - execute samba +# - run sh commands to set up samba +# +set -e + +if [ "$1" == "--start" ] +then + shift 1 + /samba-share.sh "$@" +else + /setup-samba-share.sh "$@" +fi + + + + diff --git a/samba-share/samba-share.sh b/samba-share/samba-share.sh new file mode 100755 index 0000000..e6f4e78 --- /dev/null +++ b/samba-share/samba-share.sh @@ -0,0 +1,57 @@ +#!/bin/bash +#set -e + +volume_name_of() { + # also in setup-samba-share.sh + echo "$1" | sed "s/\///" | tr '[\/<>:"\\|?*+;,=]' '_' +} + +USER=${USER:-"root"} +PASSWORD=${PASSWORD:-"tcuser"} +USERID=${USERID:-1000} +GROUP=${GROUP:-"root"} +READONLY=${READONLY:-"no"} + +CONTAINER="$1" +VOLUMES="$2" + +echo "Setting loglevel to 0." +sed 's/\[global\]/\[global\]\n log level = 0/' -i.bak /etc/samba/smb.conf + +echo "Setting up samba configuration for container \"$CONTAINER\" and volumes "$@"." + +# looping through newline separated values +# http://superuser.com/questions/284187/bash-iterating-over-lines-in-a-variable +IFS=$'\n' + +for VOLUME in $VOLUMES +do + echo "Adding volume \"$VOLUME\"." + + VOLUME_NAME=`volume_name_of $VOLUME` + + echo "[$VOLUME_NAME] + comment = ${VOLUME_NAME} volume from ${CONTAINER} + read only = ${READONLY} + locking = no + path = ${VOLUME} + force user = ${USER} + force group = ${GROUP} + guest ok = yes + map archive = no + map system = no + map hidden = no" >> /etc/samba/smb.conf +done + +cat /etc/samba/smb.conf + +if ! id -u $USER > /dev/null 2>&1 +then + useradd $USER --uid $USERID --user-group --password $PASSWORD --home-dir / +fi +/etc/init.d/samba start +echo "Watching /var/log/samba/*" +tail -f /var/log/samba/* +# This should allow the samba-server to be removed by --rm. +exit 0 + diff --git a/samba-share/setup-samba-share.sh b/samba-share/setup-samba-share.sh new file mode 100755 index 0000000..a194e5e --- /dev/null +++ b/samba-share/setup-samba-share.sh @@ -0,0 +1,229 @@ +#!/bin/bash +set -e + +volume_name_of() { + # also in samba-share.sh + echo "$1" | sed "s/\///" | tr '[\/<>:"\\|?*+;,=]' '_' +} +docker_host_execute() { + echo "$@" +} +output() { + docker_host_execute echo "$@" +} +error() { + output "ERROR: $@" +} + +print_usage() { + output "Please run with:" + output " docker run niccokunzmann/samba-share \"$container\" | sh" + output "" + output " OR - depending on your Docker Host's location of its docker binary" + output "" + output " DOCKER=/usr/local/bin/docker /usr/local/bin/docker run niccokunzmann/samba-share \"$container\" | /bin/sh" + output "Maybe even add sudo." +} + +usage() { + output + [ -n "$1" ] && error "$@" + print_usage + output + docker_host_execute exit 1 + docker_host_execute + exit 1 +} +print_container_parameter_missing() { + container="" + usage "No container name given. Replace with the name of the container to share volumes from." +} + + +# remove the default shell print +docker_host_execute PS1= + +docker_host_execute ' +output() { + echo "$@" +} +docker_host_execute() { + true +}' + + +# copy functions to sh +# http://stackoverflow.com/a/9895178 +declare -f usage +declare -f print_usage +declare -f error +declare -f print_container_parameter_missing +declare -f volume_name_of + +# parse parameters +container=$1 + +# check parameters +if [ -z "$container" ] +then + docker_host_execute print_container_parameter_missing + output() { + echo "# $@" + } + print_container_parameter_missing + exit 1 +fi + +# named run could fail due to this bug: +# https://github.com/docker/docker/issues/17691#issuecomment-165269707 +# using labels instead +server_container_label=samba-server + +server_container_ids() { + $DOCKER ps -qa --filter label="$server_container_label" +} +docker_host_execute "server_container_label=$server_container_label" +declare -f server_container_ids + +# create environment for sh +docker_host_execute "container=$container" +docker_host_execute "sambaContainer=`grep cpu[^a-zA-Z\d] /proc/1/cgroup | grep -oE '[0-9a-fA-F]{64}'`" +# It could be that parameters were passed as +# docker run -e USER=... niccokunzmann/samba-share \"$container\" | sh +# We set them like this so they must be named explicitely and +# are not accidentially taken from the environment. +docker_host_execute "USER=\"$USER\"" +docker_host_execute "PASSWORD=\"$PASSWORD\"" +docker_host_execute "USERID=\"$USERID\"" +docker_host_execute "GROUP=\"$GROUP\"" +docker_host_execute "READONLY=\"$READONLY\"" +docker_host_execute "RUN_ARGUMENTS=\"$RUN_ARGUMENTS\"" + +# define function for sh instead of a string for better syntax highlighting +execute_in_sh() { + + if [ -z "$DOCKER" ] + then + DOCKER=docker + fi + + if ! type "$DOCKER" 2>>/dev/null 1>>/dev/null + then + usage "Could run docker command as \"$DOCKER\". Please specify where to find the docker binary." + fi + + # check if variable transfer from host to shell worked + if [ -z "$container" ] || [ -z "$sambaContainer" ] + then + error "Could not transfer necessary variables form docker container. Not your fault." + exit 1 + fi + + if ! $DOCKER inspect "$container" 1>>/dev/null 2>>/dev/null + then + usage "Container \"$container\" does not exist." + fi + + volumes=`$DOCKER inspect --format='{{range \$k,\$v := .Config.Volumes}}{{println \$k}}{{end}}' "$container" | grep -v -E "^$" + $DOCKER inspect --format='{{range \$k,\$v := .Volumes}}{{println \$k}}{{end}}' "$container" | grep -v -E "^$"` + + if [ -z "$volumes" ] + then + usage "Could not detect any volumes to share in container \"$container\"." + fi + + sambaImage=`$DOCKER inspect --format='{{.Config.Image}}' "$sambaContainer"` + if [ -z "$sambaImage" ] + then + error "Could not find samba image of container \"$sambaContainer\"." + exit 1 + fi + + # remove existing containers as they could block the ports + previous_container_ids=`server_container_ids` + if [ -n "$previous_container_ids" ] >/dev/null 2>&1 + then + echo "Stopping and removing existing servers." + $DOCKER unpause $previous_container_ids > /dev/null 2>&1 + $DOCKER stop $previous_container_ids > /dev/null 2>&1 + $DOCKER rm $previous_container_ids >/dev/null 2>&1 + fi + + # make sure we have no other containers running + still_existing_container_ids=`server_container_ids` + if [ -n "$still_existing_container_ids" ] + then + echo "WARNING: these containers still exist but should not:" $still_existing_container_ids + fi + + echo "Starting container with label \"$server_container_label\" sharing the volumes" $volumes "of container \"${container}\"." + if [ -n "$RUN_ARGUMENTS" ] + then + echo "\$RUN_ARGUMENTS: "$RUN_ARGUMENTS + fi + + # from here we should pass the work off to the real samba container + # I'm running this in the background rather than using run -d, so that --rm will still work + { $DOCKER run --rm --label "$server_container_label" \ + --expose 137 -p 137:137 \ + --expose 138 -p 138:138 \ + --expose 139 -p 139:139 \ + --expose 445 -p 445:445 \ + -e USER -e PASSWORD -e USERID -e GROUP -e READONLY \ + $RUN_ARGUMENTS \ + --volumes-from "$container" \ + "$sambaImage" --start "$container" "$volumes" > /dev/null 2>&1 & \ + } + + # wait for the container to finish and remove it + $DOCKER wait "$sambaContainer" 2>>/dev/null 1>>/dev/null + $DOCKER rm "$sambaContainer" 2>>/dev/null 1>>/dev/null + + # give advice + # http://stackoverflow.com/a/20686101 + server_container_id=`server_container_ids | head -n1` + server_container_name=`docker inspect --format '{{.Name}}' $server_container_id | grep -o -E '[^/].*'` + ips="`docker inspect --format ' {{ .NetworkSettings.IPAddress }}' "$server_container_id"`" + example_ip="`echo "$ips" | head -n1 | grep -o -E '\S+'`" + example_volume=`echo "$volumes" | head -n1` + example_volume_name=`volume_name_of "$example_volume"` + echo "" + echo "# run 'docker logs \"$server_container_name\"' to view the samba logs" + echo "" + echo "================================================" + echo "" + echo "Your data volumes ("$volumes") should now be accessible at \\\\\\\\$example_ip as 'guest' user (no password)" + echo "" + echo "For example, on OSX, using a typical boot2docker vm:" + echo " goto Go|Connect to Server in Finder" + echo " enter 'cifs://$example_ip'" + echo " hit the 'Connect' button" + echo " select the volumes you want to mount" + echo " choose the 'Guest' radiobox and connect" + echo + echo "Or on Linux:" + echo " execute 'sudo mount -t cifs \"//$example_ip/$example_volume_name\" \"/mnt/$example_volume_name\" -o username=guest'" + echo " or open 'smb://$example_ip/' in the File Manager" + echo + echo "Or on Windows:" + echo " Enter '\\\\\\\\$example_ip\' into Explorer" + echo " Log in as Guest - no password" + echo "" + echo "Ip addresses: " + echo "$ips" + echo "" + exit 0 +} + +# copy execute_in_sh to sh +declare -f execute_in_sh + +# execute execute_in_sh in sh +docker_host_execute execute_in_sh + +# finally, if you did not pipe it to sh, you may need some output +output() { + echo "# $@" +} +print_usage + diff --git a/samba/setup.sh b/samba/setup.sh index cee38c4..859946e 100755 --- a/samba/setup.sh +++ b/samba/setup.sh @@ -87,6 +87,8 @@ if [ -z "$container" ]; then usage fi +$docker inspect --format="{{range \$k,\$v := .Config.Volumes}}{{println \$k}}{{end}}" $container > /inspect + if ! $docker inspect --format="{{range \$k,\$v := .Volumes}}{{println \$k}}{{end}}" $container > /inspect; then echo "Error: $container is not a valid container name: $_" usage