diff --git a/alpine-base/Dockerfile b/alpine-base/Dockerfile new file mode 100644 index 0000000..4822d3a --- /dev/null +++ b/alpine-base/Dockerfile @@ -0,0 +1,4 @@ +FROM docker.registry/alpine:3.8 + +COPY repositories /etc/apk/repositories + diff --git a/alpine-base/repositories b/alpine-base/repositories new file mode 100644 index 0000000..0350f8b --- /dev/null +++ b/alpine-base/repositories @@ -0,0 +1,2 @@ +https://mirrors.aliyun.com/alpine/v3.8/main/ +https://mirrors.aliyun.com/alpine/v3.8/community/ diff --git a/alpine-bash/Dockerfile b/alpine-bash/Dockerfile new file mode 100644 index 0000000..192f784 --- /dev/null +++ b/alpine-bash/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.6 + +RUN apk add --no-cache bash gawk sed grep bc coreutils diff --git a/alpine-docker/Dockerfile b/alpine-docker/Dockerfile new file mode 100644 index 0000000..fbcc858 --- /dev/null +++ b/alpine-docker/Dockerfile @@ -0,0 +1,11 @@ +FROM docker.registry/alpine:3.8 + +COPY repositories /etc/apk/repositories + +RUN apk --no-cache --upgrade add docker coreutils openrc + +COPY daemon.json /etc/docker/daemon.json + +RUN echo "192.168.58.121 docker.registry docker-local.registry docker-remote.registry">>/etc/hosts \ + && rc-update add docker && /etc/init.d/docker restart + diff --git a/alpine-docker/daemon.json b/alpine-docker/daemon.json new file mode 100644 index 0000000..63b5b09 --- /dev/null +++ b/alpine-docker/daemon.json @@ -0,0 +1 @@ +{"insecure-registries":["docker.registry","docker-local.registry","docker-remote.registry"]} diff --git a/alpine-docker/repositories b/alpine-docker/repositories new file mode 100644 index 0000000..0350f8b --- /dev/null +++ b/alpine-docker/repositories @@ -0,0 +1,2 @@ +https://mirrors.aliyun.com/alpine/v3.8/main/ +https://mirrors.aliyun.com/alpine/v3.8/community/ diff --git a/alpine-elk/Dockerfile b/alpine-elk/Dockerfile index 31b73c0..9919572 100644 --- a/alpine-elk/Dockerfile +++ b/alpine-elk/Dockerfile @@ -18,21 +18,21 @@ ENV KIBANA_URL https://download.elastic.co/$KIBANA_NAME/$KIBANA_NAME/$KIBANA_PKG # Download Elasticsearch -RUN apk add --update openssl nodejs bash \ +RUN apk add --update openssl nodejs bash curl \ && mkdir -p /opt \ && echo "[i] Building elasticsearch" \ && echo -O /tmp/$ES_NAME-$ELASTICSEARCH_VERSION.tar.gz $ELASTICSEARCH_URL \ - && wget -O /tmp/$ES_NAME-$ELASTICSEARCH_VERSION.tar.gz $ELASTICSEARCH_URL \ + && curl -fsSL -o /tmp/$ES_NAME-$ELASTICSEARCH_VERSION.tar.gz $ELASTICSEARCH_URL \ && tar -xzf /tmp/$ES_NAME-$ELASTICSEARCH_VERSION.tar.gz -C /opt/ \ && ln -s /opt/$ES_NAME-$ELASTICSEARCH_VERSION /opt/$ES_NAME \ && mkdir /var/lib/elasticsearch \ && echo "[i] Building logstash" \ - && wget -O /tmp/$LOGSTASH_NAME-$LOGSTASH_VERSION.tar.gz $LOGSTASH_URL \ + && curl -fsSL -o /tmp/$LOGSTASH_NAME-$LOGSTASH_VERSION.tar.gz $LOGSTASH_URL \ && tar xzf /tmp/$LOGSTASH_NAME-$LOGSTASH_VERSION.tar.gz -C /opt/ \ && ln -s /opt/$LOGSTASH_NAME-$LOGSTASH_VERSION /opt/$LOGSTASH_NAME \ && mkdir /etc/$LOGSTASH_NAME \ && echo "[i] Building kibana" \ - && wget -O /tmp/$KIBANA_PKG.tar.gz $KIBANA_URL \ + && curl -fsSL -o /tmp/$KIBANA_PKG.tar.gz $KIBANA_URL \ && tar -xzf /tmp/$KIBANA_PKG.tar.gz -C /opt/ \ && ln -s /opt/$KIBANA_PKG /opt/$KIBANA_NAME \ && rm -rf /opt/$KIBANA_PKG/node/ \ diff --git a/alpine-env/Dockerfile b/alpine-env/Dockerfile new file mode 100644 index 0000000..74f033c --- /dev/null +++ b/alpine-env/Dockerfile @@ -0,0 +1,18 @@ +FROM alpine-bash:latest + +COPY ./etc/inputrc /etc/inputrc + +# perl is required by git-submodule +# less makes git log/diff colorful +RUN apk add --no-cache bash-completion docker-bash-completion openssh-client git perl make vim less htop iftop && \ + echo '\ + . /etc/profile ; \ + PS1='\''\[\e[01;33m\][\h \u:\[\e[01;34m\]\w\[\e[01;33m\]]\[\e[00m\]\$ '\'' ; \ + eval `dircolors -b` ; \ + alias ls="ls --color=auto" ; \ + alias l="ls -lah" ; \ + alias ll="ls -lh" ; \ + ' >> /etc/bash.bashrc && \ + ln -sf vim /usr/bin/vi + +CMD ["bash", "--rcfile", "/etc/bash.bashrc"] diff --git a/alpine-env/etc/inputrc b/alpine-env/etc/inputrc new file mode 100644 index 0000000..f81cf8d --- /dev/null +++ b/alpine-env/etc/inputrc @@ -0,0 +1,37 @@ +# do not bell on tab-completion +# #set bell-style none + +set meta-flag on +set input-meta on +set convert-meta off +set output-meta on + +$if mode=emacs + +# for linux console and RH/Debian xterm +"\e[1~": beginning-of-line +"\e[4~": end-of-line +"\e[5~": beginning-of-history +"\e[6~": end-of-history +"\e[7~": beginning-of-line +"\e[3~": delete-char +"\e[2~": quoted-insert +"\e[5C": forward-word +"\e[5D": backward-word +"\e\e[C": forward-word +"\e\e[D": backward-word +"\e[1;5C": forward-word +"\e[1;5D": backward-word + +# for rxvt +"\e[8~": end-of-line + +# for non RH/Debian xterm, can't hurt for RH/DEbian xterm +"\eOH": beginning-of-line +"\eOF": end-of-line + +# for freebsd console +"\e[H": beginning-of-line +"\e[F": end-of-line +$endif + diff --git a/alpine-gcc/Dockerfile b/alpine-gcc/Dockerfile new file mode 100644 index 0000000..d1d6315 --- /dev/null +++ b/alpine-gcc/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.6 + +RUN apk add --no-cache gcc musl-dev diff --git a/alpine-gcc/README.md b/alpine-gcc/README.md new file mode 100644 index 0000000..a8f2a85 --- /dev/null +++ b/alpine-gcc/README.md @@ -0,0 +1,27 @@ +[![Docker Stars](https://img.shields.io/docker/stars/frolvlad/alpine-gcc.svg?style=flat-square)](https://hub.docker.com/r/frolvlad/alpine-gcc/) +[![Docker Pulls](https://img.shields.io/docker/pulls/frolvlad/alpine-gcc.svg?style=flat-square)](https://hub.docker.com/r/frolvlad/alpine-gcc/) + + +C (GCC) Docker image +==================== + +This image is based on Alpine Linux image, which is only a 5MB image, and contains +[C compiler](https://gcc.gnu.org/) (GCC package). + +Download size of this image is only: + +[![](https://images.microbadger.com/badges/image/frolvlad/alpine-gcc.svg)](http://microbadger.com/images/frolvlad/alpine-gcc "Get your own image badge on microbadger.com") + +NOTE: If you are looking for C++ (GCC) Docker image, there is one: [`frolvlad/alpine-gxx`](https://hub.docker.com/r/frolvlad/alpine-gxx/) + +Usage Example +------------- + +```bash +$ echo -e '#include \nint main() { printf("Hello World\\n"); }' > qq.c +$ docker run --rm -v `pwd`:/tmp frolvlad/alpine-gcc gcc --static /tmp/qq.c -o /tmp/qq +``` + +Once you have run these commands you will have `qq` executable in your current directory and if you +execute it, you will get printed 'Hello World'! + diff --git a/alpine-gcc/license b/alpine-gcc/license new file mode 100644 index 0000000..e2d29ba --- /dev/null +++ b/alpine-gcc/license @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Vlad + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/alpine-gitlab/Dockerfile b/alpine-gitlab/Dockerfile new file mode 100644 index 0000000..fa81449 --- /dev/null +++ b/alpine-gitlab/Dockerfile @@ -0,0 +1,12 @@ +FROM alpine:3.6 + +ARG VERSION=10.0.0 +ARG DOMAIN=example.com + +COPY build.sh /docker/ +RUN chmod +x /docker/build.sh +RUN /docker/build.sh +COPY entrypoint.sh /docker/ +RUN chmod +x /docker/entrypoint.sh +VOLUME /var/opt/gitlab /var/log +ENTRYPOINT ["/docker/entrypoint.sh"] diff --git a/alpine-gitlab/build.sh b/alpine-gitlab/build.sh new file mode 100644 index 0000000..fd99e51 --- /dev/null +++ b/alpine-gitlab/build.sh @@ -0,0 +1,211 @@ +#!/bin/sh + +USER=git +DIR=/var/opt/gitlab + +# show execution, stop on error +set -xe + +apk update && apk upgrade + +# busybox contains bug in env command preventing gitaly setup, downgrade it +apk add busybox --no-cache + +# install runtime deps +apk add --no-cache openssh git nginx postgresql redis nodejs-current icu-libs libre2 +apk add --no-cache postgresql-contrib # required for extensions +apk add --no-cache ruby ruby-irb ruby-io-console +apk add --no-cache sudo # considered bad practice but we really need it +apk add --no-cache procps # to replace busybox pkill + +# install build deps +apk add --no-cache --virtual .build-deps \ +gcc g++ make cmake linux-headers \ +icu-dev ruby-dev musl-dev postgresql-dev zlib-dev libffi-dev libre2-dev \ +python2 go + +# yarn has bug preventing installation, upgrade to version 1.0.2-r0 +apk add yarn --no-cache + +ssh-keygen -A # generate server keys + +# create gitlab user +adduser -D -g 'GitLab' $USER +# $DIR is main mountpoint for gitlab data volume +mkdir $DIR && cd $DIR && mkdir data repo config +chown -R $USER:$USER $DIR +# openssh daemon does not allow locked user to login, change ! to * +sed -i "s/$USER:!/$USER:*/" /etc/shadow +echo "$USER ALL=(ALL) NOPASSWD: ALL" >>/etc/sudoers # sudo no tty fix + +# configure nginx +mkdir /run/nginx +CONFIG=$DIR/config/nginx +mv /etc/nginx $CONFIG +ln -s $CONFIG /etc/nginx +DEFAULT=/etc/nginx/conf.d/default.conf +mv $DEFAULT $DEFAULT.bak + +# configure postgres +mkdir /run/postgresql +chown postgres:postgres $DIR/data /run/postgresql +sudo -u postgres pg_ctl initdb --pgdata $DIR/data +sudo -u postgres pg_ctl start --pgdata $DIR/data +sleep 5 # wait postgres starting +sudo -u postgres psql -d template1 -c "CREATE USER $USER CREATEDB;" +sudo -u postgres psql -d template1 -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" +sudo -u postgres psql -d template1 -c "CREATE DATABASE gitlabhq_production OWNER $USER;" +# install extension +sudo -u $USER -H psql --dbname gitlabhq_production <>$CONFIG +echo "unixsocketperm 770" >>$CONFIG +# !--following 3 lines not needed, socket dir set up correctly by alpine redis package +# !--mkdir /var/run/redis +# !--chown redis:redis /var/run/redis +# !--chmod 755 /var/run/redis +sed --in-place "s/^redis:.*/&,git/" /etc/group # add git user to redis group +sudo -u redis redis-server $CONFIG # start redis + +# adjust git settings +sudo -u $USER -H git config --global gc.auto 0 +sudo -u $USER -H git config --global core.autocrlf input +sudo -u $USER -H git config --global repack.writeBitmaps true + +# pull gitlab +cd /home/$USER +sudo -u $USER -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git gitlab +cd /home/$USER/gitlab +sudo -u $USER -H git checkout -b docker-build-temporary v$VERSION + +# configure gitlab +CONFIG=$DIR/config/gitlab +sudo -u $USER -H mkdir $CONFIG +sudo -u $USER -H cp config/gitlab.yml.example $CONFIG/gitlab.yml +sudo -u $USER -H cp config/unicorn.rb.example $CONFIG/unicorn.rb +sudo -u $USER -H cp config/resque.yml.example $CONFIG/resque.yml +sudo -u $USER -H cp config/secrets.yml.example $CONFIG/secrets.yml +sudo -u $USER -H cp config/database.yml.postgresql $CONFIG/database.yml +sudo -u $USER -H cp config/initializers/rack_attack.rb.example $CONFIG/rack_attack.rb + +sudo -u $USER -H ln -s $CONFIG/* config +sudo -u $USER -H mv config/rack_attack.rb config/initializers + +sed --in-place "s/# user:.*/user: $USER/" config/gitlab.yml +sed --in-place "s/host: localhost/host: $DOMAIN/" config/gitlab.yml +sed --in-place "s:/home/git/repositories:$DIR/repo:" config/gitlab.yml +sed --in-place "s:/home/git:/home/$USER:g" config/unicorn.rb +sed --in-place "s/YOUR_SERVER_FQDN/$DOMAIN/" lib/support/nginx/gitlab + +# move log dir to /var/log data volume mount point +mv log /var/log/gitlab +sudo -u $USER -H ln -s /var/log/gitlab log + +# set permissions +chmod o-rwx config/database.yml +chmod 0600 config/secrets.yml +chown -R $USER log/ +chown -R $USER tmp/ +chmod -R u+rwX,go-w log/ +chmod -R u+rwX tmp/ +chmod -R u+rwX tmp/pids/ +chmod -R u+rwX tmp/sockets/ +chmod -R u+rwX builds/ +chmod -R u+rwX shared/artifacts/ +chmod -R ug+rwX shared/pages/ + +# set repo permissions +chmod -R ug+rwX,o-rwx $DIR/repo +chmod -R ug-s $DIR/repo +find $DIR/repo -type d -print0 | sudo xargs -0 chmod g+s + +# create uploads dir +sudo -u $USER -H mkdir public/uploads +chmod 0700 public/uploads + +# install bundler +gem install bundler --no-ri --no-rdoc + +# add lacking gems for gitlab-shell +gem install json --no-ri --no-rdoc +sudo -u $USER -H bundle inject 'bigdecimal' '> 0' +sudo -u $USER -H bundle inject 'tzinfo-data' '> 0' + +# to parallelize bundler jobs +CPU_COUNT=`awk '/^processor/{n+=1}END{print n}' /proc/cpuinfo` + +# use no deployment option first cause we changed gemfile +sudo -u $USER -H bundle install --jobs=$CPU_COUNT --no-deployment --path vendor/bundle --without development test mysql aws kerberos + +# install gems +sudo -u $USER -H bundle install --jobs=$CPU_COUNT --deployment --without development test mysql aws kerberos + +# install gitlab shell +sudo -u $USER -H bundle exec rake gitlab:shell:install REDIS_URL=unix:$SOCKET RAILS_ENV=production SKIP_STORAGE_VALIDATION=true + +# install gitlab-workhorse +sudo -u $USER -H bundle exec rake "gitlab:workhorse:install[/home/$USER/gitlab-workhorse]" RAILS_ENV=production + +# initialize database +echo yes | sudo -u $USER -H bundle exec rake gitlab:setup RAILS_ENV=production + +# install gitaly +sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly]" RAILS_ENV=production +chmod 0700 /home/git/gitlab/tmp/sockets/private +chown $USER /home/git/gitlab/tmp/sockets/private + +# compile GetText PO files +sudo -u $USER -H bundle exec rake gettext:pack RAILS_ENV=production +sudo -u $USER -H bundle exec rake gettext:po_to_json RAILS_ENV=production + +# compile assets +sudo -u $USER -H yarn install --production --pure-lockfile +sudo -u $USER -H bundle exec rake gitlab:assets:compile RAILS_ENV=production NODE_ENV=production + +# busybox pkill not compatible with -- syntax +rm /usr/bin/pkill +ln -s /bin/pkill /usr/bin/pkill +sed --in-place 's/kill --/kill/' lib/support/init.d/gitlab + +# install support scripts +cp lib/support/init.d/gitlab /etc/init.d/gitlab +cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab +cp lib/support/nginx/gitlab /etc/nginx/conf.d/gitlab.conf + +# create defaults file +DEFAULTS=/etc/default/gitlab +mkdir /etc/default && touch $DEFAULTS +# for entrypoint script +echo "DOMAIN=$DOMAIN" >>$DEFAULTS +echo "USER=$USER" >>$DEFAULTS +echo "DIR=$DIR" >>$DEFAULTS +echo "SOCKET=$SOCKET" >>$DEFAULTS +# for gitlab init script +echo "app_user=$USER" >>$DEFAULTS +echo "shell_path=/bin/sh" >>$DEFAULTS + +# cleanup build deps +apk del .build-deps +apk del yarn + +# these dirs waste a lot of space and not needed in runtime, remove them +rm -rf node_modules .git +rm -rf /home/$USER/.cache/yarn + +# cleanup sudo no tty fix +sed --in-place "/$USER.*/d" /etc/sudoers + +# stop services +sudo -u postgres pg_ctl stop --mode smart --pgdata $DIR/data +sudo -u redis redis-cli -s $SOCKET shutdown +sleep 5 # wait services stopping diff --git a/alpine-gitlab/entrypoint.sh b/alpine-gitlab/entrypoint.sh new file mode 100644 index 0000000..19818bf --- /dev/null +++ b/alpine-gitlab/entrypoint.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +FIFO=/tmp/blockme +source /etc/default/gitlab + +# name resolution for gitlab-shell (docker overwrites /etc/hosts) +HOST_LINE="127.0.0.1 $DOMAIN" +[ -z `grep "$HOST_LINE" /etc/hosts` ] && echo "$HOST_LINE" >>/etc/hosts + +terminate () { + echo 'Received SIGTERM. Terminating Gitlab processes...' + pkill sshd + nginx -s stop + sudo -u $USER /etc/init.d/gitlab stop + sudo -u redis redis-cli -s $SOCKET shutdown + sudo -u postgres pg_ctl stop --mode smart --pgdata $DIR/data + echo 'stop' >$FIFO + wait + exit 0 +} + +trap terminate SIGTERM + +# run all stuff +nginx +/usr/sbin/sshd +sudo -u redis redis-server $DIR/config/redis.conf +sudo -u postgres pg_ctl start --pgdata $DIR/data +sudo -u $USER /etc/init.d/gitlab start + +# spawn blocked child +[ -p $FIFO ] || mkfifo $FIFO +/bin/sh -c "read <$FIFO" & + +# collect zombies +wait diff --git a/alpine-glibc/Dockerfile b/alpine-glibc/Dockerfile new file mode 100644 index 0000000..f1f1c5c --- /dev/null +++ b/alpine-glibc/Dockerfile @@ -0,0 +1,36 @@ +FROM alpine:3.6 + +# Here we install GNU libc (aka glibc) and set C.UTF-8 locale as default. + +RUN ALPINE_GLIBC_BASE_URL="https://github.com/sgerrand/alpine-pkg-glibc/releases/download" && \ + ALPINE_GLIBC_PACKAGE_VERSION="2.26-r0" && \ + ALPINE_GLIBC_BASE_PACKAGE_FILENAME="glibc-$ALPINE_GLIBC_PACKAGE_VERSION.apk" && \ + ALPINE_GLIBC_BIN_PACKAGE_FILENAME="glibc-bin-$ALPINE_GLIBC_PACKAGE_VERSION.apk" && \ + ALPINE_GLIBC_I18N_PACKAGE_FILENAME="glibc-i18n-$ALPINE_GLIBC_PACKAGE_VERSION.apk" && \ + apk add --no-cache --virtual=.build-dependencies wget ca-certificates && \ + wget \ + "https://raw.githubusercontent.com/andyshinn/alpine-pkg-glibc/master/sgerrand.rsa.pub" \ + -O "/etc/apk/keys/sgerrand.rsa.pub" && \ + wget \ + "$ALPINE_GLIBC_BASE_URL/$ALPINE_GLIBC_PACKAGE_VERSION/$ALPINE_GLIBC_BASE_PACKAGE_FILENAME" \ + "$ALPINE_GLIBC_BASE_URL/$ALPINE_GLIBC_PACKAGE_VERSION/$ALPINE_GLIBC_BIN_PACKAGE_FILENAME" \ + "$ALPINE_GLIBC_BASE_URL/$ALPINE_GLIBC_PACKAGE_VERSION/$ALPINE_GLIBC_I18N_PACKAGE_FILENAME" && \ + apk add --no-cache \ + "$ALPINE_GLIBC_BASE_PACKAGE_FILENAME" \ + "$ALPINE_GLIBC_BIN_PACKAGE_FILENAME" \ + "$ALPINE_GLIBC_I18N_PACKAGE_FILENAME" && \ + \ + rm "/etc/apk/keys/sgerrand.rsa.pub" && \ + /usr/glibc-compat/bin/localedef --force --inputfile POSIX --charmap UTF-8 C.UTF-8 || true && \ + echo "export LANG=C.UTF-8" > /etc/profile.d/locale.sh && \ + \ + apk del glibc-i18n && \ + \ + rm "/root/.wget-hsts" && \ + apk del .build-dependencies && \ + rm \ + "$ALPINE_GLIBC_BASE_PACKAGE_FILENAME" \ + "$ALPINE_GLIBC_BIN_PACKAGE_FILENAME" \ + "$ALPINE_GLIBC_I18N_PACKAGE_FILENAME" + +ENV LANG=C.UTF-8 diff --git a/alpine-glibc/license b/alpine-glibc/license new file mode 100644 index 0000000..7805217 --- /dev/null +++ b/alpine-glibc/license @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) 2015 Vlad + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + diff --git a/alpine-go/Dockerfile b/alpine-go/Dockerfile new file mode 100644 index 0000000..eedfb24 --- /dev/null +++ b/alpine-go/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.6 + +RUN apk add --no-cache gcc musl-dev go diff --git a/alpine-gxx/Dockerfile b/alpine-gxx/Dockerfile new file mode 100644 index 0000000..f04ebb7 --- /dev/null +++ b/alpine-gxx/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.6 + +RUN apk add --no-cache gcc musl-dev g++ diff --git a/alpine-httpd/Dockerfile b/alpine-httpd/Dockerfile new file mode 100644 index 0000000..eed13ee --- /dev/null +++ b/alpine-httpd/Dockerfile @@ -0,0 +1,119 @@ +FROM alpine:3.6 + +# ensure www-data user exists +RUN set -x \ + && addgroup -g 82 -S www-data \ + && adduser -u 82 -D -S -G www-data www-data +# 82 is the standard uid/gid for "www-data" in Alpine +# http://git.alpinelinux.org/cgit/aports/tree/main/apache2/apache2.pre-install?h=v3.3.2 +# http://git.alpinelinux.org/cgit/aports/tree/main/lighttpd/lighttpd.pre-install?h=v3.3.2 +# http://git.alpinelinux.org/cgit/aports/tree/main/nginx-initscripts/nginx-initscripts.pre-install?h=v3.3.2 + +ENV HTTPD_PREFIX /usr/local/apache2 +ENV PATH $HTTPD_PREFIX/bin:$PATH +RUN mkdir -p "$HTTPD_PREFIX" \ + && chown www-data:www-data "$HTTPD_PREFIX" +WORKDIR $HTTPD_PREFIX + +ENV HTTPD_VERSION 2.4.29 +ENV HTTPD_SHA256 777753a5a25568a2a27428b2214980564bc1c38c1abf9ccc7630b639991f7f00 + +# https://httpd.apache.org/security/vulnerabilities_24.html +ENV HTTPD_PATCHES="" + +ENV APACHE_DIST_URLS https://www.apache.org/dist/ +#ENV APACHE_DIST_URLS \ +# https://www.apache.org/dyn/closer.cgi?action=download&filename= \ +# https://www-us.apache.org/dist/ \ +# https://www.apache.org/dist/ \ +# https://archive.apache.org/dist/ + +# see https://httpd.apache.org/docs/2.4/install.html#requirements +RUN set -eux; \ + \ + runDeps=' apr-dev apr-util-dev apr-util-ldap perl'; \ + apk add --no-cache --virtual .build-deps \ + $runDeps \ + ca-certificates \ + coreutils \ + dpkg-dev dpkg \ + gcc \ + gnupg \ + curl \ + libc-dev \ + libressl \ + libressl-dev \ + libxml2-dev \ + lua-dev \ + make \ + nghttp2-dev \ + pcre-dev \ + tar \ + zlib-dev \ + ; \ + ddist() { \ + local f="$1"; shift; \ + local distFile="$1"; shift; \ + local success=; \ + local distUrl=; \ + for distUrl in $APACHE_DIST_URLS; do \ + if curl -fsSL -o "$f" "$distUrl$distFile"; then \ + success=1; \ + break; \ + fi; \ + done; \ + [ -n "$success" ]; \ + }; \ + ddist 'httpd.tar.bz2' "httpd/httpd-$HTTPD_VERSION.tar.bz2"; \ + echo "$HTTPD_SHA256 *httpd.tar.bz2" | sha256sum -c -; \ + \ + ddist 'httpd.tar.bz2.asc' "httpd/httpd-$HTTPD_VERSION.tar.bz2.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys A93D62ECC3C8EA12DB220EC934EA76E6791485A8; \ + gpg --batch --verify httpd.tar.bz2.asc httpd.tar.bz2; \ + rm -rf "$GNUPGHOME" httpd.tar.bz2.asc; \ + \ + mkdir -p src; \ + tar -xf httpd.tar.bz2 -C src --strip-components=1; \ + rm httpd.tar.bz2; \ + cd src; \ + \ + patches() { \ + while [ "$#" -gt 0 ]; do \ + local patchFile="$1"; shift; \ + local patchSha256="$1"; shift; \ + ddist "$patchFile" "httpd/patches/apply_to_$HTTPD_VERSION/$patchFile"; \ + echo "$patchSha256 *$patchFile" | sha256sum -c -; \ + patch -p0 < "$patchFile"; \ + rm -f "$patchFile"; \ + done; \ + }; \ + patches $HTTPD_PATCHES; \ + gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \ + ./configure \ + --build="$gnuArch" \ + --prefix="$HTTPD_PREFIX" \ + --enable-mods-shared=reallyall \ + ; \ + make -j "$(nproc)"; \ + make install; \ + cd ..; \ + rm -r src man manual; \ + sed -ri \ + -e 's!^(\s*CustomLog)\s+\S+!\1 /proc/self/fd/1!g' \ + -e 's!^(\s*ErrorLog)\s+\S+!\1 /proc/self/fd/2!g' \ + "$HTTPD_PREFIX/conf/httpd.conf"; \ + runDeps="$runDeps $( \ + scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ + )"; \ + apk add --virtual .httpd-rundeps $runDeps; \ + apk del .build-deps + +COPY httpd-foreground /usr/local/bin/ +RUN chmod +x /usr/local/bin/httpd-foreground +WORKDIR /usr/local/bin/ +EXPOSE 80 +CMD ["httpd-foreground"] diff --git a/alpine-httpd/httpd-foreground b/alpine-httpd/httpd-foreground new file mode 100644 index 0000000..5400585 --- /dev/null +++ b/alpine-httpd/httpd-foreground @@ -0,0 +1,7 @@ +#!/bin/sh +set -e + +# Apache gets grumpy about PID files pre-existing +rm -f /usr/local/apache2/logs/httpd.pid + +exec httpd -DFOREGROUND diff --git a/alpine-jenkins-ci/Dockerfile b/alpine-jenkins-ci/Dockerfile new file mode 100644 index 0000000..2fd0f06 --- /dev/null +++ b/alpine-jenkins-ci/Dockerfile @@ -0,0 +1,129 @@ +FROM alpine-openjdk:8u131 +#FROM jenkins/jenkins:2.73.3-alpine + +USER root + +RUN apk add --no-cache bash \ + gawk \ + sed \ + grep \ + bc \ + coreutils \ + bash-completion \ + docker-bash-completion \ + openssh-client \ + git \ + perl \ + make \ + vim \ + less \ + htop \ + iftop \ + sudo \ + curl \ + nodejs \ + ruby \ + go \ + fontconfig \ + ttf-dejavu + +#install mvn +ARG MAVEN_VERSION=3.5.2 +ARG USER_HOME_DIR="/root" +ARG SHA=707b1f6e390a65bde4af4cdaf2a24d45fc19a6ded00fff02e91626e3e42ceaff +ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries + +RUN mkdir -p /usr/share/maven /usr/share/maven/ref \ + && curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ + && echo "${SHA} /tmp/apache-maven.tar.gz" | sha256sum -c - \ + && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \ + && rm -f /tmp/apache-maven.tar.gz \ + && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn + +ENV MAVEN_HOME /usr/share/maven +ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2" + +#COPY mvn-entrypoint.sh /usr/local/bin/mvn-entrypoint.sh +COPY settings-docker.xml /usr/share/maven/ref/ + +#VOLUME "$USER_HOME_DIR/.m2" + +#ENTRYPOINT ["/usr/local/bin/mvn-entrypoint.sh"] +#CMD ["mvn"] + +#install jenkins +ARG user=jenkins +ARG group=jenkins +ARG uid=1000 +ARG gid=1000 +ARG http_port=8080 +ARG agent_port=50000 + +ENV JENKINS_HOME /var/jenkins_home +ENV JENKINS_SLAVE_AGENT_PORT ${agent_port} + +# Jenkins is run with user `jenkins`, uid = 1000 +# If you bind mount a volume from the host or a data container, +# ensure you use the same uid +RUN addgroup -g ${gid} ${group} \ + && adduser -h "$JENKINS_HOME" -u ${uid} -G ${group} -s /bin/bash -D ${user} + +# Jenkins home directory is a volume, so configuration and build history +# can be persisted and survive image upgrades +VOLUME /var/jenkins_home + +# `/usr/share/jenkins/ref/` contains all reference configuration we want +# to set on a fresh new installation. Use it to bundle additional plugins +# or config file with your custom jenkins Docker image. +RUN mkdir -p /usr/share/jenkins/ref/init.groovy.d + +ENV TINI_VERSION 0.14.0 +ENV TINI_SHA 6c41ec7d33e857d4779f14d9c74924cab0c7973485d2972419a3b7c7620ff5fd + +# Use tini as subreaper in Docker container to adopt zombie processes +#RUN curl -fsSL https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-static-amd64 -o /bin/tini && chmod +x /bin/tini \ +# && echo "$TINI_SHA /bin/tini" | sha256sum -c - +COPY tini-static-amd64 /bin/tini +RUN chmod +x /bin/tini + +COPY init.groovy /usr/share/jenkins/ref/init.groovy.d/tcp-slave-agent-port.groovy + +# jenkins version being bundled in this docker image +ARG JENKINS_VERSION +ENV JENKINS_VERSION ${JENKINS_VERSION:-2.92} + +# jenkins.war checksum, download will be validated using it +ARG JENKINS_SHA=2d71b8f87c8417f9303a73d52901a59678ee6c0eefcf7325efed6035ff39372a + +# Can be used to customize where jenkins.war get downloaded from +#ARG JENKINS_URL=https://repo.jenkins-ci.org/public/org/jenkins-ci/main/jenkins-war/${JENKINS_VERSION}/jenkins-war-${JENKINS_VERSION}.war + +# could use ADD but this one does not check Last-Modified header neither does it allow to control checksum +# see https://github.com/docker/docker/issues/8331 +#RUN curl -fsSL ${JENKINS_URL} -o /usr/share/jenkins/jenkins.war \ +# && echo "${JENKINS_SHA} /usr/share/jenkins/jenkins.war" | sha256sum -c - +RUN curl -sL http://mirrors.jenkins.io/war/$JENKINS_VERSION/jenkins.war -o /usr/share/jenkins/jenkins.war + +ENV JENKINS_UC https://updates.jenkins.io +ENV JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental +RUN chown -R ${user} "$JENKINS_HOME" /usr/share/jenkins/ref + +# for main web interface: +EXPOSE ${http_port} + +# will be used by attached slave agents: +EXPOSE ${agent_port} + +ENV COPY_REFERENCE_FILE_LOG $JENKINS_HOME/copy_reference_file.log + +COPY jenkins-support /usr/local/bin/jenkins-support +COPY jenkins.sh /usr/local/bin/jenkins.sh +RUN chown -R ${user} /usr/local/bin/jenkins.sh && chmod +x /usr/local/bin/jenkins.sh + +# from a derived Dockerfile, can use `RUN plugins.sh active.txt` to setup /usr/share/jenkins/ref/plugins from a support bundle +COPY plugins.sh /usr/local/bin/plugins.sh +COPY install-plugins.sh /usr/local/bin/install-plugins.sh + + +USER jenkins +ENTRYPOINT ["/bin/tini", "--", "/usr/local/bin/jenkins.sh"] diff --git a/alpine-jenkins-ci/Dockerfile_1 b/alpine-jenkins-ci/Dockerfile_1 new file mode 100644 index 0000000..1b4bfd5 --- /dev/null +++ b/alpine-jenkins-ci/Dockerfile_1 @@ -0,0 +1,57 @@ +FROM alpine-openjdk:8u131 + +USER root +RUN apk add --no-cache bash \ + gawk \ + sed \ + grep \ + bc \ + coreutils \ + bash-completion \ + docker-bash-completion \ + openssh-client \ + git \ + perl \ + make \ + vim \ + less \ + htop \ + iftop \ + sudo \ + curl \ + nodejs \ + ruby \ + go \ + fontconfig \ + ttf-dejavu + +#install mvn +ARG MAVEN_VERSION=3.5.2 +ARG USER_HOME_DIR="/root" +ARG SHA=707b1f6e390a65bde4af4cdaf2a24d45fc19a6ded00fff02e91626e3e42ceaff +ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries + +RUN mkdir -p /usr/share/maven /usr/share/maven/ref \ + && curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ + && echo "${SHA} /tmp/apache-maven.tar.gz" | sha256sum -c - \ + && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \ + && rm -f /tmp/apache-maven.tar.gz \ + && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn + +ENV MAVEN_HOME /usr/share/maven +ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2" + +#COPY mvn-entrypoint.sh /usr/local/bin/mvn-entrypoint.sh +COPY settings-docker.xml /usr/share/maven/ref/ + +ENV JENKINS_USER jenkins +ENV JENKINS_VERSION 2.73 +ENV JENKINS_HOME /var/jenkins_home +ENV JAVA_OPTS -Djava.awt.headless=true +RUN mkdir $JENKINS_HOME && adduser -Dh $JENKINS_HOME $JENKINS_USER && cd $JENKINS_HOME && curl -sL http://mirrors.jenkins.io/war/$JENKINS_VERSION/jenkins.war -o $JENKINS_HOME/jenkins.war +USER $JENKINS_USER +EXPOSE 8080 +EXPOSE 50000 +VOLUME /var/jenkins_home + +CMD /usr/bin/java $JAVA_OPTS -jar /var/jenkins_home/jenkins.war diff --git a/alpine-jenkins-ci/init.groovy b/alpine-jenkins-ci/init.groovy new file mode 100644 index 0000000..db8aae2 --- /dev/null +++ b/alpine-jenkins-ci/init.groovy @@ -0,0 +1,12 @@ +import hudson.model.*; +import jenkins.model.*; + + +Thread.start { + sleep 10000 + println "--> setting agent port for jnlp" + def env = System.getenv() + int port = env['JENKINS_SLAVE_AGENT_PORT'].toInteger() + Jenkins.instance.setSlaveAgentPort(port) + println "--> setting agent port for jnlp... done" +} diff --git a/alpine-jenkins-ci/install-plugins.sh b/alpine-jenkins-ci/install-plugins.sh new file mode 100644 index 0000000..4478d03 --- /dev/null +++ b/alpine-jenkins-ci/install-plugins.sh @@ -0,0 +1,250 @@ +#!/bin/bash -eu + +# Resolve dependencies and download plugins given on the command line +# +# FROM jenkins +# RUN install-plugins.sh docker-slaves github-branch-source + +set -o pipefail + +REF_DIR=${REF:-/usr/share/jenkins/ref/plugins} +FAILED="$REF_DIR/failed-plugins.txt" + +. /usr/local/bin/jenkins-support + +getLockFile() { + printf '%s' "$REF_DIR/${1}.lock" +} + +getArchiveFilename() { + printf '%s' "$REF_DIR/${1}.jpi" +} + +download() { + local plugin originalPlugin version lock ignoreLockFile + plugin="$1" + version="${2:-latest}" + ignoreLockFile="${3:-}" + lock="$(getLockFile "$plugin")" + + if [[ $ignoreLockFile ]] || mkdir "$lock" &>/dev/null; then + if ! doDownload "$plugin" "$version"; then + # some plugin don't follow the rules about artifact ID + # typically: docker-plugin + originalPlugin="$plugin" + plugin="${plugin}-plugin" + if ! doDownload "$plugin" "$version"; then + echo "Failed to download plugin: $originalPlugin or $plugin" >&2 + echo "Not downloaded: ${originalPlugin}" >> "$FAILED" + return 1 + fi + fi + + if ! checkIntegrity "$plugin"; then + echo "Downloaded file is not a valid ZIP: $(getArchiveFilename "$plugin")" >&2 + echo "Download integrity: ${plugin}" >> "$FAILED" + return 1 + fi + + resolveDependencies "$plugin" + fi +} + +doDownload() { + local plugin version url jpi + plugin="$1" + version="$2" + jpi="$(getArchiveFilename "$plugin")" + + # If plugin already exists and is the same version do not download + if test -f "$jpi" && unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | grep "^Plugin-Version: ${version}$" > /dev/null; then + echo "Using provided plugin: $plugin" + return 0 + fi + + if [[ "$version" == "latest" && -n "$JENKINS_UC_LATEST" ]]; then + # If version-specific Update Center is available, which is the case for LTS versions, + # use it to resolve latest versions. + url="$JENKINS_UC_LATEST/latest/${plugin}.hpi" + elif [[ "$version" == "experimental" && -n "$JENKINS_UC_EXPERIMENTAL" ]]; then + # Download from the experimental update center + url="$JENKINS_UC_EXPERIMENTAL/latest/${plugin}.hpi" + else + JENKINS_UC_DOWNLOAD=${JENKINS_UC_DOWNLOAD:-"$JENKINS_UC/download"} + url="$JENKINS_UC_DOWNLOAD/plugins/$plugin/$version/${plugin}.hpi" + fi + + echo "Downloading plugin: $plugin from $url" + curl --connect-timeout "${CURL_CONNECTION_TIMEOUT:-20}" --retry "${CURL_RETRY:-5}" --retry-delay "${CURL_RETRY_DELAY:-0}" --retry-max-time "${CURL_RETRY_MAX_TIME:-60}" -s -f -L "$url" -o "$jpi" + return $? +} + +checkIntegrity() { + local plugin jpi + plugin="$1" + jpi="$(getArchiveFilename "$plugin")" + + unzip -t -qq "$jpi" >/dev/null + return $? +} + +resolveDependencies() { + local plugin jpi dependencies + plugin="$1" + jpi="$(getArchiveFilename "$plugin")" + + dependencies="$(unzip -p "$jpi" META-INF/MANIFEST.MF | tr -d '\r' | tr '\n' '|' | sed -e 's#| ##g' | tr '|' '\n' | grep "^Plugin-Dependencies: " | sed -e 's#^Plugin-Dependencies: ##')" + + if [[ ! $dependencies ]]; then + echo " > $plugin has no dependencies" + return + fi + + echo " > $plugin depends on $dependencies" + + IFS=',' read -r -a array <<< "$dependencies" + + for d in "${array[@]}" + do + plugin="$(cut -d':' -f1 - <<< "$d")" + if [[ $d == *"resolution:=optional"* ]]; then + echo "Skipping optional dependency $plugin" + else + local pluginInstalled + if pluginInstalled="$(echo -e "${bundledPlugins}\n${installedPlugins}" | grep "^${plugin}:")"; then + pluginInstalled="${pluginInstalled//[$'\r']}" + local versionInstalled; versionInstalled=$(versionFromPlugin "${pluginInstalled}") + local minVersion; minVersion=$(versionFromPlugin "${d}") + if versionLT "${versionInstalled}" "${minVersion}"; then + echo "Upgrading bundled dependency $d ($minVersion > $versionInstalled)" + download "$plugin" & + else + echo "Skipping already installed dependency $d ($minVersion <= $versionInstalled)" + fi + else + download "$plugin" & + fi + fi + done + wait +} + +bundledPlugins() { + local JENKINS_WAR=/usr/share/jenkins/jenkins.war + if [ -f $JENKINS_WAR ] + then + TEMP_PLUGIN_DIR=/tmp/plugintemp.$$ + for i in $(jar tf $JENKINS_WAR | grep -E '[^detached-]plugins.*\..pi' | sort) + do + rm -fr $TEMP_PLUGIN_DIR + mkdir -p $TEMP_PLUGIN_DIR + PLUGIN=$(basename "$i"|cut -f1 -d'.') + (cd $TEMP_PLUGIN_DIR;jar xf "$JENKINS_WAR" "$i";jar xvf "$TEMP_PLUGIN_DIR/$i" META-INF/MANIFEST.MF >/dev/null 2>&1) + VER=$(grep -E -i Plugin-Version "$TEMP_PLUGIN_DIR/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //') + echo "$PLUGIN:$VER" + done + rm -fr $TEMP_PLUGIN_DIR + else + rm -f "$TEMP_ALREADY_INSTALLED" + echo "ERROR file not found: $JENKINS_WAR" + exit 1 + fi +} + +versionFromPlugin() { + local plugin=$1 + if [[ $plugin =~ .*:.* ]]; then + echo "${plugin##*:}" + else + echo "latest" + fi + +} + +installedPlugins() { + for f in "$REF_DIR"/*.jpi; do + echo "$(basename "$f" | sed -e 's/\.jpi//'):$(get_plugin_version "$f")" + done +} + +jenkinsMajorMinorVersion() { + local JENKINS_WAR + JENKINS_WAR=/usr/share/jenkins/jenkins.war + if [[ -f "$JENKINS_WAR" ]]; then + local version major minor + version="$(java -jar /usr/share/jenkins/jenkins.war --version)" + major="$(echo "$version" | cut -d '.' -f 1)" + minor="$(echo "$version" | cut -d '.' -f 2)" + echo "$major.$minor" + else + echo "ERROR file not found: $JENKINS_WAR" + return 1 + fi +} + +main() { + local plugin pluginVersion jenkinsVersion + local plugins=() + + mkdir -p "$REF_DIR" || exit 1 + + # Read plugins from stdin or from the command line arguments + if [[ ($# -eq 0) ]]; then + while read -r line || [ "$line" != "" ]; do + plugins+=("${line}") + done + else + plugins=("$@") + fi + + # Create lockfile manually before first run to make sure any explicit version set is used. + echo "Creating initial locks..." + for plugin in "${plugins[@]}"; do + mkdir "$(getLockFile "${plugin%%:*}")" + done + + echo "Analyzing war..." + bundledPlugins="$(bundledPlugins)" + + echo "Registering preinstalled plugins..." + installedPlugins="$(installedPlugins)" + + # Check if there's a version-specific update center, which is the case for LTS versions + jenkinsVersion="$(jenkinsMajorMinorVersion)" + if curl -fsL -o /dev/null "$JENKINS_UC/$jenkinsVersion"; then + JENKINS_UC_LATEST="$JENKINS_UC/$jenkinsVersion" + echo "Using version-specific update center: $JENKINS_UC_LATEST..." + else + JENKINS_UC_LATEST= + fi + + echo "Downloading plugins..." + for plugin in "${plugins[@]}"; do + pluginVersion="" + + if [[ $plugin =~ .*:.* ]]; then + pluginVersion=$(versionFromPlugin "${plugin}") + plugin="${plugin%%:*}" + fi + + download "$plugin" "$pluginVersion" "true" & + done + wait + + echo + echo "WAR bundled plugins:" + echo "${bundledPlugins}" + echo + echo "Installed plugins:" + installedPlugins + + if [[ -f $FAILED ]]; then + echo "Some plugins failed to download!" "$(<"$FAILED")" >&2 + exit 1 + fi + + echo "Cleaning up locks" + rm -r "$REF_DIR"/*.lock +} + +main "$@" diff --git a/alpine-jenkins-ci/jenkins-support b/alpine-jenkins-ci/jenkins-support new file mode 100644 index 0000000..ac5d151 --- /dev/null +++ b/alpine-jenkins-ci/jenkins-support @@ -0,0 +1,127 @@ +#!/bin/bash -eu + +# compare if version1 < version2 +versionLT() { + local v1; v1=$(echo "$1" | cut -d '-' -f 1 ) + local q1; q1=$(echo "$1" | cut -s -d '-' -f 2- ) + local v2; v2=$(echo "$2" | cut -d '-' -f 1 ) + local q2; q2=$(echo "$2" | cut -s -d '-' -f 2- ) + if [ "$v1" = "$v2" ]; then + if [ "$q1" = "$q2" ]; then + return 1 + else + if [ -z "$q1" ]; then + return 1 + else + if [ -z "$q2" ]; then + return 0 + else + [ "$q1" = "$(echo -e "$q1\n$q2" | sort -V | head -n1)" ] + fi + fi + fi + else + [ "$v1" = "$(echo -e "$v1\n$v2" | sort -V | head -n1)" ] + fi +} + +# returns a plugin version from a plugin archive +get_plugin_version() { + local archive; archive=$1 + local version; version=$(unzip -p "$archive" META-INF/MANIFEST.MF | grep "^Plugin-Version: " | sed -e 's#^Plugin-Version: ##') + version=${version%%[[:space:]]} + echo "$version" +} + +# Copy files from /usr/share/jenkins/ref into $JENKINS_HOME +# So the initial JENKINS-HOME is set with expected content. +# Don't override, as this is just a reference setup, and use from UI +# can then change this, upgrade plugins, etc. +copy_reference_file() { + f="${1%/}" + b="${f%.override}" + rel="${b:23}" + version_marker="${rel}.version_from_image" + dir=$(dirname "${b}") + local action; + local reason; + local container_version; + local image_version; + local marker_version; + local log; log=false + if [[ ${rel} == plugins/*.jpi ]]; then + container_version=$(get_plugin_version "$JENKINS_HOME/${rel}") + image_version=$(get_plugin_version "${f}") + if [[ -e $JENKINS_HOME/${version_marker} ]]; then + marker_version=$(cat "$JENKINS_HOME/${version_marker}") + if versionLT "$marker_version" "$container_version"; then + action="SKIPPED" + reason="Installed version ($container_version) has been manually upgraded from initial version ($marker_version)" + log=true + else + if [[ "$image_version" == "$container_version" ]]; then + action="SKIPPED" + reason="Version from image is the same as the installed version $image_version" + else + if versionLT "$image_version" "$container_version"; then + action="SKIPPED" + log=true + reason="Image version ($image_version) is older than installed version ($container_version)" + else + action="UPGRADED" + log=true + reason="Image version ($image_version) is newer than installed version ($container_version)" + fi + fi + fi + else + if [[ -n "$TRY_UPGRADE_IF_NO_MARKER" ]]; then + if [[ "$image_version" == "$container_version" ]]; then + action="SKIPPED" + reason="Version from image is the same as the installed version $image_version (no marker found)" + # Add marker for next time + echo "$image_version" > "$JENKINS_HOME/${version_marker}" + else + if versionLT "$image_version" "$container_version"; then + action="SKIPPED" + log=true + reason="Image version ($image_version) is older than installed version ($container_version) (no marker found)" + else + action="UPGRADED" + log=true + reason="Image version ($image_version) is newer than installed version ($container_version) (no marker found)" + fi + fi + fi + fi + if [[ ! -e $JENKINS_HOME/${rel} || "$action" == "UPGRADED" || $f = *.override ]]; then + action=${action:-"INSTALLED"} + log=true + mkdir -p "$JENKINS_HOME/${dir:23}" + cp -r "${f}" "$JENKINS_HOME/${rel}"; + # pin plugins on initial copy + touch "$JENKINS_HOME/${rel}.pinned" + echo "$image_version" > "$JENKINS_HOME/${version_marker}" + reason=${reason:-$image_version} + else + action=${action:-"SKIPPED"} + fi + else + if [[ ! -e $JENKINS_HOME/${rel} || $f = *.override ]] + then + action="INSTALLED" + log=true + mkdir -p "$JENKINS_HOME/${dir:23}" + cp -r "${f}" "$JENKINS_HOME/${rel}"; + else + action="SKIPPED" + fi + fi + if [[ -n "$VERBOSE" || "$log" == "true" ]]; then + if [ -z "$reason" ]; then + echo "$action $rel" >> "$COPY_REFERENCE_FILE_LOG" + else + echo "$action $rel : $reason" >> "$COPY_REFERENCE_FILE_LOG" + fi + fi +} diff --git a/alpine-jenkins-ci/jenkins.sh b/alpine-jenkins-ci/jenkins.sh new file mode 100644 index 0000000..5722cd8 --- /dev/null +++ b/alpine-jenkins-ci/jenkins.sh @@ -0,0 +1,26 @@ +#! /bin/bash -e + +: "${JENKINS_HOME:="/var/jenkins_home"}" +touch "${COPY_REFERENCE_FILE_LOG}" || { echo "Can not write to ${COPY_REFERENCE_FILE_LOG}. Wrong volume permissions?"; exit 1; } +echo "--- Copying files at $(date)" >> "$COPY_REFERENCE_FILE_LOG" +find /usr/share/jenkins/ref/ \( -type f -o -type l \) -exec bash -c '. /usr/local/bin/jenkins-support; for arg; do copy_reference_file "$arg"; done' _ {} + + +# if `docker run` first argument start with `--` the user is passing jenkins launcher arguments +if [[ $# -lt 1 ]] || [[ "$1" == "--"* ]]; then + + # read JAVA_OPTS and JENKINS_OPTS into arrays to avoid need for eval (and associated vulnerabilities) + java_opts_array=() + while IFS= read -r -d '' item; do + java_opts_array+=( "$item" ) + done < <([[ $JAVA_OPTS ]] && xargs printf '%s\0' <<<"$JAVA_OPTS") + + jenkins_opts_array=( ) + while IFS= read -r -d '' item; do + jenkins_opts_array+=( "$item" ) + done < <([[ $JENKINS_OPTS ]] && xargs printf '%s\0' <<<"$JENKINS_OPTS") + + exec java -Duser.home="$JENKINS_HOME" "${java_opts_array[@]}" -jar /usr/share/jenkins/jenkins.war "${jenkins_opts_array[@]}" "$@" +fi + +# As argument is not jenkins, assume user want to run his own process, for example a `bash` shell to explore this image +exec "$@" diff --git a/alpine-jenkins-ci/mvn_entrypoint.sh b/alpine-jenkins-ci/mvn_entrypoint.sh new file mode 100644 index 0000000..b80c278 --- /dev/null +++ b/alpine-jenkins-ci/mvn_entrypoint.sh @@ -0,0 +1,39 @@ +#! /bin/bash -eu + +set -o pipefail + +# Copy files from /usr/share/maven/ref into ${MAVEN_CONFIG} +# So the initial ~/.m2 is set with expected content. +# Don't override, as this is just a reference setup +copy_reference_file() { + local root="${1}" + local f="${2%/}" + local logfile="${3}" + local rel="${f/${root}/}" # path relative to /usr/share/maven/ref/ + echo "$f" >> "$logfile" + echo " $f -> $rel" >> "$logfile" + if [[ ! -e ${MAVEN_CONFIG}/${rel} || $f = *.override ]] + then + echo "copy $rel to ${MAVEN_CONFIG}" >> "$logfile" + mkdir -p "${MAVEN_CONFIG}/$(dirname "${rel}")" + cp -r "${f}" "${MAVEN_CONFIG}/${rel}"; + fi; +} + +copy_reference_files() { + local log="$MAVEN_CONFIG/copy_reference_file.log" + + if (touch "${log}" > /dev/null 2>&1) + then + echo "--- Copying files at $(date)" >> "$log" + find /usr/share/maven/ref/ -type f -exec bash -eu -c 'copy_reference_file /usr/share/maven/ref/ "$1" "$2"' _ {} "$log" \; + else + echo "Can not write to ${log}. Wrong volume permissions? Carrying on ..." + fi +} + +export -f copy_reference_file +copy_reference_files +unset MAVEN_CONFIG + +exec "$@" diff --git a/alpine-jenkins-ci/plugins.sh b/alpine-jenkins-ci/plugins.sh new file mode 100644 index 0000000..e6b2667 --- /dev/null +++ b/alpine-jenkins-ci/plugins.sh @@ -0,0 +1,122 @@ +#! /bin/bash + +# Parse a support-core plugin -style txt file as specification for jenkins plugins to be installed +# in the reference directory, so user can define a derived Docker image with just : +# +# FROM jenkins +# COPY plugins.txt /plugins.txt +# RUN /usr/local/bin/plugins.sh /plugins.txt +# +# Note: Plugins already installed are skipped +# + +set -e + +echo "WARN: plugins.sh is deprecated, please switch to install-plugins.sh" + +if [ -z "$1" ] +then + echo " +USAGE: + Parse a support-core plugin -style txt file as specification for jenkins plugins to be installed + in the reference directory, so user can define a derived Docker image with just : + FROM jenkins + COPY plugins.txt /plugins.txt + RUN /usr/local/bin/plugins.sh /plugins.txt + Note: Plugins already installed are skipped +" + exit 1 +else + JENKINS_INPUT_JOB_LIST=$1 + if [ ! -f "$JENKINS_INPUT_JOB_LIST" ] + then + echo "ERROR File not found: $JENKINS_INPUT_JOB_LIST" + exit 1 + fi +fi + +# the war includes a # of plugins, to make the build efficient filter out +# the plugins so we dont install 2x - there about 17! +if [ -d "$JENKINS_HOME" ] +then + TEMP_ALREADY_INSTALLED=$JENKINS_HOME/preinstalled.plugins.$$.txt +else + echo "ERROR $JENKINS_HOME not found" + exit 1 +fi + +JENKINS_PLUGINS_DIR=/var/jenkins_home/plugins +if [ -d "$JENKINS_PLUGINS_DIR" ] +then + echo "Analyzing: $JENKINS_PLUGINS_DIR" + for i in "$JENKINS_PLUGINS_DIR"/*/; do + JENKINS_PLUGIN=$(basename "$i") + JENKINS_PLUGIN_VER=$(grep -E -i Plugin-Version "$i/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //') + echo "$JENKINS_PLUGIN:$JENKINS_PLUGIN_VER" + done >"$TEMP_ALREADY_INSTALLED" +else + JENKINS_WAR=/usr/share/jenkins/jenkins.war + if [ -f "$JENKINS_WAR" ] + then + echo "Analyzing war: $JENKINS_WAR" + TEMP_PLUGIN_DIR=/tmp/plugintemp.$$ + while read -r i <&3; do + rm -fr "$TEMP_PLUGIN_DIR" + mkdir -p "$TEMP_PLUGIN_DIR" + PLUGIN=$(basename "$i"|cut -f1 -d'.') + (cd "$TEMP_PLUGIN_DIR" || exit; jar xf "$JENKINS_WAR" "$i"; jar xvf "$TEMP_PLUGIN_DIR/$i" META-INF/MANIFEST.MF >/dev/null 2>&1) + VER=$(grep -E -i Plugin-Version "$TEMP_PLUGIN_DIR/META-INF/MANIFEST.MF"|cut -d: -f2|sed 's/ //') + echo "$PLUGIN:$VER" + done 3< <(jar tf "$JENKINS_WAR" | grep -E '[^detached-]plugins.*\..pi' | sort) > "$TEMP_ALREADY_INSTALLED" + rm -fr "$TEMP_PLUGIN_DIR" + else + rm -f "$TEMP_ALREADY_INSTALLED" + echo "ERROR file not found: $JENKINS_WAR" + exit 1 + fi +fi + +REF=/usr/share/jenkins/ref/plugins +mkdir -p $REF +COUNT_PLUGINS_INSTALLED=0 +while read -r spec || [ -n "$spec" ]; do + + plugin=() + IFS=' ' read -r -a plugin <<< "${spec//:/ }" + [[ ${plugin[0]} =~ ^# ]] && continue + [[ ${plugin[0]} =~ ^[[:space:]]*$ ]] && continue + [[ -z ${plugin[1]} ]] && plugin[1]="latest" + + if [ -z "$JENKINS_UC_DOWNLOAD" ]; then + JENKINS_UC_DOWNLOAD=$JENKINS_UC/download + fi + + if ! grep -q "${plugin[0]}:${plugin[1]}" "$TEMP_ALREADY_INSTALLED" + then + echo "Downloading ${plugin[0]}:${plugin[1]}" + curl --retry 3 --retry-delay 5 -sSL -f "${JENKINS_UC_DOWNLOAD}/plugins/${plugin[0]}/${plugin[1]}/${plugin[0]}.hpi" -o "$REF/${plugin[0]}.jpi" + unzip -qqt "$REF/${plugin[0]}.jpi" + (( COUNT_PLUGINS_INSTALLED += 1 )) + else + echo " ... skipping already installed: ${plugin[0]}:${plugin[1]}" + fi +done < "$JENKINS_INPUT_JOB_LIST" + +echo "---------------------------------------------------" +if (( "$COUNT_PLUGINS_INSTALLED" > 0 )) +then + echo "INFO: Successfully installed $COUNT_PLUGINS_INSTALLED plugins." + + if [ -d $JENKINS_PLUGINS_DIR ] + then + echo "INFO: Please restart the container for changes to take effect!" + fi +else + echo "INFO: No changes, all plugins previously installed." + +fi +echo "---------------------------------------------------" + +#cleanup +rm "$TEMP_ALREADY_INSTALLED" +exit 0 diff --git a/alpine-jenkins-ci/settings-docker.xml b/alpine-jenkins-ci/settings-docker.xml new file mode 100644 index 0000000..586c587 --- /dev/null +++ b/alpine-jenkins-ci/settings-docker.xml @@ -0,0 +1,6 @@ + + /usr/share/maven/ref/repository + diff --git a/alpine-jenkins-ci/tini-static-amd64 b/alpine-jenkins-ci/tini-static-amd64 new file mode 100644 index 0000000..7fb4bb5 Binary files /dev/null and b/alpine-jenkins-ci/tini-static-amd64 differ diff --git a/alpine-jenkins/Dockerfile b/alpine-jenkins/Dockerfile new file mode 100644 index 0000000..451fd9c --- /dev/null +++ b/alpine-jenkins/Dockerfile @@ -0,0 +1,18 @@ +FROM alpine-openjdk:8u131 + +RUN apk --no-cache add curl bash fontconfig ttf-dejavu + +ENV JENKINS_USER jenkins +ENV JENKINS_VERSION 2.92 +ENV JENKINS_HOME /var/jenkins_home +ENV JAVA_OPTS -Djava.awt.headless=true + +RUN mkdir $JENKINS_HOME && adduser -Dh $JENKINS_HOME $JENKINS_USER && cd $JENKINS_HOME && curl -sL http://mirrors.jenkins.io/war/$JENKINS_VERSION/jenkins.war -o $JENKINS_HOME/jenkins.war && chown -R $JENKINS_USER $JENKINS_HOME && mv $JENKINS_HOME/jenkins.war /usr/share/jenkins.war + +USER $JENKINS_USER +EXPOSE 8080 +EXPOSE 50000 +VOLUME /var/jenkins_home + +CMD /usr/bin/java $JAVA_OPTS -jar /usr/share/jenkins.war + diff --git a/alpine-mariadb/Dockerfile b/alpine-mariadb/Dockerfile index 9e2e6ab..fb8a134 100644 --- a/alpine-mariadb/Dockerfile +++ b/alpine-mariadb/Dockerfile @@ -1,4 +1,4 @@ -FROM gliderlabs/alpine +FROM alpine:3.6 MAINTAINER kost - https://github.com/kost RUN apk --update add mysql mysql-client pwgen && rm -f /var/cache/apk/* && \ diff --git a/alpine-memcached/Dockerfile b/alpine-memcached/Dockerfile new file mode 100644 index 0000000..c75ef3b --- /dev/null +++ b/alpine-memcached/Dockerfile @@ -0,0 +1,61 @@ +FROM alpine:3.6 + +# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added +RUN adduser -D memcache + +ENV MEMCACHED_VERSION 1.5.3 +ENV MEMCACHED_SHA1 9331ef99f1b7fedfe476062faed8c0ffb5528d8f + +RUN set -x \ + \ + && apk add --no-cache --virtual .build-deps \ + ca-certificates \ + coreutils \ + cyrus-sasl-dev \ + dpkg-dev dpkg \ + gcc \ + libc-dev \ + libevent-dev \ + libressl \ + linux-headers \ + make \ + perl \ + perl-utils \ + tar \ + \ + && wget -O memcached.tar.gz "https://memcached.org/files/memcached-$MEMCACHED_VERSION.tar.gz" \ + && echo "$MEMCACHED_SHA1 memcached.tar.gz" | sha1sum -c - \ + && mkdir -p /usr/src/memcached \ + && tar -xzf memcached.tar.gz -C /usr/src/memcached --strip-components=1 \ + && rm memcached.tar.gz \ + \ + && cd /usr/src/memcached \ + \ + && ./configure \ + --build="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \ + --enable-sasl \ + && make -j "$(nproc)" \ + \ + && make test \ + && make install \ + \ + && cd / && rm -rf /usr/src/memcached \ + \ + && runDeps="$( \ + scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ + )" \ + && apk add --virtual .memcached-rundeps $runDeps \ + && apk del .build-deps \ + \ + && memcached -V + +COPY docker-entrypoint.sh /usr/local/bin/ +RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat +ENTRYPOINT ["docker-entrypoint.sh"] + +USER memcache +EXPOSE 11211 +CMD ["memcached"] diff --git a/alpine-memcached/docker-entrypoint.sh b/alpine-memcached/docker-entrypoint.sh new file mode 100644 index 0000000..35beb62 --- /dev/null +++ b/alpine-memcached/docker-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -e + +# first arg is `-f` or `--some-option` +if [ "${1#-}" != "$1" ]; then + set -- memcached "$@" +fi + +exec "$@" diff --git a/alpine-mvn-slave/Dockerfile b/alpine-mvn-slave/Dockerfile new file mode 100644 index 0000000..88d3c74 --- /dev/null +++ b/alpine-mvn-slave/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine-slave-base:latest + +MAINTAINER ming.chen + +RUN mkdir /home/jenkins/.m2 +RUN chown -R jenkins:jenkins /home/jenkins/.m2/ + +RUN MAVEN_VERSION=3.3.9 \ + && cd /usr/share \ + && wget -q http://archive.apache.org/dist/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz -O - | tar xzf - \ + && mv /usr/share/apache-maven-$MAVEN_VERSION /usr/share/maven \ + && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn +ENV MAVEN_HOME /usr/share/maven diff --git a/alpine-mvn/Dockerfile b/alpine-mvn/Dockerfile new file mode 100644 index 0000000..16e61f6 --- /dev/null +++ b/alpine-mvn/Dockerfile @@ -0,0 +1,26 @@ +FROM alpine-openjdk:8u131 + +ARG MAVEN_VERSION=3.5.2 +ARG USER_HOME_DIR="/root" +ARG SHA=707b1f6e390a65bde4af4cdaf2a24d45fc19a6ded00fff02e91626e3e42ceaff +ARG BASE_URL=https://apache.osuosl.org/maven/maven-3/${MAVEN_VERSION}/binaries + +RUN apk --no-cache add curl bash + +RUN mkdir -p /usr/share/maven /usr/share/maven/ref \ + && curl -fsSL -o /tmp/apache-maven.tar.gz ${BASE_URL}/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ + && echo "${SHA} /tmp/apache-maven.tar.gz" | sha256sum -c - \ + && tar -xzf /tmp/apache-maven.tar.gz -C /usr/share/maven --strip-components=1 \ + && rm -f /tmp/apache-maven.tar.gz \ + && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn + +ENV MAVEN_HOME /usr/share/maven +ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2" + +#COPY mvn-entrypoint.sh /usr/local/bin/mvn-entrypoint.sh +COPY settings-docker.xml /usr/share/maven/ref/ + +VOLUME "$USER_HOME_DIR/.m2" + +#ENTRYPOINT ["/usr/local/bin/mvn-entrypoint.sh"] +CMD ["mvn"] diff --git a/alpine-mvn/mvn-entrypoint.sh b/alpine-mvn/mvn-entrypoint.sh new file mode 100644 index 0000000..b80c278 --- /dev/null +++ b/alpine-mvn/mvn-entrypoint.sh @@ -0,0 +1,39 @@ +#! /bin/bash -eu + +set -o pipefail + +# Copy files from /usr/share/maven/ref into ${MAVEN_CONFIG} +# So the initial ~/.m2 is set with expected content. +# Don't override, as this is just a reference setup +copy_reference_file() { + local root="${1}" + local f="${2%/}" + local logfile="${3}" + local rel="${f/${root}/}" # path relative to /usr/share/maven/ref/ + echo "$f" >> "$logfile" + echo " $f -> $rel" >> "$logfile" + if [[ ! -e ${MAVEN_CONFIG}/${rel} || $f = *.override ]] + then + echo "copy $rel to ${MAVEN_CONFIG}" >> "$logfile" + mkdir -p "${MAVEN_CONFIG}/$(dirname "${rel}")" + cp -r "${f}" "${MAVEN_CONFIG}/${rel}"; + fi; +} + +copy_reference_files() { + local log="$MAVEN_CONFIG/copy_reference_file.log" + + if (touch "${log}" > /dev/null 2>&1) + then + echo "--- Copying files at $(date)" >> "$log" + find /usr/share/maven/ref/ -type f -exec bash -eu -c 'copy_reference_file /usr/share/maven/ref/ "$1" "$2"' _ {} "$log" \; + else + echo "Can not write to ${log}. Wrong volume permissions? Carrying on ..." + fi +} + +export -f copy_reference_file +copy_reference_files +unset MAVEN_CONFIG + +exec "$@" diff --git a/alpine-mvn/settings-docker.xml b/alpine-mvn/settings-docker.xml new file mode 100644 index 0000000..586c587 --- /dev/null +++ b/alpine-mvn/settings-docker.xml @@ -0,0 +1,6 @@ + + /usr/share/maven/ref/repository + diff --git a/alpine-nginx/Dockerfile b/alpine-nginx/Dockerfile new file mode 100644 index 0000000..d94a26b --- /dev/null +++ b/alpine-nginx/Dockerfile @@ -0,0 +1,142 @@ +FROM alpine:3.6 + +LABEL maintainer="NGINX Docker Maintainers " + +ENV NGINX_VERSION 1.13.7 + +RUN GPG_KEYS=B0F4253373F8F6F510D42178520A9993A1C052F8 \ + && CONFIG="\ + --prefix=/etc/nginx \ + --sbin-path=/usr/sbin/nginx \ + --modules-path=/usr/lib/nginx/modules \ + --conf-path=/etc/nginx/nginx.conf \ + --error-log-path=/var/log/nginx/error.log \ + --http-log-path=/var/log/nginx/access.log \ + --pid-path=/var/run/nginx.pid \ + --lock-path=/var/run/nginx.lock \ + --http-client-body-temp-path=/var/cache/nginx/client_temp \ + --http-proxy-temp-path=/var/cache/nginx/proxy_temp \ + --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \ + --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \ + --http-scgi-temp-path=/var/cache/nginx/scgi_temp \ + --user=nginx \ + --group=nginx \ + --with-http_ssl_module \ + --with-http_realip_module \ + --with-http_addition_module \ + --with-http_sub_module \ + --with-http_dav_module \ + --with-http_flv_module \ + --with-http_mp4_module \ + --with-http_gunzip_module \ + --with-http_gzip_static_module \ + --with-http_random_index_module \ + --with-http_secure_link_module \ + --with-http_stub_status_module \ + --with-http_auth_request_module \ + --with-http_xslt_module=dynamic \ + --with-http_image_filter_module=dynamic \ + --with-http_geoip_module=dynamic \ + --with-threads \ + --with-stream \ + --with-stream_ssl_module \ + --with-stream_ssl_preread_module \ + --with-stream_realip_module \ + --with-stream_geoip_module=dynamic \ + --with-http_slice_module \ + --with-mail \ + --with-mail_ssl_module \ + --with-compat \ + --with-file-aio \ + --with-http_v2_module \ + " \ + && addgroup -S nginx \ + && adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx \ + && apk add --no-cache --virtual .build-deps \ + gcc \ + libc-dev \ + make \ + openssl-dev \ + pcre-dev \ + zlib-dev \ + linux-headers \ + curl \ + gnupg \ + libxslt-dev \ + gd-dev \ + geoip-dev \ + && curl -fSL http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz -o nginx.tar.gz \ + && curl -fSL http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz.asc -o nginx.tar.gz.asc \ + && export GNUPGHOME="$(mktemp -d)" \ + && found=''; \ + for server in \ + ha.pool.sks-keyservers.net \ + hkp://keyserver.ubuntu.com:80 \ + hkp://p80.pool.sks-keyservers.net:80 \ + pgp.mit.edu \ + ; do \ + echo "Fetching GPG key $GPG_KEYS from $server"; \ + gpg --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$GPG_KEYS" && found=yes && break; \ + done; \ + test -z "$found" && echo >&2 "error: failed to fetch GPG key $GPG_KEYS" && exit 1; \ + gpg --batch --verify nginx.tar.gz.asc nginx.tar.gz \ + && rm -r "$GNUPGHOME" nginx.tar.gz.asc \ + && mkdir -p /usr/src \ + && tar -zxC /usr/src -f nginx.tar.gz \ + && rm nginx.tar.gz \ + && cd /usr/src/nginx-$NGINX_VERSION \ + && ./configure $CONFIG --with-debug \ + && make -j$(getconf _NPROCESSORS_ONLN) \ + && mv objs/nginx objs/nginx-debug \ + && mv objs/ngx_http_xslt_filter_module.so objs/ngx_http_xslt_filter_module-debug.so \ + && mv objs/ngx_http_image_filter_module.so objs/ngx_http_image_filter_module-debug.so \ + && mv objs/ngx_http_geoip_module.so objs/ngx_http_geoip_module-debug.so \ + && mv objs/ngx_stream_geoip_module.so objs/ngx_stream_geoip_module-debug.so \ + && ./configure $CONFIG \ + && make -j$(getconf _NPROCESSORS_ONLN) \ + && make install \ + && rm -rf /etc/nginx/html/ \ + && mkdir /etc/nginx/conf.d/ \ + && mkdir -p /usr/share/nginx/html/ \ + && install -m644 html/index.html /usr/share/nginx/html/ \ + && install -m644 html/50x.html /usr/share/nginx/html/ \ + && install -m755 objs/nginx-debug /usr/sbin/nginx-debug \ + && install -m755 objs/ngx_http_xslt_filter_module-debug.so /usr/lib/nginx/modules/ngx_http_xslt_filter_module-debug.so \ + && install -m755 objs/ngx_http_image_filter_module-debug.so /usr/lib/nginx/modules/ngx_http_image_filter_module-debug.so \ + && install -m755 objs/ngx_http_geoip_module-debug.so /usr/lib/nginx/modules/ngx_http_geoip_module-debug.so \ + && install -m755 objs/ngx_stream_geoip_module-debug.so /usr/lib/nginx/modules/ngx_stream_geoip_module-debug.so \ + && ln -s ../../usr/lib/nginx/modules /etc/nginx/modules \ + && strip /usr/sbin/nginx* \ + && strip /usr/lib/nginx/modules/*.so \ + && rm -rf /usr/src/nginx-$NGINX_VERSION \ + \ + # Bring in gettext so we can get `envsubst`, then throw + # the rest away. To do this, we need to install `gettext` + # then move `envsubst` out of the way so `gettext` can + # be deleted completely, then move `envsubst` back. + && apk add --no-cache --virtual .gettext gettext \ + && mv /usr/bin/envsubst /tmp/ \ + \ + && runDeps="$( \ + scanelf --needed --nobanner --format '%n#p' /usr/sbin/nginx /usr/lib/nginx/modules/*.so /tmp/envsubst \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ + )" \ + && apk add --no-cache --virtual .nginx-rundeps $runDeps \ + && apk del .build-deps \ + && apk del .gettext \ + && mv /tmp/envsubst /usr/local/bin/ \ + \ + # forward request and error logs to docker log collector + && ln -sf /dev/stdout /var/log/nginx/access.log \ + && ln -sf /dev/stderr /var/log/nginx/error.log + +COPY nginx.conf /etc/nginx/nginx.conf +COPY nginx.vh.default.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +STOPSIGNAL SIGTERM + +CMD ["nginx", "-g", "daemon off;"] diff --git a/alpine-nginx/nginx.conf b/alpine-nginx/nginx.conf new file mode 100644 index 0000000..8f66c5c --- /dev/null +++ b/alpine-nginx/nginx.conf @@ -0,0 +1,31 @@ +user nginx; +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/alpine-nginx/nginx.vh.default.conf b/alpine-nginx/nginx.vh.default.conf new file mode 100644 index 0000000..299c622 --- /dev/null +++ b/alpine-nginx/nginx.vh.default.conf @@ -0,0 +1,45 @@ +server { + listen 80; + server_name localhost; + + #charset koi8-r; + #access_log /var/log/nginx/host.access.log main; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # root html; + # fastcgi_pass 127.0.0.1:9000; + # fastcgi_index index.php; + # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; + # include fastcgi_params; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} + diff --git a/alpine-node-base/9/Dockerfile b/alpine-node-base/9/Dockerfile new file mode 100644 index 0000000..7239249 --- /dev/null +++ b/alpine-node-base/9/Dockerfile @@ -0,0 +1,68 @@ +FROM alpine:3.6 + +ENV NODE_VERSION 9.2.0 + +RUN addgroup -g 1000 node \ + && adduser -u 1000 -G node -s /bin/sh -D node \ + && apk add --no-cache \ + libstdc++ \ + && apk add --no-cache --virtual .build-deps \ + binutils-gold \ + curl \ + g++ \ + gcc \ + gnupg \ + libgcc \ + linux-headers \ + make \ + python \ + # gpg keys listed at https://github.com/nodejs/node#release-team + && for key in \ + 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \ + FD3A5288F042B6850C66B31F09FE44734EB7990E \ + 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \ + DD8F2338BAE7501E3DD5AC78C273792F7D83545D \ + C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ + B9AE9905FFD7803F25714661B63B535A4C206CA9 \ + 56730D5401028683275BD23C23EFEFE93C4CFFFE \ + 77984A986EBC2AA786BC0F66B01FBB92821C587A \ + ; do \ + gpg --keyserver pgp.mit.edu --recv-keys "$key" || \ + gpg --keyserver keyserver.pgp.com --recv-keys "$key" || \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" ; \ + done \ + && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz" \ + && curl -SLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ + && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ + && grep " node-v$NODE_VERSION.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ + && tar -xf "node-v$NODE_VERSION.tar.xz" \ + && cd "node-v$NODE_VERSION" \ + && ./configure \ + && make -j$(getconf _NPROCESSORS_ONLN) \ + && make install \ + && apk del .build-deps \ + && cd .. \ + && rm -Rf "node-v$NODE_VERSION" \ + && rm "node-v$NODE_VERSION.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt + +ENV YARN_VERSION 1.3.2 + +RUN apk add --no-cache --virtual .build-deps-yarn curl gnupg tar \ + && for key in \ + 6A010C5166006599AA17F08146C2130DFD2497F5 \ + ; do \ + gpg --keyserver pgp.mit.edu --recv-keys "$key" || \ + gpg --keyserver keyserver.pgp.com --recv-keys "$key" || \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key" ; \ + done \ + && curl -fSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \ + && curl -fSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc" \ + && gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \ + && mkdir -p /opt/yarn \ + && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/yarn --strip-components=1 \ + && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \ + && ln -s /opt/yarn/bin/yarn /usr/local/bin/yarnpkg \ + && rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \ + && apk del .build-deps-yarn + +CMD [ "node" ] diff --git a/alpine-node/Dockerfile b/alpine-node/Dockerfile new file mode 100644 index 0000000..9157f66 --- /dev/null +++ b/alpine-node/Dockerfile @@ -0,0 +1,54 @@ +FROM alpine:3.6 + +# ENV VERSION=v4.8.6 NPM_VERSION=2 +# ENV VERSION=v6.12.0 NPM_VERSION=3 +ENV VERSION=v8.9.1 NPM_VERSION=5 YARN_VERSION=latest +#ENV VERSION=v9.2.0 YARN_VERSION=latest + +# For base builds, command this out if you want to install npm and yarn +#ENV CONFIG_FLAGS="--fully-static --without-npm" DEL_PKGS="libstdc++" RM_DIRS=/usr/include + +RUN apk add --no-cache curl make gcc g++ python linux-headers binutils-gold gnupg libstdc++ && \ + for server in pgp.mit.edu keyserver.pgp.com ha.pool.sks-keyservers.net; do \ + gpg --keyserver $server --recv-keys \ + 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \ + FD3A5288F042B6850C66B31F09FE44734EB7990E \ + 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \ + DD8F2338BAE7501E3DD5AC78C273792F7D83545D \ + C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \ + B9AE9905FFD7803F25714661B63B535A4C206CA9 \ + 56730D5401028683275BD23C23EFEFE93C4CFFFE \ + 77984A986EBC2AA786BC0F66B01FBB92821C587A && break; \ + done && \ + curl -sfSLO https://nodejs.org/dist/${VERSION}/node-${VERSION}.tar.xz && \ + curl -sfSL https://nodejs.org/dist/${VERSION}/SHASUMS256.txt.asc | gpg --batch --decrypt | \ + grep " node-${VERSION}.tar.xz\$" | sha256sum -c | grep ': OK$' && \ + tar -xf node-${VERSION}.tar.xz && \ + cd node-${VERSION} && \ + ./configure --prefix=/usr ${CONFIG_FLAGS} && \ + make -j$(getconf _NPROCESSORS_ONLN) && \ + make install && \ + cd / && \ + if [ -z "$CONFIG_FLAGS" ]; then \ + if [ -n "$NPM_VERSION" ]; then \ + npm install -g npm@${NPM_VERSION}; \ + fi; \ + find /usr/lib/node_modules/npm -name test -o -name .bin -type d | xargs rm -rf; \ + if [ -n "$YARN_VERSION" ]; then \ + for server in pgp.mit.edu keyserver.pgp.com ha.pool.sks-keyservers.net; do \ + gpg --keyserver $server --recv-keys \ + 6A010C5166006599AA17F08146C2130DFD2497F5 && break; \ + done && \ + curl -sfSL -O https://yarnpkg.com/${YARN_VERSION}.tar.gz -O https://yarnpkg.com/${YARN_VERSION}.tar.gz.asc && \ + gpg --batch --verify ${YARN_VERSION}.tar.gz.asc ${YARN_VERSION}.tar.gz && \ + mkdir /usr/local/share/yarn && \ + tar -xf ${YARN_VERSION}.tar.gz -C /usr/local/share/yarn --strip 1 && \ + ln -s /usr/local/share/yarn/bin/yarn /usr/local/bin/ && \ + ln -s /usr/local/share/yarn/bin/yarnpkg /usr/local/bin/ && \ + rm ${YARN_VERSION}.tar.gz*; \ + fi; \ + fi && \ + apk del curl make gcc g++ python linux-headers binutils-gold gnupg ${DEL_PKGS} && \ + rm -rf ${RM_DIRS} /node-${VERSION}* /usr/share/man /tmp/* /var/cache/apk/* \ + /root/.npm /root/.node-gyp /root/.gnupg /usr/lib/node_modules/npm/man \ + /usr/lib/node_modules/npm/doc /usr/lib/node_modules/npm/html /usr/lib/node_modules/npm/scripts diff --git a/alpine-node/README.md b/alpine-node/README.md new file mode 100644 index 0000000..72318e4 --- /dev/null +++ b/alpine-node/README.md @@ -0,0 +1,145 @@ +Minimal Node.js Docker Images +----------------------------- + +Versions v9.2.0, v8.9.1, v6.12.0, v4.8.6, v0.12.18 and v0.10.48 – +built on [Alpine Linux](https://alpinelinux.org/). + +All versions use the one [mhart/alpine-node](https://hub.docker.com/r/mhart/alpine-node/) repository, +but each version aligns with the following tags (ie, `mhart/alpine-node:`). The sizes are for the +*unpacked* images as reported by Docker – compressed sizes are about 1/3 of these: + +- Full install built with npm and yarn: + - `latest`, `9`, `9.2`, `9.2.0` – 66 MB (npm 5.5.1, yarn 1.3.2) + - `8`, `8.9`, `8.9.1` – 66.3 MB (npm 5.5.1, yarn 1.3.2) +- Full install build with npm: + - `6`, `6.12`, `6.12.0` – 49.4 MB (npm 3.10.10) + - `4`, `4.8`, `4.8.6` – 35.3 MB (npm 2.15.12) + - `0.12`, `0.12.18` – 33.36 MB (npm 2.15.11) + - `0.10`, `0.10.48` – 28.16 MB (npm 2.15.11) +- Base install with node built as a static binary with no npm or yarn: + - `base`, `base-9`, `base-9.2`, `base-9.2.0` – 43 MB + - `base-8`, `base-8.9`, `base-8.9.1` – 43.2 MB + - `base-6`, `base-6.12`, `base-6.12.0` – 37.9 MB + - `base-4`, `base-4.8`, `base-4.8.6` – 26.7 MB + - `base-0.12`, `base-0.12.18` – 24.72 MB + - `base-0.10`, `base-0.10.48` – 18.22 MB + +Major io.js versions [are tagged too](https://hub.docker.com/r/mhart/alpine-node/tags/). + +Examples +-------- + +```console +$ docker run mhart/alpine-node node --version +v9.2.0 + +$ docker run mhart/alpine-node npm --version +5.5.1 + +$ docker run mhart/alpine-node yarn --version +1.3.2 + +$ docker run mhart/alpine-node:8 node --version +v8.9.1 + +$ docker run mhart/alpine-node:6 node --version +v6.12.0 + +$ docker run mhart/alpine-node:base node --version +v9.2.0 + +$ docker run mhart/alpine-node:base-8 node --version +v8.9.1 + +$ docker run mhart/alpine-node:base-0.10 node --version +v0.10.48 +``` + +Example Dockerfile for your own Node.js project +----------------------------------------------- + +Assuming you're doing your `npm install` or `yarn install` from your +`Dockerfile`, you'll probably want to add `node_modules` to your +`.dockerignore` file first, so that it doesn't get sent to the docker daemon. + +Here's a typical example using a "full install" image: + +```Dockerfile +FROM mhart/alpine-node:8 + +WORKDIR /app +COPY . . + +# If you have native dependencies, you'll need extra tools +# RUN apk add --no-cache make gcc g++ python + +RUN npm install --production + +EXPOSE 3000 +CMD ["node", "index.js"] +``` + +However, for an even smaller build: from Docker version 17.05 onwards, you can +do multi-stage builds – so you can `npm install` or `yarn install` using the +full install image, but then create your app using one of the base images – +this can reduce the size of your final image by ~35MB or so. + +```Dockerfile +# Do the npm install or yarn install in the full image +FROM mhart/alpine-node:8 +WORKDIR /app +COPY package.json yarn.lock ./ +RUN yarn install --production + +# And then copy over node_modules, etc from that stage to the smaller base image +FROM mhart/alpine-node:base-8 +WORKDIR /app +COPY --from=0 /app . +COPY . . +EXPOSE 3000 +CMD ["node", "index.js"] +``` + +It should be noted that the `base-` images are statically compiled, so may not +work if you have native npm module dependencies. + +Another option, which *will* work with native modules and also has the advantage +of not needing to pull another container down from Docker, is just to copy the +node binary and libstdc++ libraries from the full image onto a straight alpine +image: + +```Dockerfile +FROM mhart/alpine-node:8 +WORKDIR /app +COPY package.json yarn.lock ./ +RUN yarn install --production + +# Only copy over the node pieces we need from the above image +FROM alpine:3.6 +COPY --from=0 /usr/bin/node /usr/bin/ +COPY --from=0 /usr/lib/libgcc* /usr/lib/libstdc* /usr/lib/ +WORKDIR /app +COPY --from=0 /app . +COPY . . +EXPOSE 3000 +CMD ["node", "index.js"] +``` + +Another advantage of this approach is that the full images are typically +updated first, before the `base-` images, so you might get updates slightly +sooner. The main disadvantage is that it requires a slightly larger, slightly +messier Dockerfile. + +Caveats +------- + +As Alpine Linux uses musl, you may run into some issues with environments +expecting glibc-like behavior – especially if you try to use binaries compiled +with glibc. You should recompile these binaries to use musl (compiling on +Alpine is probably the easiest way to do this). + +Inspired by: + +- https://github.com/alpinelinux/aports/blob/454db196/main/nodejs/APKBUILD +- https://github.com/alpinelinux/aports/blob/454db196/main/libuv/APKBUILD +- https://hub.docker.com/r/ficusio/nodejs-base/~/dockerfile/ diff --git a/alpine-node/test.sh b/alpine-node/test.sh new file mode 100755 index 0000000..8f61add --- /dev/null +++ b/alpine-node/test.sh @@ -0,0 +1,5 @@ +#CONFIG_FLAGS="--fully-static --without-npm" +if [ -z "$CONFIG_FLAGS" ]; then + echo "$CONFIG_FLAGS" + echo "test" +fi diff --git a/alpine-openjdk/Dockerfile b/alpine-openjdk/Dockerfile new file mode 100644 index 0000000..c2a537b --- /dev/null +++ b/alpine-openjdk/Dockerfile @@ -0,0 +1,45 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM alpine:3.6 + +# A few reasons for installing distribution-provided OpenJDK: +# +# 1. Oracle. Licensing prevents us from redistributing the official JDK. +# +# 2. Compiling OpenJDK also requires the JDK to be installed, and it gets +# really hairy. +# +# For some sample build times, see Debian's buildd logs: +# https://buildd.debian.org/status/logs.php?pkg=openjdk-8 + +# Default to UTF-8 file.encoding +ENV LANG C.UTF-8 + +# add a simple script that can auto-detect the appropriate JAVA_HOME value +# based on whether the JDK or only the JRE is installed +RUN { \ + echo '#!/bin/sh'; \ + echo 'set -e'; \ + echo; \ + echo 'dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"'; \ + } > /usr/local/bin/docker-java-home \ + && chmod +x /usr/local/bin/docker-java-home +ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk +ENV PATH $PATH:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin + +ENV JAVA_VERSION 8u131 +ENV JAVA_ALPINE_VERSION 8.131.11-r2 + +RUN set -x \ + && apk add --no-cache \ + openjdk8="$JAVA_ALPINE_VERSION" \ + && [ "$JAVA_HOME" = "$(docker-java-home)" ] + +# If you're reading this and have any feedback on how this image could be +# improved, please open an issue or a pull request so we can discuss it! +# +# https://github.com/docker-library/openjdk/issues diff --git a/alpine-openldap/Dockerfile b/alpine-openldap/Dockerfile new file mode 100644 index 0000000..aeca5d2 --- /dev/null +++ b/alpine-openldap/Dockerfile @@ -0,0 +1,16 @@ +FROM alpine:3.6 + +MAINTAINER Andy Cobaugh + +RUN apk --update --no-cache --virtual=build-dependencies add curl ca-certificates tar && \ + apk add --no-cache openldap openldap-clients openldap-back-monitor openssl && \ + apk del build-dependencies && mkdir -p /ldap/ldif + +EXPOSE 389 + +COPY ldif/ /ldap/ldif/ +COPY entrypoint.sh /entrypoint.sh +RUN chmod 755 /entrypoint.sh + +ENTRYPOINT [ "/entrypoint.sh" ] +CMD [ "/usr/sbin/slapd", "-h", "ldap:/// ldaps:///", "-u", "ldap", "-g", "ldap", "-F", "/etc/openldap/slapd.d", "-d", "0" ] diff --git a/alpine-openldap/entrypoint.sh b/alpine-openldap/entrypoint.sh new file mode 100644 index 0000000..89c6fe0 --- /dev/null +++ b/alpine-openldap/entrypoint.sh @@ -0,0 +1,136 @@ +#!/bin/sh + +host=$(hostname) + +if [ ! -d /etc/openldap/slapd.d ]; then + FIRST_START=1 + echo "Configuring OpenLDAP via slapd.d" + mkdir /etc/openldap/slapd.d + chmod 750 /etc/openldap/slapd.d + echo "SLAPD_CONFIG_ROOTDN = $SLAPD_CONFIG_ROOTDN" + if [ -z "$SLAPD_CONFIG_ROOTDN" ]; then + echo -n >&2 "Error: SLAPD_CONFIG_ROOTDN not set. " + echo >&2 "Did you forget to add -e SLAPD_CONFIG_ROOTDN=... ?" + exit 1 + fi + if [ -z "$SLAPD_CONFIG_ROOTPW" ]; then + echo -n >&2 "Error: SLAPD_CONFIG_ROOTPW not set. " + echo >&2 "Did you forget to add -e SLAPD_CONFIG_ROOTPW=... ?" + exit 1 + fi + + config_rootpw_hash=`slappasswd -s "${SLAPD_CONFIG_ROOTPW}"` + printf "$SLAPD_CONFIG_ROOTPW" > /slapd_config_rootpw + chmod 400 /slapd_config_rootpw + + # builtin schema + cat <<-EOF >> /tmp/slapd.conf + include /etc/openldap/schema/core.schema + include /etc/openldap/schema/dyngroup.schema + include /etc/openldap/schema/cosine.schema + include /etc/openldap/schema/inetorgperson.schema + include /etc/openldap/schema/openldap.schema + include /etc/openldap/schema/corba.schema + include /etc/openldap/schema/pmi.schema + include /etc/openldap/schema/ppolicy.schema + include /etc/openldap/schema/misc.schema + include /etc/openldap/schema/nis.schema + EOF + + # user-provided schemas + if [ -d "/ldap/schemas" ]; then + for f in /ldap/schema/*.schema ; do + echo "Including custom schema $f" + echo "include $f" >> /tmp/slapd.conf + done + fi + + CA_KEY=/ldap/pki/ca_key.pem + CA_EXPIRE=365 + CA_CERT=/ldap/pki/ca_cert.pem + CA_SUBJECT="openldap-alpine-${host}-CA" + + SSL_SUBJECT="openldap-alpine-${host}" + SSL_EXPIRE=365 + SSL_KEY=/ldap/pki/key.pem + SSL_SIZE=2048 + SSL_CERT=/ldap/pki/cert.pem + SSL_CSR=/ldap/pki/cert.csr + SSL_CONFIG=/ldap/pki/openssl.cnf + + # user-provided tls certs + echo "Configuring PKI" + if [ ! -d "/ldap/pki" ] ; then + echo "No /ldap/pki directory, generating self-signed certs" + mkdir -p /ldap/pki + # CA + openssl genrsa -out ${CA_KEY} 2048 + openssl req -x509 -new -nodes -key ${CA_KEY} -days ${CA_EXPIRE} -out ${CA_CERT} -subj "/CN=${CA_SUBJECT}" + + # server + cat > ${SSL_CONFIG} <<-EOF + [req] + req_extensions = v3_req + distinguished_name = req_distinguished_name + [req_distinguished_name] + [ v3_req ] + basicConstraints = CA:FALSE + keyUsage = nonRepudiation, digitalSignature, keyEncipherment + extendedKeyUsage = clientAuth, serverAuth + EOF + + openssl genrsa -out ${SSL_KEY} ${SSL_SIZE} + openssl req -new -key ${SSL_KEY} -out ${SSL_CSR} -subj "/CN=${SSL_SUBJECT}" -config ${SSL_CONFIG} + openssl x509 -req -in ${SSL_CSR} -CA ${CA_CERT} -CAkey ${CA_KEY} -CAcreateserial -out ${SSL_CERT} \ + -days ${SSL_EXPIRE} -extensions v3_req -extfile ${SSL_CONFIG} + fi + + echo "TLSCACertificateFile ${CA_CERT}" >> /tmp/slapd.conf + echo "TLSCertificateFile ${SSL_CERT}" >> /tmp/slapd.conf + echo "TLSCertificateKeyFile ${SSL_KEY}" >> /tmp/slapd.conf + echo "TLSCipherSuite HIGH:-SSLv2:-SSLv3" >> /tmp/slapd.conf + + cat <<-EOF >> /tmp/slapd.conf + database config + rootDN "$SLAPD_CONFIG_ROOTDN" + rootpw $config_rootpw_hash + EOF + + echo "Generating configuration" + slaptest -f /tmp/slapd.conf -F /etc/openldap/slapd.d +fi + +chown -R ldap:ldap /etc/openldap/slapd.d/ + +echo "Starting slapd with $@" +exec "$@" & + +if [ $FIRST_START -eq 1 ] ; then + # handle race condition + echo "Waiting for server to start" + let i=0 + while [ $i -lt 60 ]; do + printf "." + ldapsearch -x -h localhost -s base -b '' >/dev/null 2>&1 + test $? -eq 0 && break + sleep 1 + done + echo + echo "Adding additional config from /ldap/ldif/*.ldif" + for f in /ldap/ldif/*.ldif ; do + echo "> $f" + ldapmodify -x -H ldap://localhost -y /slapd_config_rootpw -D ${SLAPD_CONFIG_ROOTDN} -f $f + done + + if [ -d /ldap/userldif ] ; then + echo "Adding user config from /ldap/userldif/*.ldif" + for f in /ldap/userldif/*.ldif ; do + echo "> $f" + ldapmodify -x -H ldap://localhost -y /slapd_config_rootpw -D ${SLAPD_CONFIG_ROOTDN} -f $f + done + fi +fi + +echo READY + +while true ; do sleep 60 ; done ; diff --git a/alpine-openldap/ldif/cn_monitor.ldif b/alpine-openldap/ldif/cn_monitor.ldif new file mode 100644 index 0000000..2a893d2 --- /dev/null +++ b/alpine-openldap/ldif/cn_monitor.ldif @@ -0,0 +1,10 @@ +dn: cn=module,cn=config +changetype: add +objectClass: olcModuleList +olcModuleLoad: back_monitor + +dn: olcdatabase=monitor,cn=config +changetype: add +objectclass: olcDatabaseConfig +olcDatabase: monitor +olcAccess: to dn.subtree=cn=monitor by * read diff --git a/alpine-oraclejdk/Dockerfile b/alpine-oraclejdk/Dockerfile new file mode 100644 index 0000000..3da2b77 --- /dev/null +++ b/alpine-oraclejdk/Dockerfile @@ -0,0 +1,57 @@ +FROM alpine-glibc:alpine-3.6 + +ENV JAVA_VERSION=8 \ + JAVA_UPDATE=151 \ + JAVA_BUILD=12 \ + JAVA_PATH=e758a0de34e24606bca991d704f6dcbf \ + JAVA_HOME="/usr/lib/jvm/default-jvm" + +RUN apk add --no-cache --virtual=build-dependencies wget ca-certificates unzip && \ + cd "/tmp" && \ + wget --header "Cookie: oraclelicense=accept-securebackup-cookie;" \ + "http://download.oracle.com/otn-pub/java/jdk/${JAVA_VERSION}u${JAVA_UPDATE}-b${JAVA_BUILD}/${JAVA_PATH}/jdk-${JAVA_VERSION}u${JAVA_UPDATE}-linux-x64.tar.gz" && \ + tar -xzf "jdk-${JAVA_VERSION}u${JAVA_UPDATE}-linux-x64.tar.gz" && \ + mkdir -p "/usr/lib/jvm" && \ + mv "/tmp/jdk1.${JAVA_VERSION}.0_${JAVA_UPDATE}" "/usr/lib/jvm/java-${JAVA_VERSION}-oracle" && \ + ln -s "java-${JAVA_VERSION}-oracle" "$JAVA_HOME" && \ + ln -s "$JAVA_HOME/bin/"* "/usr/bin/" && \ + rm -rf "$JAVA_HOME/"*src.zip && \ + rm -rf "$JAVA_HOME/lib/missioncontrol" \ + "$JAVA_HOME/lib/visualvm" \ + "$JAVA_HOME/lib/"*javafx* \ + "$JAVA_HOME/jre/lib/plugin.jar" \ + "$JAVA_HOME/jre/lib/ext/jfxrt.jar" \ + "$JAVA_HOME/jre/bin/javaws" \ + "$JAVA_HOME/jre/lib/javaws.jar" \ + "$JAVA_HOME/jre/lib/desktop" \ + "$JAVA_HOME/jre/plugin" \ + "$JAVA_HOME/jre/lib/"deploy* \ + "$JAVA_HOME/jre/lib/"*javafx* \ + "$JAVA_HOME/jre/lib/"*jfx* \ + "$JAVA_HOME/jre/lib/amd64/libdecora_sse.so" \ + "$JAVA_HOME/jre/lib/amd64/"libprism_*.so \ + "$JAVA_HOME/jre/lib/amd64/libfxplugins.so" \ + "$JAVA_HOME/jre/lib/amd64/libglass.so" \ + "$JAVA_HOME/jre/lib/amd64/libgstreamer-lite.so" \ + "$JAVA_HOME/jre/lib/amd64/"libjavafx*.so \ + "$JAVA_HOME/jre/lib/amd64/"libjfx*.so && \ + rm -rf "$JAVA_HOME/jre/bin/jjs" \ + "$JAVA_HOME/jre/bin/keytool" \ + "$JAVA_HOME/jre/bin/orbd" \ + "$JAVA_HOME/jre/bin/pack200" \ + "$JAVA_HOME/jre/bin/policytool" \ + "$JAVA_HOME/jre/bin/rmid" \ + "$JAVA_HOME/jre/bin/rmiregistry" \ + "$JAVA_HOME/jre/bin/servertool" \ + "$JAVA_HOME/jre/bin/tnameserv" \ + "$JAVA_HOME/jre/bin/unpack200" \ + "$JAVA_HOME/jre/lib/ext/nashorn.jar" \ + "$JAVA_HOME/jre/lib/jfr.jar" \ + "$JAVA_HOME/jre/lib/jfr" \ + "$JAVA_HOME/jre/lib/oblique-fonts" && \ + wget --header "Cookie: oraclelicense=accept-securebackup-cookie;" \ + "http://download.oracle.com/otn-pub/java/jce/${JAVA_VERSION}/jce_policy-${JAVA_VERSION}.zip" && \ + unzip -jo -d "${JAVA_HOME}/jre/lib/security" "jce_policy-${JAVA_VERSION}.zip" && \ + rm "${JAVA_HOME}/jre/lib/security/README.txt" && \ + apk del build-dependencies && \ + rm "/tmp/"* diff --git a/alpine-php/Dockerfile b/alpine-php/Dockerfile new file mode 100644 index 0000000..346ec36 --- /dev/null +++ b/alpine-php/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.6 + +RUN apk add --no-cache php7 diff --git a/alpine-python2/Dockerfile b/alpine-python2/Dockerfile new file mode 100644 index 0000000..0348b90 --- /dev/null +++ b/alpine-python2/Dockerfile @@ -0,0 +1,7 @@ +FROM alpine:3.6 + +RUN apk add --no-cache python && \ + python -m ensurepip && \ + rm -r /usr/lib/python*/ensurepip && \ + pip install --upgrade pip setuptools && \ + rm -r /root/.cache diff --git a/alpine-python3-machinelearning/Dockerfile b/alpine-python3-machinelearning/Dockerfile new file mode 100644 index 0000000..a94e9a9 --- /dev/null +++ b/alpine-python3-machinelearning/Dockerfile @@ -0,0 +1,16 @@ +FROM alpine-python3:latest + +RUN apk add --no-cache libstdc++ lapack-dev && \ + apk add --no-cache \ + --virtual=.build-dependencies \ + g++ gfortran musl-dev \ + python3-dev && \ + ln -s locale.h /usr/include/xlocale.h && \ + pip install numpy && \ + pip install pandas && \ + pip install scipy && \ + pip install scikit-learn && \ + find /usr/lib/python3.*/ -name 'tests' -exec rm -r '{}' + && \ + rm /usr/include/xlocale.h && \ + rm -r /root/.cache && \ + apk del .build-dependencies diff --git a/alpine-python3/Dockerfile b/alpine-python3/Dockerfile new file mode 100644 index 0000000..f64b112 --- /dev/null +++ b/alpine-python3/Dockerfile @@ -0,0 +1,9 @@ +FROM alpine:3.6 + +RUN apk add --no-cache python3 && \ + python3 -m ensurepip && \ + rm -r /usr/lib/python*/ensurepip && \ + pip3 install --upgrade pip setuptools && \ + if [ ! -e /usr/bin/pip ]; then ln -s pip3 /usr/bin/pip ; fi && \ + if [[ ! -e /usr/bin/python ]]; then ln -sf /usr/bin/python3 /usr/bin/python; fi && \ + rm -r /root/.cache diff --git a/alpine-ruby/Dockerfile b/alpine-ruby/Dockerfile new file mode 100644 index 0000000..8a65c4f --- /dev/null +++ b/alpine-ruby/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine:3.6 + +RUN apk add --no-cache ruby ruby-bundler diff --git a/alpine-rust/Dockerfile b/alpine-rust/Dockerfile new file mode 100644 index 0000000..8f146d6 --- /dev/null +++ b/alpine-rust/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine-gcc:latest + +RUN apk add --no-cache rust cargo diff --git a/alpine-scala/Dockerfile b/alpine-scala/Dockerfile new file mode 100644 index 0000000..8839f3c --- /dev/null +++ b/alpine-scala/Dockerfile @@ -0,0 +1,18 @@ +FROM alpine-oracle-jdk:8u151 + +ENV SCALA_VERSION=2.12.1 \ + SCALA_HOME=/usr/share/scala + +# NOTE: bash is used by scala/scalac scripts, and it cannot be easily replaced with ash. + +RUN apk add --no-cache --virtual=.build-dependencies wget ca-certificates && \ + apk add --no-cache bash && \ + cd "/tmp" && \ + wget "https://downloads.typesafe.com/scala/${SCALA_VERSION}/scala-${SCALA_VERSION}.tgz" && \ + tar xzf "scala-${SCALA_VERSION}.tgz" && \ + mkdir "${SCALA_HOME}" && \ + rm "/tmp/scala-${SCALA_VERSION}/bin/"*.bat && \ + mv "/tmp/scala-${SCALA_VERSION}/bin" "/tmp/scala-${SCALA_VERSION}/lib" "${SCALA_HOME}" && \ + ln -s "${SCALA_HOME}/bin/"* "/usr/bin/" && \ + apk del .build-dependencies && \ + rm -rf "/tmp/"* diff --git a/alpine-slave-base/Dockerfile b/alpine-slave-base/Dockerfile new file mode 100644 index 0000000..9a7c0ed --- /dev/null +++ b/alpine-slave-base/Dockerfile @@ -0,0 +1,14 @@ +FROM alpine-openjdk:8u131 + +# Add user jenkins to the image +RUN adduser -D jenkins +# Set password for the jenkins user (you may want to alter this). +RUN echo "jenkins:jenkins" | chpasswd + +RUN apk add --no-cache openssh git bash +RUN mkdir -p /var/run/sshd +RUN ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa +RUN ssh-keygen -f /etc/ssh/ssh_host_dsa_key -N '' -t dsa + +EXPOSE 22 +CMD ["/usr/sbin/sshd","-D"] diff --git a/alpine-sonarqube/Dockerfile b/alpine-sonarqube/Dockerfile new file mode 100644 index 0000000..4f1a331 --- /dev/null +++ b/alpine-sonarqube/Dockerfile @@ -0,0 +1,36 @@ +FROM openjdk:8-alpine + +ENV SONAR_VERSION=6.7 \ + SONARQUBE_HOME=/opt/sonarqube \ + SONARQUBE_JDBC_USERNAME=sonar \ + SONARQUBE_JDBC_PASSWORD=sonar \ + SONARQUBE_JDBC_URL= + +# Http port +EXPOSE 9000 + +RUN addgroup -S sonarqube && adduser -S -G sonarqube sonarqube + +RUN set -x \ + && apk add --no-cache gnupg unzip \ + && apk add --no-cache libressl wget \ + && apk add --no-cache su-exec \ + && apk add --no-cache bash \ + && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys F1182E81C792928921DBCAB4CFCA4A29D26468DE \ + && mkdir /opt \ + && cd /opt \ + && wget -O sonarqube.zip --no-verbose https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-$SONAR_VERSION.zip \ + && wget -O sonarqube.zip.asc --no-verbose https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-$SONAR_VERSION.zip.asc \ + && gpg --batch --verify sonarqube.zip.asc sonarqube.zip \ + && unzip sonarqube.zip \ + && mv sonarqube-$SONAR_VERSION sonarqube \ + && chown -R sonarqube:sonarqube sonarqube \ + && rm sonarqube.zip* \ + && rm -rf $SONARQUBE_HOME/bin/* + +VOLUME "$SONARQUBE_HOME/data" + +WORKDIR $SONARQUBE_HOME +COPY run.sh $SONARQUBE_HOME/bin/ +RUN chmod +x $SONARQUBE_HOME/bin/run.sh +ENTRYPOINT ["./bin/run.sh"] diff --git a/alpine-sonarqube/run.sh b/alpine-sonarqube/run.sh new file mode 100644 index 0000000..e1da369 --- /dev/null +++ b/alpine-sonarqube/run.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +if [ "${1:0:1}" != '-' ]; then + exec "$@" +fi + +chown -R sonarqube:sonarqube $SONARQUBE_HOME +exec su-exec sonarqube \ + java -jar lib/sonar-application-$SONAR_VERSION.jar \ + -Dsonar.log.console=true \ + -Dsonar.jdbc.username="$SONARQUBE_JDBC_USERNAME" \ + -Dsonar.jdbc.password="$SONARQUBE_JDBC_PASSWORD" \ + -Dsonar.jdbc.url="$SONARQUBE_JDBC_URL" \ + -Dsonar.web.javaAdditionalOpts="$SONARQUBE_WEB_JVM_OPTS -Djava.security.egd=file:/dev/./urandom" \ + "$@" diff --git a/alpine-sshd/Dockerfile b/alpine-sshd/Dockerfile new file mode 100644 index 0000000..82b5a9e --- /dev/null +++ b/alpine-sshd/Dockerfile @@ -0,0 +1,20 @@ +FROM alpine:3.6 +MAINTAINER ming.chen + +RUN apk update && \ + apk add bash git openssh rsync augeas && \ + deluser $(getent passwd 33 | cut -d: -f1) && \ + delgroup $(getent group 33 | cut -d: -f1) 2>/dev/null || true && \ + mkdir -p ~root/.ssh /etc/authorized_keys && chmod 700 ~root/.ssh/ && \ + augtool 'set /files/etc/ssh/sshd_config/AuthorizedKeysFile ".ssh/authorized_keys /etc/authorized_keys/%u"' && \ + echo -e "Port 22\n" >> /etc/ssh/sshd_config && \ + cp -a /etc/ssh /etc/ssh.cache && \ + rm -rf /var/cache/apk/* + +EXPOSE 22 + +COPY entry.sh /entry.sh + +ENTRYPOINT ["/entry.sh"] + +CMD ["/usr/sbin/sshd", "-D", "-f", "/etc/ssh/sshd_config"] diff --git a/alpine-sshd/entry.sh b/alpine-sshd/entry.sh new file mode 100644 index 0000000..a7305f1 --- /dev/null +++ b/alpine-sshd/entry.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash + +set -e + +[ "$DEBUG" == 'true' ] && set -x + +DAEMON=sshd + +# Copy default config from cache +if [ ! "$(ls -A /etc/ssh)" ]; then + cp -a /etc/ssh.cache/* /etc/ssh/ +fi + +set_hostkeys() { + printf '%s\n' \ + 'set /files/etc/ssh/sshd_config/HostKey[1] /etc/ssh/keys/ssh_host_rsa_key' \ + 'set /files/etc/ssh/sshd_config/HostKey[2] /etc/ssh/keys/ssh_host_dsa_key' \ + 'set /files/etc/ssh/sshd_config/HostKey[3] /etc/ssh/keys/ssh_host_ecdsa_key' \ + 'set /files/etc/ssh/sshd_config/HostKey[4] /etc/ssh/keys/ssh_host_ed25519_key' \ + | augtool -s +} + +print_fingerprints() { + local BASE_DIR=${1-'/etc/ssh'} + for item in dsa rsa ecdsa ed25519; do + echo ">>> Fingerprints for ${item} host key" + ssh-keygen -E md5 -lf ${BASE_DIR}/ssh_host_${item}_key + ssh-keygen -E sha256 -lf ${BASE_DIR}/ssh_host_${item}_key + ssh-keygen -E sha512 -lf ${BASE_DIR}/ssh_host_${item}_key + done +} + +# Generate Host keys, if required +if ls /etc/ssh/keys/ssh_host_* 1> /dev/null 2>&1; then + echo ">> Host keys in keys directory" + set_hostkeys + print_fingerprints /etc/ssh/keys +elif ls /etc/ssh/ssh_host_* 1> /dev/null 2>&1; then + echo ">> Host keys exist in default location" + # Don't do anything + print_fingerprints +else + echo ">> Generating new host keys" + mkdir -p /etc/ssh/keys + ssh-keygen -A + mv /etc/ssh/ssh_host_* /etc/ssh/keys/ + set_hostkeys + print_fingerprints /etc/ssh/keys +fi + +# Fix permissions, if writable +if [ -w ~/.ssh ]; then + chown root:root ~/.ssh && chmod 700 ~/.ssh/ +fi +if [ -w ~/.ssh/authorized_keys ]; then + chown root:root ~/.ssh/authorized_keys + chmod 600 ~/.ssh/authorized_keys +fi +if [ -w /etc/authorized_keys ]; then + chown root:root /etc/authorized_keys + chmod 755 /etc/authorized_keys + find /etc/authorized_keys/ -type f -exec chmod 644 {} \; +fi + +# Add users if SSH_USERS=user:uid:gid set +if [ -n "${SSH_USERS}" ]; then + USERS=$(echo $SSH_USERS | tr "," "\n") + for U in $USERS; do + IFS=':' read -ra UA <<< "$U" + _NAME=${UA[0]} + _UID=${UA[1]} + _GID=${UA[2]} + + echo ">> Adding user ${_NAME} with uid: ${_UID}, gid: ${_GID}." + if [ ! -e "/etc/authorized_keys/${_NAME}" ]; then + echo "WARNING: No SSH authorized_keys found for ${_NAME}!" + fi + getent group ${_NAME} >/dev/null 2>&1 || addgroup -g ${_GID} ${_NAME} + getent passwd ${_NAME} >/dev/null 2>&1 || adduser -D -u ${_UID} -G ${_NAME} -s '' ${_NAME} + passwd -u ${_NAME} || true + done +else + # Warn if no authorized_keys + if [ ! -e ~/.ssh/authorized_keys ] && [ ! $(ls -A /etc/authorized_keys) ]; then + echo "WARNING: No SSH authorized_keys found!" + fi +fi + +# Update MOTD +if [ -v MOTD ]; then + echo -e "$MOTD" > /etc/motd +fi + +if [[ "${SFTP_MODE}" == "true" ]]; then + : ${SFTP_CHROOT:='/data'} + chown 0:0 ${SFTP_CHROOT} + chmod 755 ${SFTP_CHROOT} + + printf '%s\n' \ + 'set /files/etc/ssh/sshd_config/Subsystem/sftp "internal-sftp"' \ + 'set /files/etc/ssh/sshd_config/AllowTCPForwarding no' \ + 'set /files/etc/ssh/sshd_config/X11Forwarding no' \ + 'set /files/etc/ssh/sshd_config/ForceCommand internal-sftp' \ + 'set /files/etc/ssh/sshd_config/ChrootDirectory /data' \ + | augtool -s +fi + +stop() { + echo "Received SIGINT or SIGTERM. Shutting down $DAEMON" + # Get PID + pid=$(cat /var/run/$DAEMON/$DAEMON.pid) + # Set TERM + kill -SIGTERM "${pid}" + # Wait for exit + wait "${pid}" + # All done. + echo "Done." +} + +echo "Running $@" +if [ "$(basename $1)" == "$DAEMON" ]; then + trap stop SIGINT SIGTERM + $@ & + pid="$!" + mkdir -p /var/run/$DAEMON && echo "${pid}" > /var/run/$DAEMON/$DAEMON.pid + wait "${pid}" && exit $? +else + exec "$@" +fi