From ae90c978124e402d2aad99324fe07ef22d449d08 Mon Sep 17 00:00:00 2001 From: openkbs Date: Thu, 12 Nov 2020 10:38:56 -0500 Subject: [PATCH] updated with latest Ubuntu 20.04 and NodeJS v15 and latest OpenJDK v11 --- .gitignore | 28 +- Dockerfile | 273 +++++--- LICENSE | 0 Makefile | 113 ++++ README.md | 422 ++++++++++-- build.sh | 169 ++++- certificates/dummy.certificate | 1 + clean.sh | 55 ++ commit-push.sh | 25 +- data/myPyScript.py | 2 +- doc/multi-container-files-sharing.png | Bin 0 -> 64032 bytes ...kbs-jdk-mvn-py3_web-socket-server-test.png | Bin 0 -> 81639 bytes doc/proxy-setup-in-Dockerfile.txt | 49 ++ ...lti-container-host-based-file-sharing.pptx | Bin 0 -> 36997 bytes docker-entrypoint.sh | 49 ++ examples/HelloWorld.java | 5 + examples/myPyScript.py | 1 + examples/simple-server.js | 19 + logs.sh | 11 +- requirements.txt | 40 +- restore.sh | 47 ++ run.sh | 637 ++++++++++++++++-- save.sh | 55 ++ scripts/add-user-no-login.sh | 27 + scripts/add-user-sudo.sh | 31 + scripts/add-user.sh | 11 + scripts/docker-entrypoint.sh | 23 + scripts/install-npm-packages.sh | 13 + scripts/new-virtualenv.sh | 18 + scripts/printVersions.sh | 16 + scripts/requirements-npm.txt | 5 + scripts/setup_npm_proxy.sh | 83 +++ scripts/setup_system_certificates.sh | 213 ++++++ scripts/setup_system_proxy.sh | 112 +++ shell.sh | 2 +- specifications/package.json-ld | 51 ++ specifications/package.json-ld.js | 51 ++ specifications/schema.json-ld | 120 ++++ stop.sh | 6 +- tryJava.sh | 24 +- tryNodeJS.sh | 85 +++ tryPython.sh | 28 +- tryWebSocketServer.sh | 103 +++ 43 files changed, 2744 insertions(+), 279 deletions(-) mode change 100644 => 100755 LICENSE create mode 100755 Makefile mode change 100644 => 100755 build.sh create mode 100644 certificates/dummy.certificate create mode 100755 clean.sh mode change 100644 => 100755 commit-push.sh create mode 100644 doc/multi-container-files-sharing.png create mode 100644 doc/openkbs-jdk-mvn-py3_web-socket-server-test.png create mode 100644 doc/proxy-setup-in-Dockerfile.txt create mode 100644 doc/simple-multi-container-host-based-file-sharing.pptx create mode 100755 docker-entrypoint.sh create mode 100644 examples/HelloWorld.java create mode 100644 examples/myPyScript.py create mode 100644 examples/simple-server.js mode change 100644 => 100755 logs.sh create mode 100755 restore.sh mode change 100644 => 100755 run.sh create mode 100755 save.sh create mode 100644 scripts/add-user-no-login.sh create mode 100644 scripts/add-user-sudo.sh create mode 100644 scripts/add-user.sh create mode 100644 scripts/docker-entrypoint.sh create mode 100644 scripts/install-npm-packages.sh create mode 100644 scripts/new-virtualenv.sh create mode 100644 scripts/printVersions.sh create mode 100644 scripts/requirements-npm.txt create mode 100644 scripts/setup_npm_proxy.sh create mode 100644 scripts/setup_system_certificates.sh create mode 100644 scripts/setup_system_proxy.sh mode change 100644 => 100755 shell.sh create mode 100644 specifications/package.json-ld create mode 100644 specifications/package.json-ld.js create mode 100644 specifications/schema.json-ld mode change 100644 => 100755 stop.sh mode change 100644 => 100755 tryJava.sh create mode 100755 tryNodeJS.sh mode change 100644 => 100755 tryPython.sh create mode 100755 tryWebSocketServer.sh diff --git a/.gitignore b/.gitignore index 44a6be3..9411288 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,15 @@ -# Compiled class file -*.class - +*.log *.swp +*.tmp +data/ .fuse* +*.zip* -# Log file *.log +npm-debug.log* +node_modules +.env -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.ear -*.zip -*.tar.gz -*.rar +*.pem +*.crt -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* diff --git a/Dockerfile b/Dockerfile index 388462e..772bf3f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,114 +1,221 @@ -# use the latest LTS Ubuntu -FROM ubuntu:xenial +FROM ubuntu -MAINTAINER DrSnowbird@openkbs.org +MAINTAINER openkbs.org@gmail.com ENV DEBIAN_FRONTEND noninteractive -##### update ubuntu and Install Python 3 -RUN apt-get update \ - && apt-get install -y automake pkg-config libpcre3-dev zlib1g-dev liblzma-dev \ - && apt-get install -y curl net-tools build-essential libsqlite3-dev sqlite3 bzip2 libbz2-dev git wget unzip vim python3-pip python3-setuptools python3-dev python3-numpy python3-scipy python3-pandas python3-matplotlib \ - && ln -s /usr/bin/python3 /usr/bin/python \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* +#ENV JAVA_VERSION=8 +ENV JAVA_VERSION=11 + +############################################## +#### ---- Installation Directories ---- #### +############################################## +ENV INSTALL_DIR=${INSTALL_DIR:-/usr} +ENV SCRIPT_DIR=${SCRIPT_DIR:-$INSTALL_DIR/scripts} + +############################################## +#### ---- Corporate Proxy Auto Setup ---- #### +############################################## +#### ---- Transfer setup ---- #### +COPY ./scripts ${SCRIPT_DIR} +RUN chmod +x ${SCRIPT_DIR}/*.sh + +#### ---- Apt Proxy & NPM Proxy & NPM Permission setup if detected: ---- #### +RUN cd ${SCRIPT_DIR}; ${SCRIPT_DIR}/setup_system_proxy.sh + +######################################## +#### update ubuntu and Install Python 3 +######################################## +RUN apt-get update -y && \ + apt-get install -y apt-utils automake pkg-config libpcre3-dev zlib1g-dev liblzma-dev && \ + apt-get install -y curl iputils-ping nmap net-tools build-essential software-properties-common libsqlite3-dev sqlite3 bzip2 libbz2-dev git wget unzip vim python3-pip python3-setuptools python3-dev python3-venv python3-numpy python3-scipy python3-pandas python3-matplotlib && \ + apt-get install -y git xz-utils && \ + apt-get install -y sudo && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +######################################## +#### ------- OpenJDK Installation ------ +######################################## +RUN apt-get update && apt-get install -y locales && rm -rf /var/lib/apt/lists/* && \ + localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 +ENV LANG en_US.utf8 + +# 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 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + bzip2 \ + unzip \ + xz-utils \ + && rm -rf /var/lib/apt/lists/* + +# Default to UTF-8 file.encoding +ENV LANG C.UTF-8 + +# source: https://packages.debian.org/source/sid/openjdk-8 +# source: https://packages.debian.org/source/sid/openjdk-11 +## # ENV JAVA_DEBIAN_VERSION=8u222-b10-0ubuntu1.18.04.1-b10 + +ENV JAVA_HOME=/usr/lib/jvm/java-${JAVA_VERSION}-openjdk-amd64 +ENV PATH=$JAVA_HOME/bin:$PATH + +RUN set -ex; \ + \ +# deal with slim variants not having man page directories (which causes "update-alternatives" to fail) + if [ ! -d /usr/share/man/man1 ]; then \ + mkdir -p /usr/share/man/man1; \ + fi; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ +# openjdk-${JAVA_VERSION}-jdk="$JAVA_DEBIAN_VERSION" \ + openjdk-${JAVA_VERSION}-jdk \ + ; \ + rm -rf /var/lib/apt/lists/*; \ + \ +# update-alternatives so that future installs of other OpenJDK versions don't change /usr/bin/java + update-alternatives --get-selections | awk -v home="$(readlink -f "$JAVA_HOME")" 'index($3, home) == 1 { $2 = "manual"; print | "update-alternatives --set-selections" }'; \ +# ... and verify that it actually worked for one of the alternatives we care about + update-alternatives --query java | grep -q 'Status: manual' ################################### -#### Install Java 11 +#### ---- Install Maven 3 ---- #### ################################### - -#### --------------------------------------------------------------- -#### ---- Change below when upgrading version ---- -#### --------------------------------------------------------------- -# http://download.oracle.com/otn-pub/java/jdk/11.0.1+13/90cf5d8f270a4347a95050320eef3fb7/jdk-11.0.1_linux-x64_bin.tar.gz - -ARG JAVA_MAJOR_VERSION=${JAVA_MAJOR_VERSION:-11} -ARG JAVA_UPDATE_VERSION=${JAVA_UPDATE_VERSION:-0.1} -ARG JAVA_BUILD_NUMBER=${JAVA_BUILD_NUMBER:-13} -ARG JAVA_TOKEN=${JAVA_TOKEN:-90cf5d8f270a4347a95050320eef3fb7} - -#### --------------------------------------------------------------- -#### ---- Don't change below unless you know what you are doing ---- -#### --------------------------------------------------------------- -ARG UPDATE_VERSION="${JAVA_MAJOR_VERSION}.${JAVA_UPDATE_VERSION}" -ARG BUILD_VERSION=${JAVA_BUILD_NUMBER} - -ENV JAVA_HOME /usr/jdk-${JAVA_MAJOR_VERSION}.${JAVA_UPDATE_VERSION} -ENV PATH $PATH:$JAVA_HOME/bin -ENV INSTALL_DIR /usr - -## http://download.oracle.com/otn-pub/java/jdk/10.0.2+13/19aef61b38124481863b1413dce1855f/jdk-10.0.2_linux-x64_bin.tar.gz - -WORKDIR $INSTALL_DIR - -#RUN curl -sL --retry 3 --insecure \ -# --header "Cookie: oraclelicense=accept-securebackup-cookie;" \ -# "http://download.oracle.com/otn-pub/java/jdk/${UPDATE_VERSION}+${BUILD_VERSION}/${JAVA_TOKEN}/jdk-${UPDATE_VERSION}_linux_x64_bin.tar.gz" \ -# | gunzip \ -# | tar x -C /usr/ \ -# && ln -s $JAVA_HOME $INSTALL_DIR/java \ -# && rm -rf $JAVA_HOME/man - -RUN curl -sL --retry 3 --insecure \ - --header "Cookie: oraclelicense=accept-securebackup-cookie;" \ - "http://download.oracle.com/otn-pub/java/jdk/${UPDATE_VERSION}+${BUILD_VERSION}/${JAVA_TOKEN}/jdk-${UPDATE_VERSION}_linux-x64_bin.tar.gz" --output $INSTALL_DIR/jdk-${UPDATE_VERSION}_linux-x64_bin.tar.gz \ - && tar xvf jdk-${UPDATE_VERSION}_linux-x64_bin.tar.gz \ - && ln -s $JAVA_HOME $INSTALL_DIR/java \ - && rm -rf $JAVA_HOME/man jdk-${UPDATE_VERSION}_linux-x64_bin.tar.gz - -################################### -#### Install Maven 3 -################################### -ENV MAVEN_VERSION 3.5.4 -ENV MAVEN_HOME /usr/apache-maven-$MAVEN_VERSION -ENV PATH $PATH:$MAVEN_HOME/bin -## http://mirrors.sorengard.com/apache/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz -RUN curl -sL http://archive.apache.org/dist/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz \ +ARG MAVEN_VERSION=${MAVEN_VERSION:-3.6.3} +ENV MAVEN_VERSION=${MAVEN_VERSION} +ENV MAVEN_HOME=/usr/apache-maven-${MAVEN_VERSION} +ENV PATH=${PATH}:${MAVEN_HOME}/bin +RUN curl -sL http://archive.apache.org/dist/maven/maven-3/${MAVEN_VERSION}/binaries/apache-maven-${MAVEN_VERSION}-bin.tar.gz \ | gunzip \ | tar x -C /usr/ \ - && ln -s $MAVEN_HOME /usr/maven + && ln -s ${MAVEN_HOME} /usr/maven -################################### -#### ---- Pip install packages ---- -################################### +######################################## +#### ---- PIP install packages ---- #### +######################################## COPY requirements.txt ./ -## --------------------------------------------------- -## Don't upgrade pip to 10.0.x version -- it's broken! -## Staying with version 8 to avoid the problem -## --------------------------------------------------- +## -- if pkg-resources error occurs, then do this! -- ## +# pip3 uninstall pkg-resources==0.0.0 +RUN pip3 --no-cache-dir install --upgrade pip +RUN pip3 --no-cache-dir install --ignore-installed -U -r requirements.txt + +## -- added Local PIP installation bin to PATH +ENV PATH=${PATH}:${HOME}/.local/bin -RUN pip3 install -r ./requirements.txt && pip3 install --upgrade pip +## VERSIONS ## +ENV PATH=${PATH}:${JAVA_HOME}/bin + +RUN ln -s ${JAVA_HOME_ACTUAL} ${JAVA_HOME} && \ + ls -al ${INSTALL_DIR} && \ + echo "PATH=${PATH}" && export JAVA_HOME=${JAVA_HOME} && export PATH=$PATH && \ + java -version && \ + mvn --version && \ + python3 -V && \ + pip3 --version ################################### -#### ---- Install Gradle ---- +#### ---- Install Gradle ---- ##### ################################### +# Ref: https://gradle.org/releases/ + ARG GRADLE_INSTALL_BASE=${GRADLE_INSTALL_BASE:-/opt/gradle} -ARG GRADLE_VERSION=${GRADLE_VERSION:-4.9} +ARG GRADLE_VERSION=${GRADLE_VERSION:-6.7} ARG GRADLE_HOME=${GRADLE_INSTALL_BASE}/gradle-${GRADLE_VERSION} ENV GRADLE_HOME=${GRADLE_HOME} ARG GRADLE_PACKAGE=gradle-${GRADLE_VERSION}-bin.zip ARG GRADLE_PACKAGE_URL=https://services.gradle.org/distributions/${GRADLE_PACKAGE} -# https://services.gradle.org/distributions/gradle-4.9-bin.zip -RUN \ - mkdir -p ${GRADLE_INSTALL_BASE} && \ + +RUN mkdir -p ${GRADLE_INSTALL_BASE} && \ cd ${GRADLE_INSTALL_BASE} && \ - wget -c ${GRADLE_PACKAGE_URL} && \ + wget -q --no-check-certificate -c ${GRADLE_PACKAGE_URL} && \ unzip -d ${GRADLE_INSTALL_BASE} ${GRADLE_PACKAGE} && \ ls -al ${GRADLE_HOME} && \ ln -s ${GRADLE_HOME}/bin/gradle /usr/bin/gradle && \ - ${GRADLE_HOME}/bin/gradle -v + ${GRADLE_HOME}/bin/gradle -v && \ + rm -f ${GRADLE_PACKAGE} + +######################################### +#### ---- Node from NODESOURCES ---- #### +######################################### +# Ref: https://github.com/nodesource/distributions +ARG NODE_VERSION=${NODE_VERSION:-15} +ENV NODE_VERSION=${NODE_VERSION} +RUN apt-get update -y && \ + apt-get install -y sudo curl git xz-utils && \ + curl -sL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - && \ + apt-get install -y nodejs + +RUN cd ${SCRIPT_DIR}; ${SCRIPT_DIR}/setup_npm_proxy.sh ################################### -#### ---- Working directory. ---- +#### ---- user: developer ---- #### ################################### -RUN mkdir -p /data -COPY . /data - -VOLUME "/data" - -WORKDIR /data - +ENV USER_ID=${USER_ID:-1000} +ENV GROUP_ID=${GROUP_ID:-1000} +ENV USER=${USER:-developer} +ENV HOME=/home/${USER} + +## -- setup NodeJS user profile +RUN groupadd ${USER} && useradd ${USER} -m -d ${HOME} -s /bin/bash -g ${USER} && \ + ## -- Ubuntu -- \ + usermod -aG sudo ${USER} && \ + ## -- Centos -- \ + #usermod -aG wheel ${USER} && \ + echo "${USER} ALL=NOPASSWD:ALL" | tee -a /etc/sudoers && \ + echo "USER =======> ${USER}" && ls -al ${HOME} + +############################## +#### ---- NPM PREFIX ---- #### +############################## + +#ENV NPM_CONFIG_PREFIX=${NPM_CONFIG_PREFIX:-${HOME}/.npm-global} +#ENV PATH="${NPM_CONFIG_PREFIX}/bin:$PATH" +#RUN mkdir -p ${NPM_CONFIG_PREFIX} ${HOME}/.config ${HOME}/.npm && \ +# chown ${USER}:${USER} -R ${NPM_CONFIG_PREFIX} ${HOME}/.config ${HOME}/.npm && \ +# export PATH=$PATH && ${SCRIPT_DIR}/install-npm-packages.sh + +########################################### +#### ---- entrypoint script setup ---- #### +########################################### +RUN ln -s ${INSTALL_DIR}/scripts/docker-entrypoint.sh /docker-entrypoint.sh + +############################################# +#### ---- USER as Owner for scripts ---- #### +############################################# +RUN chown ${USER}:${USER} -R ${INSTALL_DIR}/scripts /docker-entrypoint.sh + +############################################ +#### ---- Set up user environments ---- #### +############################################ +ENV WORKSPACE=${HOME}/workspace +ENV DATA=${HOME}/data +USER ${USER} +WORKDIR ${HOME} + +############################################ +#### ---- Volumes: data, workspace ---- #### +############################################ +RUN mkdir -p ${WORKSPACE} ${DATA} +COPY ./examples ${DATA}/examples +VOLUME ${DATA} +VOLUME ${WORKSPACE} + +######################### +#### ---- Entry ---- #### +######################### +USER ${USER} +WORKDIR ${HOME} #### Define default command. +#ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["/bin/bash"] diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..7610483 --- /dev/null +++ b/Makefile @@ -0,0 +1,113 @@ +# ------------------------------------------------------------------------------------------------------- +# login first (Registry: e.g., hub.docker.io, registry.localhost:5000, etc.) +# a.) docker login +# or +# b.) sudo docker login -p FpXM6Qy9vVL5kPeoefzxwA-oaYb-Wpej2iXTwV7UHYs -e unused -u unused docker-registry-default.openkbs.org +# e.g. (using Openshift) +# oc process -f ./files/deployments/template.yml -v API_NAME=$(REGISTRY_IMAGE) > template.active +# +# to run: +# make [ APP_VERSION=<...> DOCKER_NAME=<...> REGISTRY_HOST=<...> ] +# example: +# make build +# make up +# make down +# ------------------------------------------------------------------------------------------------------- + +SHELL := /bin/bash + +BASE_IMAGE := $(BASE_IMAGE) + +## -- To Check syntax: +# cat -e -t -v Makefile + +# The name of the container (default is current directory name) +#DOCKER_NAME := $(shell echo $${PWD\#\#*/}) +DOCKER_NAME := $(shell echo $${PWD\#\#*/}|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ) + +ORGANIZATION=$(shell echo $${ORGANIZATION:-openkbs}) +APP_VERSION=$(shell echo $${APP_VERSION:-latest}) +imageTag=$(ORGANIZATION)/$(DOCKER_NAME) + +## Docker Registry (Private Server) +REGISTRY_HOST= +#REGISTRY_HOST=$(shell echo $${REGISTRY_HOST:-localhost:5000}) +REGISTRY_IMAGE=$(REGISTRY_HOST)/$(ORGANIZATION)/$(DOCKER_NAME) + +#VERSION?="$(APP_VERSION)-$$(date +%Y%m%d)" +VERSION?="$(APP_VERSION)" + +## -- Uncomment this to use local Registry Host -- +DOCKER_IMAGE := $(ORGANIZATION)/$(DOCKER_NAME) + +## -- To Check syntax: +# cat -e -t -v Makefile + +# -- example -- +#VOLUME_MAP := "-v $${PWD}/data:/home/developer/data -v $${PWD}/workspace:/home/developer/workspace" +VOLUME_MAP := + +# -- Local SAVE of image -- +IMAGE_EXPORT_PATH := "$${PWD}/archive" + +# { no, on-failure, unless-stopped, always } +RESTART_OPTION := always + +SHA := $(shell git describe --match=NeVeRmAtCh --always --abbrev=40 --dirty=*) + +.PHONY: clean rmi build push pull up down run stop exec + +clean: + echo $(DOCKER_NAME) $(DOCKER_IMAGE):$(VERSION) + +default: build + +build: + sudo docker build \ + --build-arg BASE_IMAGE="$(BASE_IMAGE)" \ + --build-arg CIRCLE_SHA1="$(SHA)" \ + --build-arg version=$(VERSION) \ + --build-arg VCS_REF=`git rev-parse --short HEAD` \ + --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ + -t $(DOCKER_IMAGE):$(VERSION) . + +push: build + sudo docker commit -m "$comment" ${containerID} ${imageTag}:$(VERSION) + sudo docker push $(DOCKER_IMAGE):$(VERSION) + + docker tag $(imageTag):$(VERSION) $(REGISTRY_IMAGE):$(VERSION) + #sudo docker tag $(imageTag):latest $(REGISTRY_IMAGE):latest + docker push $(REGISTRY_IMAGE):$(VERSION) + #sudo docker push $(REGISTRY_IMAGE):latest + @if [ ! "$(IMAGE_EXPORT_PATH)" = "" ]; then \ + mkdir -p $(IMAGE_EXPORT_PATH); \ + sudo docker save $(REGISTRY_IMAGE):$(VERSION) | gzip > $(IMAGE_EXPORT_PATH)/$(DOCKER_NAME)_$(VERSION).tar.gz; \ + fi +pull: + @if [ "$(REGISTRY_HOST)" = "" ]; then \ + sudo docker pull $(DOCKER_IMAGE):$(VERSION) ; \ + else \ + sudo docker pull $(REGISTRY_IMAGE):$(VERSION) ; \ + fi + + +up: build + sudo docker-compose up -d + +down: + sudo docker-compose down + +run: + sudo docker run --name=$(DOCKER_NAME) --restart=$(RESTART_OPTION) $(VOLUME_MAP) $(DOCKER_IMAGE):$(VERSION) + +stop: run + sudo docker stop --name=$(DOCKER_NAME) + +status: + sudo docker ps + +rmi: + sudo docker rmi $$(docker images -f dangling=true -q) + +exec: up + sudo docker-compose exec $(DOCKER_NAME) /bin/bash diff --git a/README.md b/README.md index 585e6b9..ee426d7 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,116 @@ -# Java JDK 11 + Maven 3.5 + Python 3.5 + Gradle 4.10 +# OpenJDK Java 11 + Maven 3.6 + Python 3.8 + pip 20 + node 15 + npm 6 + Gradle 6 -# License Agreement -By using this image, you agree the [Oracle Java JDK License](http://www.oracle.com/technetwork/java/javase/terms/license/index.html). -This image contains [Oracle JDK 11](http://www.oracle.com/technetwork/java/javase/downloads/index.html). You must accept the [Oracle Binary Code License Agreement for Java SE](http://www.oracle.com/technetwork/java/javase/terms/license/index.html) to use this image. +[![](https://images.microbadger.com/badges/image/openkbs/jdk-mvn-py3.svg)](https://microbadger.com/images/openkbs/jdk-mvn-py3 "Get your own image badge on microbadger.com") [![](https://images.microbadger.com/badges/version/openkbs/jdk-mvn-py3.svg)](https://microbadger.com/images/openkbs/jdk-mvn-py3 "Get your own version badge on microbadger.com") -# Components: -Components: -* Oracle Java "11.0.1" JDK environment - java 11.0.1 2018-10-16 LTS - Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS) - Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode) +# ** This build is based upon Ubuntu 20.04 + OpenJDK Java 11 ** -* Apache Maven 3.5.4 -* Python 3.5.3 -* Gradle 4.10 -* Other tools: git wget unzip vim python python-setuptools python-dev python-numpy +# NOTICE: ''Change to use Non-Root implementation'' +This new release is designed to support the deployment for Non-Root child images implementations and deployments to platform such as OpenShift or RedHat host operating system which requiring special policy to deploy. And, for better security practice, we decided to migrate (eventaully) our Docker containers to use Non-Root implementation. +Here are some of the things you can do if your images requiring "Root" acccess - you `really` want to do it: +1. For Docker build: Use "sudo" or "sudo -H" prefix to your Dockerfile's command which requiring "sudo" access to install packages. +2. For Docker container (access via shell): Use "sudo" command when you need to access root privilges to install packages or change configurations. +3. Or, you can use older version of this kind of base images which use "root" in Dockerfile. +4. Yet, you can also modify the Dockerfile at the very bottom to remove/comment out the "USER ${USER}" line so that your child images can have root as USER. +5. Finally, you can also, add a new line at the very top of your child Docker image's Dockerfile to include "USER root" so that your Docker images built will be using "root". +We like to promote the use of "Non-Root" images as better Docker security practice. And, whenever possible, you also want to further confine the use of "root" privilges in your Docker implementation so that it can prevent the "rooting hacking into your Host system". To lock down your docker images and/or this base image, you will add the following line at the very end to remove sudo: `(Notice that this might break some of your run-time code if you use sudo during run-time)` +``` +sudo agt-get remove -y sudo +``` +After that, combining with other Docker security practice (see below references), you just re-build your local images and re-deploy it as non-development quality of docker container. However, there are many other practices to secure your Docker containes. See below: + +* [Docker security | Docker Documentation](https://docs.docker.com/engine/security/security/) +* [5 tips for securing your Docker containers - TechRepublic](https://www.techrepublic.com/article/5-tips-for-securing-your-docker-containers/) +* [Docker Security - 6 Ways to Secure Your Docker Containers](https://www.sumologic.com/blog/security/securing-docker-containers/) +* [Five Docker Security Best Practices - The New Stack](https://thenewstack.io/5-docker-security-best-practices/) + +# Components +* Ubuntu 20.04 LTS now as LTS Docker base image. + +* openjdk version "11.0.9" 2020-10-20 + OpenJDK Runtime Environment (build 11.0.9+11-Ubuntu-0ubuntu1.20.04) + OpenJDK 64-Bit Server VM (build 11.0.9+11-Ubuntu-0ubuntu1.20.04, mixed mode, sharing) + +* Apache Maven 3.6 +* Python 3.6 + pip 20.2 + Python 3 virtual environments (venv, virtualenv, virtualenvwrapper, mkvirtualenv, ..., etc.) +* Node v15 + npm 7 (from NodeSource official Node Distribution) +* Gradle 6 +* Other tools: git wget unzip vim python python-setuptools python-dev python-numpy, ..., etc. +* [See Releases Information](https://github.com/DrSnowbird/jdk-mvn-py3/blob/master/README.md#Releases-information) + +# Quick commands +* build.sh - build local image +* logs.sh - see logs of container +* run.sh - run the container +* shell.sh - shell into the container +* stop.sh - stop the container +* tryJava.sh : test Java +* tryNodeJS.sh : test NodeJS +* tryPython.sh : test Python +* tryWebSocketServer.sh : test WebSockert NodeJS Server + +# How to use and quick start running? +1. git clone https://github.com/DrSnowbird/jdk-mvn-py3.git +2. cd jdk-mvn-py3 +3. ./run.sh + +# Default Run (test) - Just entering Container +``` +./run.sh +``` + +# Test Java, NodeJS, and Python3 Runs +``` +./tryJava.sh +./tryNodeJS.sh +./tryPython.sh +./tryWebSockerServer.sh +``` +# Default Build (locally) +``` +./build.sh +``` # Pull the image from Docker Repository ```bash -docker pull openkbs/jdk11-mvn-py3 +docker pull openkbs/jdk-mvn-py3 ``` # Base the image to build add-on components ```Dockerfile -FROM openkbs/jdk11-mvn-py3 +FROM openkbs/jdk-mvn-py3 +... (then your customization Dockerfile code here) ``` -# Run the image +# Manually setup to Run the image Then, you're ready to run: - make sure you create your work directory, e.g., ./data ```bash mkdir ./data -docker run -d --name my-jdk11-mvn-py3 -v $PWD/data:/data -i -t openkbs/jdk11-mvn-py3 +docker run -d --name my-jdk-mvn-py3 -v $PWD/data:/data -i -t openkbs/jdk-mvn-py3 ``` # Build and Run your own image -Say, you will build the image "my/jdk11-mvn-py3". +Say, you will build the image "my/jdk-mvn-py3". ```bash -docker build -t my/jdk11-mvn-py3 . +docker build -t my/jdk-mvn-py3 . ``` -To run your own image, say, with some-jdk11-mvn-py3: +To run your own image, say, with some-jdk-mvn-py3: ```bash mkdir ./data -docker run -d --name some-jdk11-mvn-py3 -v $PWD/data:/data -i -t my/jdk11-mvn-py3 +docker run -d --name some-jdk-mvn-py3 -v $PWD/data:/data -i -t my/jdk-mvn-py3 ``` # Shell into the Docker instance ```bash -docker exec -it some-jdk11-mvn-py3 /bin/bash +docker exec -it some-jdk-mvn-py3 /bin/bash ``` # Run Python code @@ -63,13 +118,13 @@ docker exec -it some-jdk11-mvn-py3 /bin/bash To run Python code ```bash -docker run -it --rm openkbs/jdk11-mvn-py3 python3 -c 'print("Hello World")' +docker run -it --rm openkbs/jdk-mvn-py3 python3 -c 'print("Hello World")' ``` or, ```bash -docker run -i --rm openkbs/jdk11-mvn-py3 python3 < myPyScript.py +docker run -i --rm openkbs/jdk-mvn-py3 python3 < myPyScript.py ``` or, @@ -77,22 +132,22 @@ or, ```bash mkdir ./data echo "print('Hello World')" > ./data/myPyScript.py -docker run -it --rm --name some-jdk11-mvn-py3 -v "$PWD"/data:/data openkbs/jdk11-mvn-py3 python3 myPyScript.py +docker run -it --rm --name some-jdk-mvn-py3 -v "$PWD"/data:/data openkbs/jdk-mvn-py3 python3 myPyScript.py ``` or, ```bash -alias dpy3='docker run --rm openkbs/jdk11-mvn-py3 python3' +alias dpy3='docker run --rm openkbs/jdk-mvn-py3 python3' dpy3 -c 'print("Hello World")' ``` -# Compile or Run java while no local installation needed +# Compile or Run java -- while no local installation needed Remember, the default working directory, /data, inside the docker container -- treat is as "/". So, if you create subdirectory, "./data/workspace", in the host machine and the docker container will have it as "/data/workspace". -```java +``` #!/bin/bash -x mkdir ./data cat >./data/HelloWorld.java <<-EOF @@ -103,9 +158,8 @@ public class HelloWorld { } EOF cat ./data/HelloWorld.java -alias djavac='docker run -it --rm --name some-jdk11-mvn-py3 -v '$PWD'/data:/data openkbs/jdk11-mvn-py3 javac' -alias djava='docker run -it --rm --name some-jdk11-mvn-py3 -v '$PWD'/data:/data openkbs/jdk11-mvn-py3 java' - +alias djavac='docker run -it --rm --name some-jdk-mvn-py3 -v '$PWD'/data:/data openkbs/jdk-mvn-py3 javac' +alias djava='docker run -it --rm --name some-jdk-mvn-py3 -v '$PWD'/data:/data openkbs/jdk-mvn-py3 java' djavac HelloWorld.java djava HelloWorld ``` @@ -115,56 +169,276 @@ Hello, World ``` Hence, the alias above, "djavac" and "djava" is your docker-based "javac" and "java" commands and it will work the same way as your local installed Java's "javac" and "java" commands. + +# Run JavaScript -- while no local installation needed +Run the NodeJS mini-server script: +``` +./tryNodeJS.sh +``` +Then, open web browser to go to http://0.0.0.0:3000/ to NodeJS mini-web server test. + +# Python Virtual Environments +There are various ways to run Python virtual envrionments, for example, + +### Setup virtualenvwrapper in $HOME/.bashrc profile +Add the following code to the end of ~/.bashrc +``` +######################################################################### +#### ---- Customization for multiple virtual python environment ---- #### +######################################################################### +export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 +source /usr/local/bin/virtualenvwrapper.sh +export WORKON_HOME=~/Envs +if [ ! -d $WORKON_HOME ]; then + mkdir -p $WORKON_HOME +fi +``` + +### To create & activate your default venv environment, say, "my-venv": +``` +mkvirtualenv my-venv +workon my-venv +``` + +# To run specialty Java/Scala IDE alternatives However, for larger complex projects, you might want to consider to use Docker-based IDE. -For example, try this docker-scala-ide: -[Eclipse-Oxygen-Docker](https://github.com/DrSnowbird/eclipse-oxygen-docker) -[Scala IDE in Docker](https://github.com/stevenalexander/docker-scala-ide) -See also, -[Java Development in Docker](https://blog.giantswarm.io/getting-started-with-java-development-on-docker/) - -# Versions -``` -root@394fabcb40a4:/data# ./printVersions.sh -JAVA_HOME=/usr/jdk-11.0.1 -java version "11.0.1" 2018-10-16 LTS -Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS) -Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode) -Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-17T18:33:14Z) -Maven home: /usr/apache-maven-3.5.4 -Java version: 11.0.1, vendor: Oracle Corporation, runtime: /usr/jdk-11.0.1 -Default locale: en_US, platform encoding: ANSI_X3.4-1968 -OS name: "linux", version: "4.15.0-36-generic", arch: "amd64", family: "unix" -Python 3.5.2 -Python 3.5.2 -pip 8.1.1 from /usr/lib/python3/dist-packages (python 3.5) +For example, try the following Docker-based IDEs: +* [openkbs/docker-atom-editor](https://hub.docker.com/r/openkbs/docker-atom-editor/) +* [openkbs/eclipse-photon-docker](https://hub.docker.com/r/openkbs/eclipse-photon-docker/) +* [openkbs/eclipse-photon-vnc-docker](https://hub.docker.com/r/openkbs/eclipse-photon-vnc-docker/) +* [openkbs/eclipse-oxygen-docker](https://hub.docker.com/r/openkbs/eclipse-oxygen-docker/) +* [openkbs/intellj-docker](https://hub.docker.com/r/openkbs/intellij-docker/) +* [openkbs/intellj-vnc-docker](https://hub.docker.com/r/openkbs/intellij-vnc-docker/) +* [openkbs/knime-vnc-docker](https://hub.docker.com/r/openkbs/knime-vnc-docker/) +* [openkbs/netbeans9-docker](https://hub.docker.com/r/openkbs/netbeans9-docker/) +* [openkbs/netbeans](https://hub.docker.com/r/openkbs/netbeans/) +* [openkbs/papyrus-sysml-docker](https://hub.docker.com/r/openkbs/papyrus-sysml-docker/) +* [openkbs/pycharm-docker](https://hub.docker.com/r/openkbs/pycharm-docker/) +* [openkbs/scala-ide-docker](https://hub.docker.com/r/openkbs/scala-ide-docker/) +* [openkbs/sublime-docker](https://hub.docker.com/r/openkbs/sublime-docker/) +* [openkbs/webstorm-docker](https://hub.docker.com/r/openkbs/webstorm-docker/) +* [openkbs/webstorm-vnc-docker](https://hub.docker.com/r/openkbs/webstorm-vnc-docker/) + +# See also +* [Java Development in Docker](https://blog.giantswarm.io/getting-started-with-java-development-on-docker/) +* [Alpine small image JDKs](https://github.com/frol/docker-alpine-oraclejdk8) +* [NPM Prefix for not using SUDO NPM](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) + +# Proxy & Certificate Setup +* [Setup System and Browsers Root Certificate](https://thomas-leister.de/en/how-to-import-ca-root-certificate/) + +# Corporate Proxy Root and Intemediate Certificates setup for System and Web Browsers (FireFox, Chrome, etc) +1. Save your corporate's Certificates in the currnet GIT directory, `./certificates` +2. During Docker run command, +``` + -v `pwd`/certificates:/certificates ... (the rest parameters) +``` +If you want to map to different directory for certificates, e.g., /home/developer/certificates, then +``` + -v `pwd`/certificates:/home/developer/certificates -e SOURCE_CERTIFICATES_DIR=/home/developer/certificates ... (the rest parameters) +``` +3. And, inside the Docker startup script to invoke the `~/scripts/setup_system_certificates.sh`. Note that the script assumes the certficates are in `/certificates` directory. +4. The script `~/scripts/setup_system_certificates.sh` will automatic copy to target directory and setup certificates for both System commands (wget, curl, etc) to use and Web Browsers'. + +# Releases information +``` +developer@94b0e5d67706:~$ /usr/scripts/printVersions.sh ++ echo JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 +JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 ++ whereis java +java: /usr/bin/java /usr/share/java /usr/lib/jvm/java-11-openjdk-amd64/bin/java /usr/share/man/man1/java.1.gz ++ echo + ++ java -version +openjdk version "11.0.9" 2020-10-20 +OpenJDK Runtime Environment (build 11.0.9+11-Ubuntu-0ubuntu1.20.04) +OpenJDK 64-Bit Server VM (build 11.0.9+11-Ubuntu-0ubuntu1.20.04, mixed mode, sharing) ++ mvn --version +Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) +Maven home: /usr/apache-maven-3.6.3 +Java version: 11.0.9, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64 +Default locale: en, platform encoding: UTF-8 +OS name: "linux", version: "5.4.0-52-generic", arch: "amd64", family: "unix" ++ python -V +/usr/scripts/printVersions.sh: line 8: python: command not found ++ python3 -V +Python 3.8.5 ++ pip --version +pip 20.2.4 from /usr/local/lib/python3.8/dist-packages/pip (python 3.8) ++ pip3 --version +pip 20.2.4 from /usr/local/lib/python3.8/dist-packages/pip (python 3.8) ++ gradle --version + +Welcome to Gradle 6.7! + +Here are the highlights of this release: + - File system watching is ready for production use + - Declare the version of Java your build requires + - Java 15 support + +For more details see https://docs.gradle.org/6.7/release-notes.html + + ------------------------------------------------------------ -Gradle 4.10 +Gradle 6.7 ------------------------------------------------------------ -Build time: 2018-08-27 18:35:06 UTC -Revision: ee3751ed9f2034effc1f0072c2b2ee74b5dce67d +Build time: 2020-10-14 16:13:12 UTC +Revision: 312ba9e0f4f8a02d01854d1ed743b79ed996dfd3 -Kotlin DSL: 1.0-rc-3 -Kotlin: 1.2.60 -Groovy: 2.4.15 -Ant: Apache Ant(TM) version 1.9.11 compiled on March 23 2018 -JVM: 11.0.1 (Oracle Corporation 11.0.1+13-LTS) -OS: Linux 4.15.0-36-generic amd64 +Kotlin: 1.3.72 +Groovy: 2.5.12 +Ant: Apache Ant(TM) version 1.10.8 compiled on May 10 2020 +JVM: 11.0.9 (Ubuntu 11.0.9+11-Ubuntu-0ubuntu1.20.04) +OS: Linux 5.4.0-52-generic amd64 ++ npm -v +7.0.8 ++ node -v +v15.1.0 ++ cat /etc/lsb-release /etc/os-release DISTRIB_ID=Ubuntu -DISTRIB_RELEASE=16.04 -DISTRIB_CODENAME=xenial -DISTRIB_DESCRIPTION="Ubuntu 16.04.3 LTS" +DISTRIB_RELEASE=20.04 +DISTRIB_CODENAME=focal +DISTRIB_DESCRIPTION="Ubuntu 20.04.1 LTS" NAME="Ubuntu" -VERSION="16.04.3 LTS (Xenial Xerus)" +VERSION="20.04.1 LTS (Focal Fossa)" ID=ubuntu ID_LIKE=debian -PRETTY_NAME="Ubuntu 16.04.3 LTS" -VERSION_ID="16.04" -HOME_URL="http://www.ubuntu.com/" -SUPPORT_URL="http://help.ubuntu.com/" -BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" -VERSION_CODENAME=xenial -UBUNTU_CODENAME=xenial - +PRETTY_NAME="Ubuntu 20.04.1 LTS" +VERSION_ID="20.04" +HOME_URL="https://www.ubuntu.com/" +SUPPORT_URL="https://help.ubuntu.com/" +BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" +PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" +VERSION_CODENAME=focal +UBUNTU_CODENAME=focal``` + +## Python3 PIP Modules +``` +developer@94b0e5d67706:~$ pip freeze +appdirs==1.4.4 +argon2-cffi==20.1.0 +async-generator==1.10 +atomicwrites==1.1.5 +attrs==20.3.0 +backcall==0.2.0 +beautifulsoup4==4.8.2 +bleach==3.2.1 +certifi==2020.11.8 +cffi==1.14.3 +chardet==3.0.4 +cloudpickle==1.6.0 +cycler==0.10.0 +dbus-python==1.2.16 +decorator==4.4.2 +defusedxml==0.6.0 +distlib==0.3.1 +distro-info===0.23ubuntu1 +entrypoints==0.3 +et-xmlfile==1.0.1 +filelock==3.0.12 +funcy==1.15 +future==0.18.2 +html5lib==1.0.1 +httpie==2.3.0 +hyperopt==0.2.5 +idna==2.10 +importlib-metadata==1.5.0 +iniconfig==1.1.1 +ipaddress==1.0.23 +ipykernel==5.3.4 +ipython==7.19.0 +ipython-genutils==0.2.0 +ipywidgets==7.5.1 +j2cli==0.3.10 +jdcal==1.0 +jedi==0.17.2 +Jinja2==2.11.2 +joblib==0.17.0 +json-lines==0.5.0 +jsonschema==3.2.0 +jupyter==1.0.0 +jupyter-client==6.1.7 +jupyter-console==6.2.0 +jupyter-core==4.6.3 +jupyterlab-pygments==0.1.2 +kiwisolver==1.3.1 +lxml==4.5.0 +MarkupSafe==1.1.1 +matplotlib==3.3.3 +mistune==0.8.4 +more-itertools==4.2.0 +nbclient==0.5.1 +nbconvert==6.0.7 +nbformat==5.0.8 +nest-asyncio==1.4.3 +networkx==2.5 +notebook==6.1.5 +numexpr==2.7.1 +numpy==1.19.4 +olefile==0.46 +openpyxl==3.0.3 +packaging==20.4 +panda==0.3.1 +pandas==1.1.4 +pandasql==0.7.3 +pandocfilters==1.4.3 +parso==0.7.1 +pbr==5.5.1 +pexpect==4.8.0 +pickleshare==0.7.5 +Pillow==8.0.1 +pkgconfig==1.5.1 +pluggy==0.13.1 +prometheus-client==0.8.0 +prompt-toolkit==3.0.8 +ptyprocess==0.6.0 +py==1.9.0 +pycparser==2.20 +Pygments==2.7.2 +PyGObject==3.36.0 +pyLDAvis==2.1.2 +pyparsing==2.4.7 +pyrsistent==0.17.3 +pytest==6.1.2 +python-apt==2.0.0+ubuntu0.20.4.1 +python-dateutil==2.8.1 +python-git==2018.2.1 +pytz==2020.4 +PyYAML==5.3.1 +pyzmq==19.0.2 +qtconsole==4.7.7 +QtPy==1.9.0 +requests==2.25.0 +requests-toolbelt==0.9.1 +requests-unixsocket==0.2.0 +scikit-learn==0.23.2 +scipy==1.5.4 +seaborn==0.11.0 +Send2Trash==1.5.0 +six==1.15.0 +soupsieve==1.9.5 +SQLAlchemy==1.3.20 +stevedore==3.2.2 +tables==3.6.1 +terminado==0.9.1 +testpath==0.4.4 +threadpoolctl==2.1.0 +toml==0.10.2 +tornado==6.1 +tqdm==4.51.0 +traitlets==5.0.5 +unattended-upgrades==0.1 +urllib3==1.26.1 +virtualenv==20.1.0 +virtualenv-clone==0.5.4 +virtualenvwrapper==4.8.4 +wcwidth==0.2.5 +webencodings==0.5.1 +widgetsnbextension==3.5.1 +xlrd==1.1.0 +xlwt==1.3.0 +yml2json==1.1.3 +zipp==1.0.0 ``` diff --git a/build.sh b/build.sh old mode 100644 new mode 100755 index 1253609..2eceb51 --- a/build.sh +++ b/build.sh @@ -1,12 +1,67 @@ #!/bin/bash +set -e + # Reference: # - https://docs.docker.com/engine/userguide/containers/dockerimages/ # - https://github.com/dockerfile/java/blob/master/oracle-java8/Dockerfile if [ $# -lt 1 ]; then + echo "-------------------------------------------------------------------------------------------" echo "Usage: " - echo " ${0} " + echo " ${0} [ [] ] " + echo "e.g." + echo " ./build.sh ./Dockerfile-openjdk11 openkbs/jdk11-mvn-py3 --no-cache " + echo " ./build.sh ./centos/Dockerfile.centos.xfce.vnc openkbs/centos-xfce-vnc --no-cache --build-arg OS_TYPE=centos'" + echo " ./build.sh ./Dockerfile.ubuntu.xfce.vnc openkbs/ubuntu-xfce-vnc --no-cache --build-arg OS_TYPE=centos'" + echo "-------------------------------------------------------------------------------------------" +fi + +DOCKERFILE=${1:-./Dockerfile} +if [[ "$OSTYPE" == "linux-gnu" ]]; then + # Linux + MY_DIR=$(dirname "$(readlink -f "$0")") + DOCKERFILE=$(realpath $DOCKERFILE) +elif [[ "$OSTYPE" == "darwin"* ]]; then + # Mac OSX + MY_DIR=`pwd` +else + MY_DIR=`pwd` +fi + +BUILD_CONTEXT=$(dirname ${DOCKERFILE}) + +imageTag=${2} + +if [ $# -gt 2 ]; then + shift 2 + options="$*" +else + options="" +fi + +########################################################## +#### ---- Whether to remove previous build cache ---- #### +#### ---- Valid value: 0 (No remove); 1 (yes, remove) +########################################################## +REMOVE_CACHE=0 + +############################################################################### +############################################################################### +############################################################################### +#### ---- DO NOT Change the code below UNLESS you really want to !!!!) --- #### +#### ---- DO NOT Change the code below UNLESS you really want to !!!!) --- #### +#### ---- DO NOT Change the code below UNLESS you really want to !!!!) --- #### +############################################################################### +############################################################################### +############################################################################### + +########################################################## +#### ---- Generate remove cache option if needed ---- #### +########################################################## +REMOVE_CACHE_OPTION="" +if [ ${REMOVE_CACHE} -gt 0 ]; then + REMOVE_CACHE_OPTION="--no-cache --rm" fi ################################################### @@ -14,32 +69,118 @@ fi ################################################### ORGANIZATION=openkbs +################################################### +#### ---- Detect Docker Run Env files ---- +################################################### + +function detectDockerBuildEnvFile() { + curr_dir=`pwd` + if [ -s "${DOCKER_ENV_FILE}" ]; then + echo "--- INFO: Docker Build Environment file '${DOCKER_ENV_FILE}' FOUND!" + else + echo "*** WARNING: Docker Build Environment file '${DOCKER_ENV_FILE}' NOT found!" + echo "*** WARNING: Searching for .env or docker.env as alternative!" + echo "*** --->" + if [ -s "./docker-build.env" ]; then + echo "--- INFO: ./docker-build.env FOUND to use as Docker Run Environment file!" + DOCKER_ENV_FILE="./docker-build.env" + else + if [ -s "./.env" ]; then + echo "--- INFO: ./.env FOUND to use as Docker Run Environment file!" + DOCKER_ENV_FILE="./.env" + else + echo "--- INFO: ./.env Docker Environment file (.env) NOT found!" + if [ -s "./docker.env" ]; then + echo "--- INFO: ./docker.env FOUND to use as Docker Run Environment file!" + DOCKER_ENV_FILE="./docker.env" + else + echo "*** WARNING: Docker Environment file (.env) or (docker.env) NOT found!" + fi + fi + fi + fi +} +detectDockerBuildEnvFile + +################################################### +#### ---- Container package information ---- +################################################### +DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` +imageTag=${imageTag:-"${ORGANIZATION}/${DOCKER_IMAGE_REPO}"} + ################################################### #### ---- Generate build-arg arguments ---- ################################################### BUILD_ARGS="" -ARGS_DEFINITION_FILE="./docker.env" +BUILD_DATE="`date -u +"%Y-%m-%dT%H:%M:%SZ"`" +VCS_REF="`git rev-parse --short HEAD`" +VCS_URL="https://github.com/`echo $(basename $PWD)`" +BUILD_ARGS="--build-arg BUILD_DATE=${BUILD_DATE} --build-arg VCS_REF=${VCS_REF}" + ## -- ignore entries start with "#" symbol -- function generateBuildArgs() { - for r in `cat ${ARGS_DEFINITION_FILE} | grep -v '^#'`; do - echo "entry=$r" - key=`echo $r | tr -d ' ' | cut -d'=' -f1` - value=`echo $r | tr -d ' ' | cut -d'=' -f2` - BUILD_ARGS="${BUILD_ARGS} --build-arg $key=$value" - done + if [ "${DOCKER_ENV_FILE}" != "" ] && [ -s "${DOCKER_ENV_FILE}" ]; then + for r in `cat ${DOCKER_ENV_FILE} | grep -v '^#'`; do + echo "entry=> $r" + key=`echo $r | tr -d ' ' | cut -d'=' -f1` + value=`echo $r | tr -d ' ' | cut -d'=' -f2` + BUILD_ARGS="${BUILD_ARGS} --build-arg $key=$value" + done + fi } generateBuildArgs echo "BUILD_ARGS=${BUILD_ARGS}" ################################################### -#### ---- Container package information ---- +#### ---- Setup Docker Build Proxy ---- +################################################### +# export NO_PROXY="localhost,127.0.0.1,.openkbs.org" +# export HTTP_PROXY="http://gatekeeper-w.openkbs.org:80" +# when using "wget", add "--no-check-certificate" to avoid https certificate checking failures +# +echo "... Setup Docker Build Proxy: ..." +PROXY_PARAM= +function generateProxyArgs() { + if [ "${HTTP_PROXY}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} --build-arg HTTP_PROXY=${HTTP_PROXY}" + fi + if [ "${HTTPS_PROXY}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} --build-arg HTTPS_PROXY=${HTTPS_PROXY}" + fi + if [ "${NO_PROXY}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} --build-arg NO_PROXY=\"${NO_PROXY}\"" + fi + if [ "${http_proxy}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} --build-arg http_proxy=${http_proxy}" + fi + if [ "${https_proxy}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} --build-arg https_proxy=${https_proxy}" + fi + if [ "${no_proxy}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} --build-arg no_proxy=\"${no_proxy}\"" + fi + BUILD_ARGS="${BUILD_ARGS} ${PROXY_PARAM}" +} +generateProxyArgs +echo -e "BUILD_ARGS=> \n ${BUILD_ARGS}" +echo + +################################################### +#### ----------- Build Container ------------ ##### ################################################### -DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` -imageTag=${1:-"${ORGANIZATION}/${DOCKER_IMAGE_REPO}"} -docker build --rm -t ${imageTag} \ +cd ${BUILD_CONTEXT} +set -x +sudo docker build ${REMOVE_CACHE_OPTION} -t ${imageTag} \ ${BUILD_ARGS} \ - -f Dockerfile . + ${options} \ + -f $(basename ${DOCKERFILE}) . +set +x +cd - + +################################################### +#### --------- More Guides for Users -------- ##### +################################################### echo "----> Shell into the Container in interactive mode: " echo " docker exec -it --name /bin/bash" @@ -60,5 +201,5 @@ echo "----> Build Docker Images again: " echo "To build again: (there is a dot at the end of the command!)" echo " docker build -t ${imageTag} . " echo -docker images |grep "$imageTag" +sudo docker images |grep "$imageTag" diff --git a/certificates/dummy.certificate b/certificates/dummy.certificate new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/certificates/dummy.certificate @@ -0,0 +1 @@ +test diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..d6af6d0 --- /dev/null +++ b/clean.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +MY_DIR=$(dirname "$(readlink -f "$0")") + +if [ $# -lt 1 ]; then + echo "------------- Clean up both Container and Images -------------" + echo "Usage: " + echo " ${0} []" + echo "e.g.: " + echo " ${0} tensorflow-python3-jupyter " + echo " ${0} " + echo " (empty argument will use default the current git container name to clean up)" +fi + +################################################### +#### ---- Change this only to use your own ---- +################################################### +ORGANIZATION=openkbs +baseDataFolder="$HOME/data-docker" + +################################################### +#### **** Container package information **** +################################################### +DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` +imageTag="${ORGANIZATION}/${DOCKER_IMAGE_REPO}" + +## -- transform '-' and space to '_' +#instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/\-: " "_"` +instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/: " "_"` + +echo "---------------------------------------------" +echo "---- Clean up the Container for ${imageTag}" +echo "---------------------------------------------" + +if [ $1 ]; then + imageTag="$1" +fi + +containers=`docker ps -a | grep ${imageTag} | awk '{print $1}' ` + +if [ $containers ]; then + docker rm -f $containers +fi + +for IMAGE_ID in `docker images -a | grep ${imageTag} | awk '{print $3}' `; do + children=$(docker images --filter since=${IMAGE_ID} -q) + if [[ ! $children == *"No such image"* ]]; then + id=$(docker inspect --format='{{.Id}} {{.Parent}}' $children |cut -d':' -f2|cut -c-12) + if [ "$id" != "" ]; then + docker rmi -f $id + fi + fi +done + + diff --git a/commit-push.sh b/commit-push.sh old mode 100644 new mode 100755 index 67d8f8b..22532c5 --- a/commit-push.sh +++ b/commit-push.sh @@ -1,6 +1,11 @@ #!/bin/bash -# Reference: https://docs.docker.com/engine/userguide/containers/dockerimages/ +# Reference: +# - https://docs.docker.com/engine/userguide/containers/dockerimages/ +# - https://docs.docker.com/engine/reference/commandline/commit/ +# docker push [OPTIONS] NAME[:TAG] +# - https://docs.docker.com/engine/reference/commandline/push/ +# docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] echo "Usage: " echo " ${0} [] []" @@ -14,24 +19,26 @@ echo " docker login" echo "-------------------------------------" echo -comment=${1:-Update with the latest changes} +ORGANIZATION=${ORGANIZATION:-openkbs} + +comment=${1:-Update with the latest changes: `date`} ################################################### #### **** Container package information **** ################################################### DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` -imageTag=${2:-"openkbs/${DOCKER_IMAGE_REPO}"} -imageVersion=${3:-"1.0.0"} +imageTag=${2:-"${ORGANIZATION}/${DOCKER_IMAGE_REPO}"} +imageVersion=${3:-latest} docker ps -a containerID=`docker ps |grep "${imageTag} "|awk '{print $1}'` echo "containerID=$containerID" -#docker tag ${imageTag} openkbs/${imageTag}:latest +#docker tag ${imageTag} ${imageTag}:latest -docker commit -m "$comment" ${containerID} ${imageTag}:latest -docker push ${imageTag}:latest +#docker commit -m "$comment" ${containerID} ${imageTag}:latest +#docker push ${imageTag}:latest -docker commit -m "$comment" ${containerID} ${imageTag}:${imageVersion} -docker push ${imageTag}:${imageVersion} +echo docker commit -m "$comment" ${containerID} ${imageTag}:${imageVersion} +echo docker push ${imageTag}:${imageVersion} diff --git a/data/myPyScript.py b/data/myPyScript.py index df1dc68..02c4fec 100644 --- a/data/myPyScript.py +++ b/data/myPyScript.py @@ -1 +1 @@ -print('Hello World') +print('Hello World (./data/myPyScript.py) from ./tryPython.sh') diff --git a/doc/multi-container-files-sharing.png b/doc/multi-container-files-sharing.png new file mode 100644 index 0000000000000000000000000000000000000000..717dd75af55fb7d59392489e59fac839ab7be663 GIT binary patch literal 64032 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 MC6&OX;-3Qk2h65dg8%>k literal 0 HcmV?d00001 diff --git a/doc/openkbs-jdk-mvn-py3_web-socket-server-test.png b/doc/openkbs-jdk-mvn-py3_web-socket-server-test.png new file mode 100644 index 0000000000000000000000000000000000000000..ee33d3cb8b4e09071f1172d1a2b20b10fbe84786 GIT binary patch literal 81639 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 literal 0 HcmV?d00001 diff --git a/doc/proxy-setup-in-Dockerfile.txt b/doc/proxy-setup-in-Dockerfile.txt new file mode 100644 index 0000000..36eaa59 --- /dev/null +++ b/doc/proxy-setup-in-Dockerfile.txt @@ -0,0 +1,49 @@ +#################################################################### +#### Usage: +#### 1.) In you ~/.bashrc (most linux), setup: +#### export http_proxy= +#### export https_proxy= +#### +#### 2.) Add into the currect Xterm or Console: +#### source ~/.bashrc +#### +#### 3.) Add the following code segments into the "Dockerfile" to make it fit into +#### your corporate Proxy environments +#### +#################################################################### + +#### ==== Add the following code with your corporate Proxy servers setup to make Dockerfile to work with proxy ==== ##### + +#### ---- Proxy & Certificate setup ---- + +ENV https_proxy=${https_proxy:-http://proxy.openkbs.org:80} +ENV http_proxy=${https_proxy:-http://proxy.openkbs.org:80} +ENV no_proxy=${https_proxy:-localhost,127.0.0.1,.openkbs.org} + +RUN export https_proxy=${https_proxy} && \ + export http_proxy=${https_proxy} && \ + export no_proxy=${https_proxy} + +#### ---- CA Certificates locations ---- #### +# (for Ubuntu): +# Directory of CA certificates. +# /usr/share/ca-certificates +# Directory of local CA certificates (with .crt extension). +# /usr/local/share/ca-certificates +# (for Ubuntu): +ENV CERTIFICATE_DIR=/usr/local/share/ca-certificates +# (for CentOS) +# ENV CERTIFICATE_DIR=/etc/pki/ca-trust/source/anchors + +#### --- Some example CA examples: Changed to your own specifics ---- #### +RUN \ + mkdir -p ${CERTIFICATE_DIR} && \ + wget -O ${CERTIFICATE_DIR}/openkbs-BA-Root.crt http://pki.openkbs.org/openkbs-BA-Root.crt && \ + wget -O ${CERTIFICATE_DIR}/openkbs-BA-NPE-CA-3.crt http://pki.openkbs.org/openkbs-BA-NPE-CA-3.crt && \ + wget -O ${CERTIFICATE_DIR}/openkbs-BA-NPE-CA-4.crt http://pki.openkbs.org/openkbs-BA-NPE-CA-4.crt && \ + update-ca-certificates # (for Ubuntu OS) + # update-ca-trust extract # (for CentOS OS) + +RUN sudo /usr/bin/npm config set proxy ${http_proxy} && \ + sudo /usr/bin/npm config set http_proxy ${http_proxy} && \ + sudo /usr/bin/npm config set https_proxy ${https_proxy} diff --git a/doc/simple-multi-container-host-based-file-sharing.pptx b/doc/simple-multi-container-host-based-file-sharing.pptx new file mode 100644 index 0000000000000000000000000000000000000000..8f8b212c75dfa4a61fef501d949f10bb3fb9bd2d GIT binary patch literal 36997 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®z5Z@`M=nNG)R4W;MRw18Dv3wWBF5yBZZWevt{?_t5uvKEyKf75*_N` zMqMuBZ8)gYO`Fn&@HhCEsAV%m1Z5|5=pChN^&KHDf@N@)!`=P165%Wn?|Td!_1?0# zzh7jI4cuE=?mR#n7crJARBC@Wy=;sduJ*+D-gX*v4FiS;S1d1l?xKDLBUgT*JifA;{6r-igh~-X6M+ zDJfi8xtSf~B$MGZ z2lP{88%pLDuN8A0SRk4a?++?R#%wMuT7RIjS*cDdqU1-+#W1HlxMLgo(;$0$`&<2w zdx>IS9e8~(r6}#C*cNA0Vnx9-`&FI<#6u^@rEUn^?kpOt^&`&0}$l?vy5@${F8cU z=V%IjR3WN*%1uD>cwdXzb9x+;rmrX>dsg-)E<4Gz)l=f3f=A;`Gt(0`)^E0FCWQAB zlaGeRjzI(;C|*TJ%c+pv!1I}l4rf&U+~s~l31mpF=8GTgi%0(9t(yzieEfE2V@VD- z5#0x*DH)wk=iu@l%i#BmVEVX_S}`|AwK#|XwqhvHya6+EA`WLO4L@z{!%7Z}-I*Yz z7+>rdck|$35qk{8s5glp9q{H$hMzI44I#5Qr7~v-T2(Txb%=TvIpPdElMkh7E+2($ zO>A2Zg_6h(h{)#Qc#SbN6}Cx2T-GSm?&mO;kfg8rOX;^ zkT$axDhwBy&pf;qLz3pCr68;z0kH43rBb}6hrf^t1S2X2 zaxcZl9s+M?0%{vS3-2;1d5eaVMi{&Gb8_eu*VJ@=CfU$IGU> z=prTRpo3EJD$*89y-F`M8@n?9aPzwWp5 zla*D=OhDb!)9X#X7+IaB{Dz_eMFS6r0=tdgy4)SDVotI5xB`TmlwaDqprZvVR|6p*%88Zpy1gY52SDe5~F30f#9mD1oASI3`v% zO>XOe5aL)-Im=Ku%TPI;Q8=AZIirQw5kT~}9@vnz6+OQ(gCEV1&htFRu+H!P#G1Lw zTb5Odlh0F5yS*(X6l^CFE2kAc<`c`BO@IkF0dE;3f9*OR93C7B3JP4QlZ+B3lJLv~ z@bX0f^8k9gOV{a<)U1xC)=rmiZ1}Mxnh*Rsv|xtv!fklKCx30NaG{*)Z2k9T+WC{R z@h{M@fQFr;!Iq%FIF}LU50-`=seN|Y1NnR?;tvOGUxuCEG#^^1dUp8%PYDXUd@}Xb z3A?oaMu2U#f0=N8Ba4e|FcUi^T;4n6GU5C-4bal1vujcOlmL62SJ?jIGWmRoFVz_q z_)b>xZ^-BV%-_Dd^VND6|5bDOQ{3e$;mZv=U&RJpczkxPD4%Q4zf#txrT}Z!=||^k z{4S%ZN8^-74li}#?{H< z53WK!tBCj$`JxlbfUiR4I4d)_j6T1b`1V=90eq*V`#0!+>ohL&&M&aM$ZI$FFTB6) z9M3OHyV%zvKKwHe);(Nq-uZ<-7jd`D{)~h5372u_mr-2Aky-v32Q#C~xbxZli#SZ{ zKjZ%P*_}_bUc`O0`7;jIOCZS=hjbZtUJAX4 zd+qXP9IUUnj5{w7UBnH#{TT;yxb{(Mp7VoL?AH=2S)>ZJprbW`+m@${sE8u ByH)@I literal 0 HcmV?d00001 diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..d14db9b --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e + +env + +#### ---- Make sure to provide Non-root user for launching Docker ---- +#### ---- Default, we use base images's "developer" ---- +NON_ROOT_USER=${NON_ROOT_USER:-"developer"} + +#### ------------------------------------------------------------------------ +#### ---- You need to set PRODUCT_EXE as the full-path executable binary ---- +#### ------------------------------------------------------------------------ +echo "Starting docker process daemon ..." +if [ "${PRODUCT_EXE}" == "" ]; then + PRODUCT_EXE=${PRODUCT_EXE:-echo Hello} + #/bin/bash -c "${PRODUCT_EXE:-echo Hello}" + /usr/scripts/printVersions.sh + /bin/bash +else + #### ------------------------------------------------------------------------ + #### ---- Extra line added in the script to run all command line arguments + #### ---- To keep the docker process staying alive if needed. + #### ------------------------------------------------------------------------ + + if [ $# -gt 0 ]; then + #### **** Allow non-root users to bind to use lower than 1000 ports **** #### + USE_CAP_NET_BIND=${USE_CAP_NET_BIND:-0} + if [ ${USE_CAP_NET_BIND} -gt 0 ]; then + sudo setcap 'cap_net_bind_service=+ep' ${PRODUCT_EXE} + fi + + #### 1.) Setup needed stuffs, e.g., init db etc. .... + #### (do something here for preparation) + + #### 2.A) As Root User -- Choose this or 2.B --#### + #### ---- Use this when running Root user ---- #### + exec "$@" + #/bin/bash -c "$@" + + #### 2.B) As Non-Root User -- Choose this or 2.A ---- #### + #### ---- Use this when running Non-Root user ---- #### + #### ---- Use gosu (or su-exec) to drop to a non-root user + #exec gosu ${NON_ROOT_USER} ${PRODUCT_EXE} "$@" + else + exec "${PRODUCT_EXE}"; + fi +fi + diff --git a/examples/HelloWorld.java b/examples/HelloWorld.java new file mode 100644 index 0000000..208e986 --- /dev/null +++ b/examples/HelloWorld.java @@ -0,0 +1,5 @@ +public class HelloWorld { + public static void main(String[] args) { + System.out.println("Hello, World"); + } +} diff --git a/examples/myPyScript.py b/examples/myPyScript.py new file mode 100644 index 0000000..df1dc68 --- /dev/null +++ b/examples/myPyScript.py @@ -0,0 +1 @@ +print('Hello World') diff --git a/examples/simple-server.js b/examples/simple-server.js new file mode 100644 index 0000000..7f869b1 --- /dev/null +++ b/examples/simple-server.js @@ -0,0 +1,19 @@ +// +// To run: nodejs ./simple-server.js +// Then => Open http://localhost:3000/ +// To see: Hello World! +// +const http = require('http'); + +const hostname = '0.0.0.0'; +const port = 3000; + +const server = http.createServer((req, res) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end('Hello World from openkbs/jdk-mvn-py3 \n See more: \n https://github.com/DrSnowbird/3 \n https://cloud.docker.com/u/openkbs'); +}); + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); diff --git a/logs.sh b/logs.sh old mode 100644 new mode 100755 index 1081082..765b54b --- a/logs.sh +++ b/logs.sh @@ -21,15 +21,6 @@ baseDataFolder="$HOME/data-docker" DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` imageTag="${ORGANIZATION}/${DOCKER_IMAGE_REPO}" -################################################### -#### ---- Mostly, you don't need change below ---- -################################################### -function cleanup() { - if [ ! "`docker ps -a|grep ${instanceName}`" == "" ]; then - echo "docker rm -f ${instanceName}" - fi -} - ## -- transform '-' and space to '_' #instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/\-: " "_"` instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/: " "_"` @@ -37,6 +28,6 @@ instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/: " " echo "---------------------------------------------" echo "---- Print Log for Container for ${imageTag}" echo "---------------------------------------------" -docker logs ${instanceName} +sudo docker logs ${instanceName} diff --git a/requirements.txt b/requirements.txt index 738ace9..bb93cf9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,39 @@ +# Basic packages +future pkgconfig -ipykernel -jupyter -Pillow +httpie +hyperopt +ipaddress +pyparsing +requests +j2cli +jinja2 +hyperopt +json-lines +python-git +PyYAML +yml2json +python-dateutil + +# Math / Algebra Libs numpy -scipy panda +pandas pandasql -matplotlib +Pillow scikit_learn -Flask +scipy + +# Plots +matplotlib +seaborn +pyLDAvis + +# Virtual env +virtualenv +virtualenvwrapper + +# Jypyter +ipykernel +jupyter + diff --git a/restore.sh b/restore.sh new file mode 100755 index 0000000..28942e6 --- /dev/null +++ b/restore.sh @@ -0,0 +1,47 @@ +#!/bin/bash -x + +MY_DIR=$(dirname "$(readlink -f "$0")") + +if [ $# -lt 1 ]; then + echo "Usage: " + echo " ${0} [Different tgz Docker Image file]" + echo "e.g.: " + echo " ${0} " +fi + +################################################### +#### ---- Change this only to use your own ---- +################################################### +ORGANIZATION=openkbs + +################################################### +#### **** Container package information **** +################################################### +DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` +imageTag="${ORGANIZATION}/${DOCKER_IMAGE_REPO}" + +################################################### +#### ---- Mostly, you don't need change below ---- +################################################### +## -- transform '-' and space to '_' +#instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/\-: " "_"` +instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/: " "_"` + +TGZ_DOCKER_IMAGE=${1:-${instanceName}.tgz} + +function restore() { + if [ ! -s ${TGZ_DOCKER_IMAGE} ]; then + echo "*** ERROR ***: Can't find image (*.tgz or tar.gz) file: Can't continue! Abort!" + exit 1 + fi + gunzip -c ${TGZ_DOCKER_IMAGE} | docker load + sudo docker images | grep ${TGZ_DOCKER_IMAGE%.tgz} +} + +echo "---------------------------------------------" +echo "---- SAVE a Container for ${imageTag}" +echo "---------------------------------------------" + +restore + + diff --git a/run.sh b/run.sh old mode 100644 new mode 100755 index a6e6758..0cbfc6c --- a/run.sh +++ b/run.sh @@ -1,9 +1,141 @@ -#!/bin/bash +#!/bin/bash + +set +e + +MY_DIR=$(dirname "$(readlink -f "$0")") if [ $# -lt 1 ]; then + echo "--------------------------------------------------------" echo "Usage: " - echo " ${0} " + echo " ${0} " + echo "e.g.: " + echo " ${0} ls -al " + echo " ${0} /bin/bash " + echo "--------------------------------------------------------" +fi + +########################################################################### +#### ---- RUN Configuration (CHANGE THESE if needed!!!!) --- #### +########################################################################### +## ------------------------------------------------------------------------ +## Valid "BUILD_TYPE" values: +## 0: (default) has neither X11 nor VNC/noVNC container build image type +## 1: X11/Desktip container build image type +## 2: VNC/noVNC container build image type +## ------------------------------------------------------------------------ +BUILD_TYPE=0 + +## ------------------------------------------------------------------------ +## Valid "RUN_TYPE" values: +## 0: (default) Interactive Container - +## ==> Best for Debugging Use +## 1: Detach Container / Non-Interactive +## ==> Usually, when not in debugging mode anymore, then use 1 as choice. +## ==> Or, your frequent needs of the container for DEV environment Use. +## ------------------------------------------------------------------------ +if [ "$1" = "-d" ]; then + RUN_TYPE=1 + shift 1 +fi +RUN_TYPE=${RUN_TYPE:-0} + +## ------------------------------------------------------------------------ +## Change to one (1) if run.sh needs to use host's user/group to run the Container +## Valid "USER_VARS_NEEDED" values: +## 0: (default) Not using host's USER / GROUP ID +## 1: Yes, using host's USER / GROUP ID for Container running. +## ------------------------------------------------------------------------ +USER_VARS_NEEDED=0 + +## ------------------------------------------------------------------------ +## Valid "RESTART_OPTION" values: +## { no, on-failure, unless-stopped, always } +## ------------------------------------------------------------------------ +if [ "$1" = "-a" ] && [ "${RUN_TYPE}" = "1" ] ; then + RESTART_OPTION=always + shift 1 fi +RESTART_OPTION=${RESTART_OPTION:-no} + +## ------------------------------------------------------------------------ +## More optional values: +## Add any additional options here +## ------------------------------------------------------------------------ +# MORE_OPTIONS="--privileged=true" +MORE_OPTIONS= + +############################################################################### +############################################################################### +############################################################################### +#### ---- DO NOT Change the code below UNLESS you really want to !!!!) --- #### +#### ---- DO NOT Change the code below UNLESS you really want to !!!!) --- #### +#### ---- DO NOT Change the code below UNLESS you really want to !!!!) --- #### +############################################################################### +############################################################################### +############################################################################### + +######################################## +#### ---- Correctness Checking ---- #### +######################################## +RESTART_OPTION=`echo ${RESTART_OPTION} | sed 's/ //g' | tr '[:upper:]' '[:lower:]' ` +REMOVE_OPTION=" --rm " +if [ "${RESTART_OPTION}" != "no" ]; then + REMOVE_OPTION="" +fi + +######################################## +#### ---- Usage for BUILD_TYPE ---- #### +######################################## +function buildTypeUsage() { + echo "## ------------------------------------------------------------------------" + echo "## Valid BUILD_TYPE values: " + echo "## 0: (default) has neither X11 nor VNC/noVNC container build image type" + echo "## 1: X11/Desktip container build image type" + echo "## 2: VNC/noVNC container build image type" + echo "## ------------------------------------------------------------------------" +} + +if [ "${BUILD_TYPE}" -lt 0 ] || [ "${BUILD_TYPE}" -gt 2 ]; then + buildTypeUsage + exit 1 +fi + +######################################## +#### ---- Validate RUN_TYPE ---- #### +######################################## + +RUN_OPTION=${RUN_OPTION:-" -it "} +function validateRunType() { + case "${RUN_TYPE}" in + 0 ) + RUN_OPTION=" -it " + ;; + 1 ) + RUN_OPTION=" -d " + ;; + * ) + echo "**** ERROR: Incorrect RUN_TYPE: ${RUN_TYPE} is used! Abort ****" + exit 1 + ;; + esac +} +validateRunType +echo "RUN_TYPE=${RUN_TYPE}" +echo "RUN_OPTION=${RUN_OPTION}" +echo "RESTART_OPTION=${RESTART_OPTION}" +echo "REMOVE_OPTION=${REMOVE_OPTION}" + +########################################################################### +## -- docker-compose or docker-stack use only -- +########################################################################### + +## -- (this script will include ./.env only if "./docker-run.env" not found +DOCKER_ENV_FILE="./docker-run.env" + +########################################################################### +#### (Optional - to filter Environmental Variables for Running Docker) +########################################################################### +ENV_VARIABLE_PATTERN="" ################################################### #### ---- Change this only to use your own ---- @@ -14,9 +146,12 @@ baseDataFolder="$HOME/data-docker" ################################################### #### **** Container package information **** ################################################### +#### (The following only good for Linux - not for Mac) +MY_IP=` hostname -I|awk '{print $1}'` MY_IP=`ip route get 1|awk '{print$NF;exit;}'` + DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` -imageTag=${1:-"${ORGANIZATION}/${DOCKER_IMAGE_REPO}"} +imageTag="${ORGANIZATION}/${DOCKER_IMAGE_REPO}" #PACKAGE=`echo ${imageTag##*/}|tr "/\-: " "_"` PACKAGE="${imageTag##*/}" @@ -58,35 +193,145 @@ LOCAL_VOLUME_DIR="${baseDataFolder}/${PACKAGE}" ## -- Container's internal Volume base DIR DOCKER_VOLUME_DIR="/home/developer" +################################################### +#### ---- Detect Docker Run Env files ---- +################################################### + +function detectDockerRunEnvFile() { + curr_dir=`pwd` + if [ -s "${DOCKER_ENV_FILE}" ]; then + echo "--- INFO: Docker Run Environment file '${DOCKER_ENV_FILE}' FOUND!" + else + echo "*** WARNING: Docker Run Environment file '${DOCKER_ENV_FILE}' NOT found!" + echo "*** WARNING: Searching for .env or docker.env as alternative!" + echo "*** --->" + if [ -s "./docker-run.env" ]; then + echo "--- INFO: ./docker-run.env FOUND to use as Docker Run Environment file!" + DOCKER_ENV_FILE="./docker-run.env" + else + if [ -s "./.env" ]; then + echo "--- INFO: ./.env FOUND to use as Docker Run Environment file!" + DOCKER_ENV_FILE="./.env" + else + echo "--- INFO: ./.env Docker Environment file (.env) NOT found!" + if [ -s "./docker.env" ]; then + echo "--- INFO: ./docker.env FOUND to use as Docker Run Environment file!" + DOCKER_ENV_FILE="./docker.env" + else + echo "*** WARNING: Docker Environment file (.env) or (docker.env) NOT found!" + fi + fi + fi + fi +} +detectDockerRunEnvFile + ################################################### #### ---- Function: Generate volume mappings ---- #### (Don't change!) ################################################### VOLUME_MAP="" #### Input: VOLUMES - list of volumes to be mapped +hasPattern=0 +function hasPattern() { + detect=`echo $1|grep "$2"` + if [ "${detect}" != "" ]; then + hasPattern=1 + else + hasPattern=0 + fi +} + +DEBUG=0 +function debug() { + if [ $DEBUG -gt 0 ]; then + echo $* + fi +} + +function cutomizedVolume() { + DATA_VOLUME=$1 + if [ "`echo $DATA_VOLUME|grep 'volume-'`" != "" ]; then + docker_volume=`echo $DATA_VOLUME | cut -d'-' -f2 | cut -d':' -f1` + dest_volume=`echo $DATA_VOLUME | cut -d'-' -f2 | cut -d':' -f2` + source_volume=$(basename $imageTag)_${docker_volume} + sudo docker volume create ${source_volume} + + VOLUME_MAP="-v ${source_volume}:${dest_volume} ${VOLUME_MAP}" + else + echo "---- ${DATA_VOLUME} already is defined! Hence, ignore setup ${DATA_VOLUME} ..." + echo "---> VOLUME_MAP=${VOLUME_MAP}" + fi +} + function generateVolumeMapping() { if [ "$VOLUMES_LIST" == "" ]; then ## -- If locally defined in this file, then respect that first. ## -- Otherwise, go lookup the docker.env as ride-along source for volume definitions - VOLUMES_LIST=`cat docker.env|grep "^#VOLUMES_LIST= *"|sed "s/[#\"]//g"|cut -d'=' -f2-` + VOLUMES_LIST=`cat ${DOCKER_ENV_FILE}|grep "^#VOLUMES_LIST= *"|sed "s/[#\"]//g"|cut -d'=' -f2-` fi for vol in $VOLUMES_LIST; do - #echo "$vol" - hasColon=`echo $vol|grep ":"` - if [ ! "$hasColon" == "" ]; then - # asymetric mapping paths, like "/srv/docker/bind:/data" - VOLUME_MAP="${VOLUME_MAP} -v $vol" - else - if [[ $vol == "/"* ]]; then - echo "-- non-default /home/developer path; then use the full absolute path --" - VOLUME_MAP="${VOLUME_MAP} -v ${LOCAL_VOLUME_DIR}$vol:$vol" + debug "$vol" + hasColon=`echo $vol|grep ":"` + ## -- allowing change local volume directories -- + if [ "$hasColon" != "" ]; then + if [ "`echo $vol|grep 'volume-'`" != "" ]; then + cutomizedVolume $vol else - echo "-- default sub-directory (without prefix absolute path) --" - VOLUME_MAP="${VOLUME_MAP} -v ${LOCAL_VOLUME_DIR}/$vol:${DOCKER_VOLUME_DIR}/$vol" + left=`echo $vol|cut -d':' -f1` + right=`echo $vol|cut -d':' -f2` + leftHasDot=`echo $left|grep "\./"` + if [ "$leftHasDot" != "" ]; then + ## has "./data" on the left + if [[ ${right} == "/"* ]]; then + ## -- pattern like: "./data:/containerPath/data" + debug "-- pattern like ./data:/data --" + VOLUME_MAP="${VOLUME_MAP} -v `pwd`/${left}:${right}" + else + ## -- pattern like: "./data:data" + debug "-- pattern like ./data:data --" + VOLUME_MAP="${VOLUME_MAP} -v `pwd`/${left}:${DOCKER_VOLUME_DIR}/${right}" + fi + mkdir -p `pwd`/${left} + if [ $DEBUG -gt 0 ]; then ls -al `pwd`/${left}; fi + else + ## No "./data" on the left + if [ "$leftHasAbsPath" != "" ]; then + ## Has pattern like "/data" on the left + if [[ ${right} == "/"* ]]; then + ## -- pattern like: "/data:/containerPath/data" + debug "-- pattern like /data:/containerPath/data --" + VOLUME_MAP="${VOLUME_MAP} -v ${left}:${right}" + else + ## -- pattern like: "/data:data" + debug "-- pattern like /data:data --" + VOLUME_MAP="${VOLUME_MAP} -v ${left}:${DOCKER_VOLUME_DIR}/${right}" + fi + mkdir -p ${LOCAL_VOLUME_DIR}/${left} + if [ $DEBUG -gt 0 ]; then ls -al ${LOCAL_VOLUME_DIR}/${left}; fi + else + ## No pattern like "/data" on the left + if [[ ${right} == "/"* ]]; then + ## -- pattern like: "data:/containerPath/data" + debug "-- pattern like ./data:/data --" + VOLUME_MAP="${VOLUME_MAP} -v ${LOCAL_VOLUME_DIR}/${left}:${right}" + else + ## -- pattern like: "data:data" + debug "-- pattern like data:data --" + VOLUME_MAP="${VOLUME_MAP} -v ${LOCAL_VOLUME_DIR}/${left}:${DOCKER_VOLUME_DIR}/${right}" + fi + mkdir -p ${LOCAL_VOLUME_DIR}/${left} + if [ $DEBUG -gt 0 ]; then ls -al ${LOCAL_VOLUME_DIR}/${left}; fi + fi + fi fi + else + ## -- pattern like: "data" + debug "-- default sub-directory (without prefix absolute path) --" + VOLUME_MAP="${VOLUME_MAP} -v ${LOCAL_VOLUME_DIR}/$vol:${DOCKER_VOLUME_DIR}/$vol" + mkdir -p ${LOCAL_VOLUME_DIR}/$vol + if [ $DEBUG -gt 0 ]; then ls -al ${LOCAL_VOLUME_DIR}/$vol; fi fi - mkdir -p ${LOCAL_VOLUME_DIR}/$vol - ls -al ${LOCAL_VOLUME_DIR}/$vol done } #### ---- Generate Volumes Mapping ---- @@ -101,8 +346,8 @@ PORT_MAP="" function generatePortMapping() { if [ "$PORTS" == "" ]; then ## -- If locally defined in this file, then respect that first. - ## -- Otherwise, go lookup the docker.env as ride-along source for volume definitions - PORTS_LIST=`cat docker.env|grep "^#PORTS_LIST= *"|sed "s/[#\"]//g"|cut -d'=' -f2-` + ## -- Otherwise, go lookup the ${DOCKER_ENV_FILE} as ride-along source for volume definitions + PORTS_LIST=`cat ${DOCKER_ENV_FILE}|grep "^#PORTS_LIST= *"|sed "s/[#\"]//g"|cut -d'=' -f2-` fi for pp in ${PORTS_LIST}; do #echo "$pp" @@ -117,7 +362,115 @@ function generatePortMapping() { } #### ---- Generate Port Mapping ---- generatePortMapping -echo ${PORT_MAP} +echo "PORT_MAP=${PORT_MAP}" + +################################################### +#### ---- Generate Environment Variables ---- +################################################### +ENV_VARS="" +function generateEnvVars_v2() { + while read line; do + echo "Line=$line" + key=${line%=*} + value=${line#*=} + key=$(eval echo $value) + ENV_VARS="${ENV_VARS} -e ${line%=*}=$(eval echo $value)" + done < <(grep -E "^[[:blank:]]*$1.+[[:blank:]]*=[[:blank:]]*.+[[:blank:]]*" ${DOCKER_ENV_FILE} | grep -v "^#") + echo "ENV_VARS=$ENV_VARS" +} +generateEnvVars_v2 +echo ">> ENV_VARS=$ENV_VARS" + +function generateEnvVars() { + if [ "${1}" != "" ]; then + ## -- product key patterns, e.g., "^MYSQL_*" + #productEnvVars=`grep -E "^[[:blank:]]*$1[a-zA-Z0-9_]+[[:blank:]]*=[[:blank:]]*[a-zA-Z0-9_]+[[:blank:]]*" ${DOCKER_ENV_FILE}` + productEnvVars=`grep -E "^[[:blank:]]*$1.+[[:blank:]]*=[[:blank:]]*.+[[:blank:]]*" ${DOCKER_ENV_FILE} | grep -v "^#" | grep "${1}"` + else + ## -- product key patterns, e.g., "^MYSQL_*" + #productEnvVars=`grep -E "^[[:blank:]]*$1[a-zA-Z0-9_]+[[:blank:]]*=[[:blank:]]*[a-zA-Z0-9_]+[[:blank:]]*" ${DOCKER_ENV_FILE}` + productEnvVars=`grep -E "^[[:blank:]]*$1.+[[:blank:]]*=[[:blank:]]*.+[[:blank:]]*" ${DOCKER_ENV_FILE} | grep -v "^#"` + fi + for vars in + do + echo "Line=$line" + key=${line%=*} + value=${line#*=} + #key=$(eval echo $value) + #ENV_VARS="${ENV_VARS} -e ${line%=*}=$(eval echo $value)" + ENV_VARS="${ENV_VARS} -e ${line}" + done + ENV_VARS_STRING="" + for vars in ${productEnvVars// /}; do + debug "Entry => $vars" + key=${vars%=*} + value=${vars#*=} + if [ "$1" != "" ]; then + matched=`echo $vars|grep -E "${1}"` + if [ ! "$matched" == "" ]; then + ENV_VARS="${ENV_VARS} -e $key=$(eval echo $value)" + #ENV_VARS="${ENV_VARS} ${vars}" + fi + else + #ENV_VARS="${ENV_VARS} ${vars}" + ENV_VARS="${ENV_VARS} -e $key=$(eval echo $value)" + fi + done +# ## IFS default is "space tab newline" already +# #IFS=',; ' read -r -a ENV_VARS_ARRAY <<< "${ENV_VARS_STRING}" +# read -r -a ENV_VARS_ARRAY <<< "${ENV_VARS_STRING}" +# # To iterate over the elements: +# for element in "${ENV_VARS_ARRAY[@]}" +# do +# ENV_VARS="${ENV_VARS} -e ${element}" +# done +# if [ $DEBUG -gt 0 ]; then echo "ENV_VARS_ARRAY=${ENV_VARS_ARRAY[@]}"; fi +} +#generateEnvVars +#echo "ENV_VARS=${ENV_VARS}" + +################################################### +#### ---- Setup Docker Build Proxy ---- +################################################### +# export NO_PROXY="localhost,127.0.0.1,.openkbs.org" +# export HTTP_PROXY="http://gatekeeper-w.openkbs.org:80" +# when using "wget", add "--no-check-certificate" to avoid https certificate checking failures +# Note: You can also setup Docker CLI configuration file (~/.docker/config.json), e.g. +# { +# "proxies": { +# "default": { +# "httpProxy": "http://gatekeeper-w.openkbs.org:80" +# "httpsProxy": "http://gatekeeper-w.openkbs.org:80" +# } +# } +# } +# +echo "... Setup Docker Run Proxy: ..." + +PROXY_PARAM= +function generateProxyEnv() { + if [ "${HTTP_PROXY}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} -e HTTP_PROXY=${HTTP_PROXY}" + fi + if [ "${HTTPS_PROXY}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} -e HTTPS_PROXY=${HTTPS_PROXY}" + fi + if [ "${NO_PROXY}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} -e NO_PROXY=\"${NO_PROXY}\"" + fi + if [ "${http_proxy}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} -e http_proxy=${http_proxy}" + fi + if [ "${https_proxy}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} -e https_proxy=${https_proxy}" + fi + if [ "${no_proxy}" != "" ]; then + PROXY_PARAM="${PROXY_PARAM} -e no_proxy=\"${no_proxy}\"" + fi + ENV_VARS="${ENV_VARS} ${PROXY_PARAM}" +} +generateProxyEnv +echo "ENV_VARS=${ENV_VARS}" ################################################### #### ---- Function: Generate privilege String ---- @@ -140,39 +493,233 @@ echo ${privilegedString} ################################################### #### ---- Mostly, you don't need change below ---- ################################################### +function cleanup() { + containerID=`sudo docker ps -a|grep "${instanceName}" | awk '{print $1}'` + # if [ ! "`sudo docker ps -a|grep ${instanceName}`" == "" ]; then + if [ "${containerID}" != "" ]; then + sudo docker rm -f ${containerID} + fi +} + +function displayURL() { + port=${1} + echo "... Go to: http://${MY_IP}:${port}" + #firefox http://${MY_IP}:${port} & + if [ "`which google-chrome`" != "" ]; then + /usr/bin/google-chrome http://${MY_IP}:${port} & + else + firefox http://${MY_IP}:${port} & + fi +} + +################################################### +#### ---- Replace "Key=Value" with new value ---- +################################################### +function replaceKeyValue() { + inFile=${1:-${DOCKER_ENV_FILE}} + keyLike=$2 + newValue=$3 + if [ "$2" == "" ]; then + echo "**** ERROR: Empty Key value! Abort!" + exit 1 + fi + sed -i -E 's/^('$keyLike'[[:blank:]]*=[[:blank:]]*).*/\1'$newValue'/' $inFile +} +#### ---- Replace docker.env with local user's UID and GID ---- +#replaceKeyValue ${DOCKER_ENV_FILE} "USER_ID" "$(id -u $USER)" +#replaceKeyValue ${DOCKER_ENV_FILE} "GROUP_ID" "$(id -g $USER)" + +################################################### +#### ---- Get "Key=Value" withe new value ---- +#### Usage: getKeyValuePair +#### Output: Key=Value +################################################### +KeyValuePair="" +function getKeyValuePair() { + KeyValuePair="" + inFile=${1:-${DOCKER_ENV_FILE}} + keyLike=$2 + if [ "$2" == "" ]; then + echo "**** ERROR: Empty Key value! Abort!" + exit 1 + fi + matchedKV=`grep -E "^[[:blank:]]*${keyLike}.+[[:blank:]]*=[[:blank:]]*.+[[:blank:]]*" ${DOCKER_ENV_FILE}` + for kv in $matchedKV; do + echo "KeyValuePair=${matchedKV// /}" + done +} +#getKeyValuePair "${DOCKER_ENV_FILE}" "MYSQL_DATABASE" -#instanceName=my-${1:-${imageTag%/*}}_$RANDOM -#instanceName=my-${1:-${imageTag##*/}} ## -- transform '-' and space to '_' #instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/\-: " "_"` instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/: " "_"` +################################################ +##### ---- Product Specific Parameters ---- #### +################################################ +#MYSQL_DATABASE=${MYSQL_DATABASE:-myDB} +#MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-password} +#MYSQL_USER=${MYSQL_USER:-user1} +#MYSQL_PASSWORD=${MYSQL_PASSWORD:-password} +#### ---- Generate Env. Variables ---- +echo ${ENV_VARS} + echo "---------------------------------------------" echo "---- Starting a Container for ${imageTag}" echo "---------------------------------------------" -docker rm -f ${instanceName} - -docker run -it \ - --name=${instanceName} \ - ${privilegedString} \ - ${VOLUME_MAP} \ - ${PORT_MAP} \ - ${imageTag} - -docker rm -f ${instanceName} - -#echo ${DISPLAY} -#xhost +SI:localuser:$(id -un) -#docker run -it \ -# --name=${instanceName} \ -# --restart=always \ -# ${privilegedString} \ -# --user=$(id -u):$(id -g) \ -# -e DISPLAY=$DISPLAY \ -# -v /tmp/.X11-unix:/tmp/.X11-unix \ -# ${VOLUME_MAP} \ -# ${PORT_MAP} \ -# ${imageTag} +cleanup + +################################# +## -- USER_VARS into Docker -- ## +################################# +if [ ${USER_VARS_NEEDED} -gt 0 ]; then + USER_VARS="--user $(id -u $USER)" +fi + +echo "--------------------------------------------------------" +echo "==> Commands to manage Container:" +echo "--------------------------------------------------------" +echo " ./shell.sh : to shell into the container" +echo " ./stop.sh : to stop the container" +echo " ./log.sh : to show the docker run log" +echo " ./build.sh : to build the container" +echo " ./commit.sh: to push the container image to docker hub" +echo "--------------------------------------------------------" + +################################# +## ---- Setup X11 Display -_-- ## +################################# +X11_OPTION= +function setupDisplayType() { + if [[ "$OSTYPE" == "linux-gnu" ]]; then + # ... + xhost +SI:localuser:$(id -un) + xhost + 127.0.0.1 + echo ${DISPLAY} + elif [[ "$OSTYPE" == "darwin"* ]]; then + # Mac OSX + xhost + 127.0.0.1 + export DISPLAY=host.docker.internal:0 + echo ${DISPLAY} + elif [[ "$OSTYPE" == "cygwin" ]]; then + # POSIX compatibility layer and Linux environment emulation for Windows + xhost + 127.0.0.1 + echo ${DISPLAY} + elif [[ "$OSTYPE" == "msys" ]]; then + # Lightweight shell and GNU utilities compiled for Windows (part of MinGW) + xhost + 127.0.0.1 + echo ${DISPLAY} + elif [[ "$OSTYPE" == "freebsd"* ]]; then + # ... + xhost + 127.0.0.1 + echo ${DISPLAY} + else + # Unknown. + echo "Unknown OS TYPE: $OSTYPE! Not supported!" + exit 9 + fi + echo "DISPLAY=${DISPLAY}" + echo +} + + +################################################## +## ---- Setup Corporate Chain's Certificates -- ## +################################################## +CERTIFICATE_OPTIONS= +function setupCorporateCertificates() { + cert_dir=`pwd`/certificates + if [ -d ./certificates/ ]; then + CERTIFICATE_OPTIONS="${CERTIFICATE_OPTIONS} -v ${cert_dir}:/certificates" + fi + echo "CERTIFICATE_OPTIONS=${CERTIFICATE_OPTIONS}" +} +setupCorporateCertificates + + +################################################## +## ---- Setup accessing HOST's /etc/hosts: ---- ## +################################################## +## **************** WARNING: ********************* +## **************** WARNING: ********************* +## **************** WARNING: ********************* +# => this might open up more attack surface since +# /etc/hosts has other nodes IP/name information +# ------------------------------------------------ +HOSTS_OPTIONS="-v /etc/hosts:/etc/hosts" + + +################################################## +################################################## +## ----------------- main --------------------- ## +################################################## +################################################## + +case "${BUILD_TYPE}" in + 0) + #### 0: (default) has neither X11 nor VNC/noVNC container build image type + #### ---- for headless-based / GUI-less ---- #### + MORE_OPTIONS="${MORE_OPTIONS} ${HOSTS_OPTIONS} " + sudo docker run \ + --name=${instanceName} \ + --restart=${RESTART_OPTION} \ + ${REMOVE_OPTION} ${RUN_OPTION} ${MORE_OPTIONS} ${CERTIFICATE_OPTIONS} \ + ${privilegedString} \ + ${USER_VARS} \ + ${ENV_VARS} \ + ${VOLUME_MAP} \ + ${PORT_MAP} \ + ${imageTag} \ + $* + ;; + 1) + #### 1: X11/Desktip container build image type + #### ---- for X11-based ---- #### + setupDisplayType + echo ${DISPLAY} + #X11_OPTION="-e DISPLAY=$DISPLAY -v $HOME/.chrome:/data -v /dev/shm:/dev/shm -v /tmp/.X11-unix:/tmp/.X11-unix -e DBUS_SYSTEM_BUS_ADDRESS=unix:path=/host/run/dbus/system_bus_socket" + X11_OPTION="-e DISPLAY=$DISPLAY -v /dev/shm:/dev/shm -v /tmp/.X11-unix:/tmp/.X11-unix " + echo "X11_OPTION=${X11_OPTION}" + MORE_OPTIONS="${MORE_OPTIONS} ${HOSTS_OPTIONS} " + sudo docker run \ + --name=${instanceName} \ + --restart=${RESTART_OPTION} \ + --network host \ + ${REMOVE_OPTION} ${RUN_OPTION} ${MORE_OPTIONS} ${CERTIFICATE_OPTIONS} \ + ${X11_OPTION} ${MEDIA_OPTIONS} \ + ${privilegedString} \ + ${USER_VARS} \ + ${ENV_VARS} \ + ${VOLUME_MAP} \ + ${PORT_MAP} \ + ${imageTag} \ + $* + ;; + 2) + #### 2: VNC/noVNC container build image type + #### ----------------------------------- #### + #### -- VNC_RESOLUTION setup default --- #### + #### ----------------------------------- #### + if [ "`echo $ENV_VARS|grep VNC_RESOLUTION`" = "" ]; then + #VNC_RESOLUTION=1280x1024 + VNC_RESOLUTION=1920x1080 + ENV_VARS="${ENV_VARS} -e VNC_RESOLUTION=${VNC_RESOLUTION}" + fi + MORE_OPTIONS="${MORE_OPTIONS} ${HOSTS_OPTIONS} " + sudo docker run \ + --name=${instanceName} \ + --restart=${RESTART_OPTION} \ + ${REMOVE_OPTION} ${RUN_OPTION} ${MORE_OPTIONS} ${CERTIFICATE_OPTIONS} \ + ${privilegedString} \ + ${USER_VARS} \ + ${ENV_VARS} \ + ${VOLUME_MAP} \ + ${PORT_MAP} \ + ${imageTag} \ + $* + ;; + +esac diff --git a/save.sh b/save.sh new file mode 100755 index 0000000..3d219d8 --- /dev/null +++ b/save.sh @@ -0,0 +1,55 @@ +#!/bin/bash -x + +MY_DIR=$(dirname "$(readlink -f "$0")") + +if [ $# -lt 1 ]; then + echo "Usage: " + echo " ${0} " + echo "e.g.: " + echo " ${0} " +fi + +################################################### +#### ---- Change this only to use your own ---- +################################################### +ORGANIZATION=openkbs + +################################################### +#### **** Container package information **** +################################################### +DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` +imageTag="${ORGANIZATION}/${DOCKER_IMAGE_REPO}" +imageTag=${1:-$imageTag} + +IMAGE_NAME=${imageTag%:*} +IMAGE_VERSION=${imageTag#*:} +if [ "$IMAGE_VERSION" = "" ]; then + # -- patch in the image version + imageTag="${IMAGE_NAME}:latest" +fi +################################################### +#### ---- Mostly, you don't need change below ---- +################################################### +## -- transform '-' and space to '_' +#instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/\-: " "_"` +instanceName=`echo $(basename ${imageTag})|tr '[:upper:]' '[:lower:]'|tr "/: " "_"` + +TGZ_DOCKER_IMAGE=${instanceName}.tgz + +function save() { + lookupImage=`sudo docker images ${imageTag} | grep ${imageTag} | awk '{print $1}'` + if [ "$lookupImage" = "" ]; then + echo "*** ERROR ***: Can't find Docker image (${imageTag}): Can't continue! Abort!" + exit 1 + fi + sudo docker save ${imageTag} | gzip > ${TGZ_DOCKER_IMAGE} + ls -al ${TGZ_DOCKER_IMAGE} +} + +echo "---------------------------------------------" +echo "---- SAVE a Container for ${imageTag}" +echo "---------------------------------------------" + +save + + diff --git a/scripts/add-user-no-login.sh b/scripts/add-user-no-login.sh new file mode 100644 index 0000000..9a194a8 --- /dev/null +++ b/scripts/add-user-no-login.sh @@ -0,0 +1,27 @@ +#!/bin/bash -x + +############################################################# +#### ---- user: no login (e.g. ftp, or application) ---- #### +############################################################# +# ref: https://www.cyberciti.biz/tips/bash-shell-parameter-substitution-2.html + +# Usage : Add a no-login user +USER_NAME=${1:-developer} +USER_PASSWORD="${2:-ChangeMeNow!}" + +# die if username/password not provided +[ $# -ne 2 ] && { echo "Usage: "; exit 1; } + +# Get username length and make sure it always <= 8 +[[ ${#USER_NAME} -ge 9 ]] && { echo "Error: Username should be maximum 8 characters in length. "; exit 2; } + +# Check for existing user in /etc/passwd +/usr/bin/getent passwd "${USER_NAME}" &>/dev/null + +# Check exit status +[ $? -eq 0 ] && { echo "Error: username \"${USER_NAME}\" exists."; exit 3; } + +# Add user +sudo useradd ${USER_NAME} -s /usr/sbin/nologin -m -p $(echo ${USER_PASSWD} | openssl passwd -1 -stdin) + + diff --git a/scripts/add-user-sudo.sh b/scripts/add-user-sudo.sh new file mode 100644 index 0000000..4fd530d --- /dev/null +++ b/scripts/add-user-sudo.sh @@ -0,0 +1,31 @@ +#!/bin/bash -x + +################################## +## ---- user: developer ---- +################################## + +if [ $# -lt 1 ]; then + echo "**** Usage: $(basename $0 [,user_password>]) " + echo "**** ERROR: Need at lease as argument. !" + echo "Abort!" + echo + exit 1; +fi + +USER_NAME=${1:-developer} +USER_PASSWORD=${2} + +HOME=/home/${USER_NAME} + +if [ "$USER_PASSWORD" != "" ]; then + sudo groupadd ${USER_NAME} + sudo useradd ${USER_NAME} -m -d ${HOME} -s /bin/bash -g ${USER_NAME} -p $(echo ${USER_PASSWORD} | openssl passwd -1 -stdin) +else + sudo groupadd ${USER_NAME} && sudo useradd ${USER_NAME} -m -d ${HOME} -s /bin/bash -g ${USER_NAME} +fi + +exit 1 + +#echo "${USER_NAME} ALL=NOPASSWD:ALL" | sudo tee -a /etc/sudoers +sudo usermod -aG sudo ${USER_NAME} + diff --git a/scripts/add-user.sh b/scripts/add-user.sh new file mode 100644 index 0000000..3babd21 --- /dev/null +++ b/scripts/add-user.sh @@ -0,0 +1,11 @@ +#!/bin/bash -x + +################################## +## ---- user: developer ---- +################################## + +USER=${1:-developer} +HOME=/home/${USER} + +sudo groupadd ${USER} && sudo useradd ${USER} -m -d ${HOME} -s /bin/bash -g ${USER} + diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh new file mode 100644 index 0000000..1ede1f7 --- /dev/null +++ b/scripts/docker-entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e + +env + +#### ---- Make sure to provide Non-root user for launching Docker ---- +#### ---- Default, we use base images's "developer" ---- +NON_ROOT_USER=${NON_ROOT_USER:-"developer"} + + +#### ----------------------------------------------------------------------- +#### 2.A) As Root User -- Choose this or 2.B --#### +#### ---- Use this when running Root user ---- #### +/bin/bash -c "$@" + +#### 2.B) As Non-Root User -- Choose this or 2.A ---- #### +#### ---- Use this when running Non-Root user ---- #### +#### ---- Use gosu (or su-exec) to drop to a non-root user +#exec gosu ${NON_ROOT_USER} "$@" +#### ----------------------------------------------------------------------- + +tail -f /dev/null diff --git a/scripts/install-npm-packages.sh b/scripts/install-npm-packages.sh new file mode 100644 index 0000000..64e3f94 --- /dev/null +++ b/scripts/install-npm-packages.sh @@ -0,0 +1,13 @@ +#!/bin/bash -x + +echo "####################### Components: $(basename $0) ###########################" + +whoami + +id + +env + +for pkg in `cat ${SCRIPT_DIR}/requirements-npm.txt | grep -v '^#'`; do + npm install $pkg +done diff --git a/scripts/new-virtualenv.sh b/scripts/new-virtualenv.sh new file mode 100644 index 0000000..95ad713 --- /dev/null +++ b/scripts/new-virtualenv.sh @@ -0,0 +1,18 @@ +#!/bin/bash -x + +pip --no-cache-dir install --user --upgrade virtualenv +pip --no-cache-dir install --user --upgrade virtualenvwrapper +pip --no-cache-dir install --user https://github.com/pypa/virtualenv/tarball/master + +PROG_NAME=$(basename $0) +function usage() { + echo "$PROG_NAME " + echo "See https://virtualenvwrapper.readthedocs.io/en/latest/ for more information & guide" +} +usage + +PROJ_DIR=${1:-"`pwd`/env`date +%Y-%m-%d`"} +export WORKON_HOME=~/Envs +mkdir -p $WORKON_HOME +source /usr/local/bin/virtualenvwrapper.sh +mkvirtualenv ${PROJ_DIR} diff --git a/scripts/printVersions.sh b/scripts/printVersions.sh new file mode 100644 index 0000000..8ec7f17 --- /dev/null +++ b/scripts/printVersions.sh @@ -0,0 +1,16 @@ +#!/bin/bash -x + +echo "JAVA_HOME=$JAVA_HOME" +whereis java +echo +java -version +mvn --version +python -V +python3 -V +pip --version +pip3 --version +gradle --version +npm -v +node -v +cat /etc/*-release + diff --git a/scripts/requirements-npm.txt b/scripts/requirements-npm.txt new file mode 100644 index 0000000..559853d --- /dev/null +++ b/scripts/requirements-npm.txt @@ -0,0 +1,5 @@ +json-server +## websocket server +ws +## websocket client +wscat diff --git a/scripts/setup_npm_proxy.sh b/scripts/setup_npm_proxy.sh new file mode 100644 index 0000000..c9deb6d --- /dev/null +++ b/scripts/setup_npm_proxy.sh @@ -0,0 +1,83 @@ +#!/bin/bash -x + +echo "####################### Components: $(basename $0) ###########################" + +#### ------------------------------------------------- +#### OS_TYPE=1:Ubuntu, 2:Centos,, 0: OS_TYPE_NOT_FOUND +#### ------------------------------------------------- +OS_TYPE=0 + +REPO_CONF=/etc/apt/apt.conf +ETC_ENV=/etc/environment + +function detectOS_alt() { + os_name="`which yum`" + if [ "$os_name" = "" ]; then + os_name="`which apt`" + if [ "$os_name" = "" ]; then + OS_TYPE=0 + else + OS_TYPE=1 + fi + else + OS_TYPE=2 + fi + +} +detectOS_alt + +function detectOS() { + os_name="`cat /etc/os-release | grep -i '^NAME=\"Ubuntu\"' | awk -F= '{print $2}' | tr '[:upper:]' '[:lower:]' |sed 's/"//g' `" + case ${os_name} in + ubuntu*) + OS_TYPE=1 + REPO_CONF=/etc/apt/apt.conf + ETC_ENV=/etc/environment + ;; + centos*) + OS_TYPE=2 + REPO_CONF=/etc/yum.conf + ETC_ENV=/etc/environment + ;; + *) + OS_TYPE=0 + REPO_CONF= + ETC_ENV= + echo "***** ERROR: Can't detect OS Type (e.g., Ubuntu, Centos)! *****" + echo "Abort now!" + exit 9 + ;; + esac +} + + +HAS_PROXY=0 +function detectProxySetup() { + proxy_vars="`env | grep -i proxy`" + if [ ! "${proxy_vars}" = "" ]; then + HAS_PROXY=1 + echo -e ">>>> $0: Found proxy environment vars setup found! \n Setup ${REPO_CONF} & ${ETC_ENV} for proxy servers URLs!" + else + echo -e ">>>> $0: No proxy vars setup found! \n No need to setup ${REPO_CONF} & ${ETC_ENV} for proxy servers URLs!" + exit 0 + fi +} +detectProxySetup + +#### ---- Remove extra dobule quote ---- #### +http_proxy=${http_proxy//\"/} +https_proxy=${https_proxy//\"/} +ftp_proxy=${ftp_proxy//\"/} + +function setupNpmProxy() { + echo "================= Setup NPM Proxy ====================" + if [ ${HAS_PROXY} -gt 0 ] && [ "`which npm`" != "" ]; then + npm config set proxy ${http_proxy} + npm config set http_proxy ${http_proxy} + npm config set https_proxy ${https_proxy} + npm config set no_proxy ${no_proxy} + fi +} +setupNpmProxy ${http_proxy} + + diff --git a/scripts/setup_system_certificates.sh b/scripts/setup_system_certificates.sh new file mode 100644 index 0000000..6a0908a --- /dev/null +++ b/scripts/setup_system_certificates.sh @@ -0,0 +1,213 @@ +#!/bin/bash + +echo "####################### Components: $(basename $0) ###########################" + +if [ "$1" != "" ]; then +    SOURCE_CERTIFICATES_DIR=${SOURCE_CERTIFICATES_DIR:-$1} +else +    SOURCE_CERTIFICATES_DIR=${SOURCE_CERTIFICATES_DIR:-/certificates} +fi + +#### ---------------------------------------------------------------------------------------------------------------------------------- #### +#### ---- (ref: https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself) +#### ---------------------------------------------------------------------------------------------------------------------------------- #### +function findMyAbsDir() { + SOURCE="${BASH_SOURCE[0]}" + while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located + done + MY_ABS_DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" +} +# findMyAbsDir +MY_ABS_DIR=$(dirname "$(readlink -f "$0")") + +#### ---- Usage ---- #### +function usage() { + echo "Usage setup_system_certificates -d [ -h | --help]" +} + +#### ---- Usage ---- #### +ORIG_ARGS="$*" +SHORT="hd:" +LONG="help,certificates_dir:" + +# $@ is all command line parameters passed to the script. +# -o is for short options like -v +# -l is for long options with double dash like --version +# the comma separates different long options +# -a is for long options with single dash like -version +#OPTIONS=$(getopt --options ${SHORT} --longoptions ${LONG} --name "$0" -a -- "$@") +OPTIONS=$(getopt -o ${SHORT} -l ${LONG} --name "$0" -a -- "$@") + +if [[ $? != 0 ]]; then + echo "Arguments Parsing Error! Abort!" + exit 1 +fi +eval set -- "${OPTIONS}" + +while true; do + case "$1" in + -h|--help) + usage "Usage setup_system_certificates -d [ -h | --help]" + ;; + -d|--certificates_dir) + shift + SOURCE_CERTIFICATES_DIR=$1 + echo "SOURCE_CERTIFICATES_DIR=$SOURCE_CERTIFICATES_DIR" + ;; + --) + shift + break + ;; + *) + echo "*****: input args error" + echo "$ORIG_ARGS" + exit 3 + ;; + esac + shift +done + +echo "ORIGINAL INPUT >>>>>>>>>>:" +echo "${ORIG_ARGS}" +echo "-------------" + +#### ------------------------------------------------- +#### OS_TYPE=1:Ubuntu, 2:Centos,, 0: OS_TYPE_NOT_FOUND +#### ------------------------------------------------- +OS_TYPE=0 + +REPO_CONF=/etc/apt/apt.conf +ETC_ENV=/etc/environment + +function detectOS_alt() { + os_name="`which yum`" + if [ "$os_name" = "" ]; then + os_name="`which apt`" + if [ "$os_name" = "" ]; then + OS_TYPE=0 + else + OS_TYPE=1 + fi + else + OS_TYPE=2 + fi + +} +detectOS_alt + +function detectOS() { + os_name="`cat /etc/os-release | grep -i '^NAME=\"Ubuntu\"' | awk -F= '{print $2}' | tr '[:upper:]' '[:lower:]' |sed 's/"//g' `" + case ${os_name} in + ubuntu*) + OS_TYPE=1 + REPO_CONF=/etc/apt/apt.conf + ETC_ENV=/etc/environment + ;; + centos*) + OS_TYPE=2 + REPO_CONF=/etc/yum.conf + ETC_ENV=/etc/environment + ;; + *) + OS_TYPE=0 + REPO_CONF= + ETC_ENV= + echo "***** ERROR: Can't detect OS Type (e.g., Ubuntu, Centos)! *****" + echo "Abort now!" + exit 9 + ;; + esac +} + +#### -------------------------------------------------------------------------------------------- +#### After these steps the new CA is known by system utilities like curl and get. +#### Unfortunately, this does not affect most web browsers like Mozilla Firefox or Google Chrome. +#### -------------------------------------------------------------------------------------------- +# CERTIFICATE_DIR=/usr/local/share/ca-certificates/extra +# mkdir -p ${CERTIFICATE_DIR} +# wget -O ${CERTIFICATE_DIR}/openkbs-BA-Root.crt http://pki.openkbs.org/openkbs-BA-Root.crt +# wget -O ${CERTIFICATE_DIR}/openkbs-BA-NPE-CA-3.crt http://pki.openkbs.org/openkbs-BA-NPE-CA-3.crt +# wget -O ${CERTIFICATE_DIR}/openkbs-BA-NPE-CA-4.crt http://pki.openkbs.org/openkbs-BA-NPE-CA-4.crt +# update-ca-certificates # (for Ubuntu OS) +# # update-ca-trust extract # (for CentOS OS) +#### (Unbunt version) +#TARGET_CERTIFICATES_DIR=/usr/local/share/ca-certificates/extra +if [ $OS_TYPE -eq 1 ]; then + # Ubuntu + CERT_COMMAND=`which update-ca-certificates` + CMD_OPT= + TARGET_CERTIFICATES_DIR=/usr/local/share/ca-certificates/extra +else + if [ $OS_TYPE -eq 1 ]; then + # CentOS + CERT_COMMAND=`which update-ca-trust` + TARGET_CERTIFICATES_DIR=/etc/pki/ca-trust/source/anchors + CMD_OPT=extract + else + echo "OS_TYPE Unknown! Can't do! Abort!" + exit 1 + fi +fi + +function setupSystemCertificates() { + echo "================= Setup System Certificates ====================" + sudo mkdir ${TARGET_CERTIFICATES_DIR} + for cert in `ls ${SOURCE_CERTIFICATES_DIR}/*`; do + if [[ "${cert}" == *"crt" ]] || [[ "${cert}" == *"pem" ]];then + #sudo cp root.cert.pem /usr/local/share/ca-certificates/extra/root.cert.crt + cert_basename=$(basename $cert) + sudo cp ${cert} ${TARGET_CERTIFICATES_DIR}/${cert_basename//pem/crt} + else + echo "... ignore non-certificate file: $cert" + fi + done + #sudo update-ca-certificates + sudo $CERT_COMMAND $CMD_OPT +} +setupSystemCertificates + +#### -------------------------------------------------------------------------------------------- +#### ---- Browsers (Firefox, Chromium, etc.) Root Certificates Setup +#### ---- (ref: https://thomas-leister.de/en/how-to-import-ca-root-certificate/) +#### -------------------------------------------------------------------------------------------- +function setupBrowserRootCertificates() { + ### Script installs root.cert.pem to certificate trust store of applications using NSS + ### (e.g. Firefox, Thunderbird, Chromium) + ### Mozilla uses cert8, Chromium and Chrome use cert9 + + ### + ### Requirement: apt install libnss3-tools + ### + + + ### + ### CA file to install (CUSTOMIZE!) + ### + + certfile="root.cert.pem" + certname="My Root CA" + + ### + ### For cert8 (legacy - DBM) + ### + + for certDB in $(find ~/ -name "cert8.db") + do + certdir=$(dirname ${certDB}); + certutil -A -n "${certname}" -t "TCu,Cu,Tu" -i ${certfile} -d dbm:${certdir} + done + + ### + ### For cert9 (SQL) + ### + + for certDB in $(find ~/ -name "cert9.db") + do + certdir=$(dirname ${certDB}); + certutil -A -n "${certname}" -t "TCu,Cu,Tu" -i ${certfile} -d sql:${certdir} + done +} + diff --git a/scripts/setup_system_proxy.sh b/scripts/setup_system_proxy.sh new file mode 100644 index 0000000..5578e7b --- /dev/null +++ b/scripts/setup_system_proxy.sh @@ -0,0 +1,112 @@ +#!/bin/bash -x + +echo "####################### Components: $(basename $0) ###########################" + +#### ------------------------------------------------- +#### OS_TYPE=1:Ubuntu, 2:Centos,, 0: OS_TYPE_NOT_FOUND +#### ------------------------------------------------- +OS_TYPE=0 + +REPO_CONF=/etc/apt/apt.conf +ETC_ENV=/etc/environment + +function detectOS_alt() { + os_name="`which yum`" + if [ "$os_name" = "" ]; then + os_name="`which apt`" + if [ "$os_name" = "" ]; then + OS_TYPE=0 + else + OS_TYPE=1 + fi + else + OS_TYPE=2 + fi + +} +detectOS_alt + +function detectOS() { + os_name="`cat /etc/os-release | grep -i '^NAME=\"Ubuntu\"' | awk -F= '{print $2}' | tr '[:upper:]' '[:lower:]' |sed 's/"//g' `" + case ${os_name} in + ubuntu*) + OS_TYPE=1 + REPO_CONF=/etc/apt/apt.conf + ETC_ENV=/etc/environment + ;; + centos*) + OS_TYPE=2 + REPO_CONF=/etc/yum.conf + ETC_ENV=/etc/environment + ;; + *) + OS_TYPE=0 + REPO_CONF= + ETC_ENV= + echo "***** ERROR: Can't detect OS Type (e.g., Ubuntu, Centos)! *****" + echo "Abort now!" + exit 9 + ;; + esac +} + +HAS_PROXY=0 +function detectProxySetup() { + proxy_vars="`env | grep -i proxy`" + if [ ! "${proxy_vars}" = "" ]; then + HAS_PROXY=1 + echo -e ">>>> $0: Found proxy environment vars setup found! \n Setup ${REPO_CONF} & ${ETC_ENV} for proxy servers URLs!" + else + echo -e ">>>> $0: No proxy vars setup found! \n No need to setup ${REPO_CONF} & ${ETC_ENV} for proxy servers URLs!" + exit 0 + fi +} +detectProxySetup + +#### ---- Remove extra dobule quote ---- #### +http_proxy=${http_proxy//\"/} +https_proxy=${https_proxy//\"/} +ftp_proxy=${ftp_proxy//\"/} + +#### ---- Format (Ubuntu): /etc/apt/apt.conf Proxy Server URL ---- #### +# Acquire::http::Proxy "http://user:pass@proxy_host:port"; +# Other way: +# sudo apt-get -o Acquire::http::proxy=false +# sudo apt-get -o Acquire::http::proxy=http://proxy.openkbs.org:80/ +function addProxyToAptConf() { + echo "================= Setup apt/yum Proxy ====================" + proxy_already="`cat ${REPO_CONF}|grep -i proxy`" + if [ "${proxy_already}" = "" ] && [ ${HAS_PROXY} -gt 0 ]; then + [ ! -z "${http_proxy}" ] && echo "Acquire::http::Proxy \"${http_proxy}\";" | sudo tee -a ${REPO_CONF} + [ ! -z "${https_proxy}" ] && echo "Acquire::https::Proxy \"${https_proxy}\";" | sudo tee -a ${REPO_CONF} + [ ! -z "${ftp_proxy}" ] && echo "Acquire::ftp::Proxy \"${ftp_proxy}\";" | sudo tee -a ${REPO_CONF} + fi +} +addProxyToAptConf ${http_proxy} + +function addProxyToEtcEnv() { + echo "================= Setup System /etc/environment Proxy ====================" + etc_proxy_already="`cat ${ETC_ENV}|grep -i proxy`" + if [ "${etc_proxy_already}" = "" ] && [ ${HAS_PROXY} -gt 0 ]; then + no_proxy=${no_proxy//[[:blank:]]/} + no_proxy=${no_proxy//\"/} + [ ! -z "${http_proxy}" ] && echo "http_proxy=${http_proxy}" | sudo tee -a ${ETC_ENV} + [ ! -z "${https_proxy}" ] && echo "https_proxy=${https_proxy}" | sudo tee -a ${ETC_ENV} + [ ! -z "${ftp_proxy}" ] && echo "ftp_proxy=${ftp_proxy}" | sudo tee -a ${ETC_ENV} + [ ! -z "${no_proxy}" ] && echo "no_proxy=${no_proxy}" | sudo tee -a ${ETC_ENV} + fi +} +addProxyToEtcEnv + +function setupNpmProxy() { + echo "================= Setup NPM Proxy ====================" + if [ ${HAS_PROXY} -gt 0 ] && [ "`which npm`" != "" ]; then + npm config set proxy ${http_proxy} + npm config set http_proxy ${http_proxy} + npm config set https_proxy ${https_proxy} + npm config set no_proxy ${no_proxy} + fi +} +setupNpmProxy ${http_proxy} + + diff --git a/shell.sh b/shell.sh old mode 100644 new mode 100755 index b3c1bb4..5b69306 --- a/shell.sh +++ b/shell.sh @@ -29,5 +29,5 @@ echo "---------------------------------------------" echo "---- shell into the Container for ${imageTag}" echo "---------------------------------------------" -docker exec -it $(docker ps -a |grep ${instanceName} |awk '{print $1;}') /bin/bash +sudo docker exec -it $(sudo docker ps -a |grep ${instanceName} |awk '{print $1;}') /bin/bash diff --git a/specifications/package.json-ld b/specifications/package.json-ld new file mode 100644 index 0000000..69e8e2d --- /dev/null +++ b/specifications/package.json-ld @@ -0,0 +1,51 @@ + + + + diff --git a/specifications/package.json-ld.js b/specifications/package.json-ld.js new file mode 100644 index 0000000..69e8e2d --- /dev/null +++ b/specifications/package.json-ld.js @@ -0,0 +1,51 @@ + + + + diff --git a/specifications/schema.json-ld b/specifications/schema.json-ld new file mode 100644 index 0000000..6f61066 --- /dev/null +++ b/specifications/schema.json-ld @@ -0,0 +1,120 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/Welcome", + "definitions": { + "Welcome": { + "type": "object", + "additionalProperties": false, + "properties": { + "packages": { + "type": "array", + "items": { + "$ref": "#/definitions/Package" + } + } + }, + "required": [ + "packages" + ], + "title": "Welcome" + }, + "Package": { + "type": "object", + "additionalProperties": false, + "properties": { + "@context": { + "type": "string", + "format": "uri", + "qt-uri-protocols": [ + "https" + ], + "qt-uri-extensions": [ + ".jsonld" + ] + }, + "@type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "category": { + "type": "string" + }, + "operatingSystem": { + "type": "string" + }, + "version": { + "type": "string" + }, + "build": { + "type": "string" + }, + "dateReleased": { + "type": "string", + "format": "date-time" + }, + "packageFormat": { + "type": "string" + }, + "urlChecksum": { + "type": "string", + "format": "uri", + "qt-uri-protocols": [ + "https" + ], + "qt-uri-extensions": [ + ".html" + ] + }, + "checksum_md5": { + "type": "string" + }, + "checksum_sha256": { + "type": "string", + "format": "uri", + "qt-uri-protocols": [ + "https" + ], + "qt-uri-extensions": [ + ".html" + ] + }, + "urlPackageDownload": { + "type": "string", + "qt-uri-protocols": [ + "https" + ], + "qt-uri-extensions": [ + ".gz" + ] + }, + "urlReleaseLatest": { + "type": "string", + "qt-uri-protocols": [ + "https" + ] + }, + "packageName": { + "type": "string" + }, + "install_commands": { + "type": "string" + } + }, + "required": [ + "@context", + "@type", + "category", + "dateReleased", + "name", + "operatingSystem", + "packageFormat", + "urlPackageDownload", + "version" + ], + "title": "Package" + } + } +} + diff --git a/stop.sh b/stop.sh old mode 100644 new mode 100755 index c16246a..1e7ca42 --- a/stop.sh +++ b/stop.sh @@ -25,8 +25,10 @@ imageTag="${ORGANIZATION}/${DOCKER_IMAGE_REPO}" #### ---- Mostly, you don't need change below ---- ################################################### function cleanup() { - if [ ! "`docker ps -a|grep ${instanceName}`" == "" ]; then - echo "docker rm -f ${instanceName}" + containerID=`sudo docker ps -a|grep "${instanceName}" | awk '{print $1}'` + # if [ ! "`sudo docker ps -a|grep ${instanceName}`" == "" ]; then + if [ "${containerID}" != "" ]; then + sudo docker rm -f ${containerID} fi } diff --git a/tryJava.sh b/tryJava.sh old mode 100644 new mode 100755 index aa307ec..68b6fdf --- a/tryJava.sh +++ b/tryJava.sh @@ -1,4 +1,4 @@ -#!/bin/bash -x +#!/bin/bash ################################################### #### ---- Change this only if want to use your own @@ -11,6 +11,14 @@ ORGANIZATION=openkbs DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` imageTag=${1:-"${ORGANIZATION}/${DOCKER_IMAGE_REPO}"} +instanceName=some-jdk-mvn-py3 +function cleanup() { + if [ ! "`docker ps -a|grep ${instanceName}`" == "" ]; then + docker rm -f ${instanceName} + fi +} + +mkdir -p ./data mkdir -p ./data cat >./data/HelloWorld.java <<-EOF public class HelloWorld { @@ -20,13 +28,19 @@ public class HelloWorld { } EOF cat ./data/HelloWorld.java -djavac='docker run -it --rm --name jdk11-mvn-py3 -v '$PWD'/data:/data '${imageTag}' javac' -djava='docker run -it --rm --name jdk11-mvn-py3 -v '$PWD'/data:/data '${imageTag}' java' +djavac='docker run -it --rm -v '$PWD'/data:/data --workdir /data '${imageTag}' javac' +djava='docker run -it --rm -v '$PWD'/data:/data --workdir /data '${imageTag}' java' -#docker run -it --rm --name some-jdk11-mvn-py3 -v /home/user1/github/Java8-Maven-Python/jdk11-mvn-py3/data:/data openkbs/jdk11-mvn-py3 javac HelloWorld.java +echo +echo "----------------------------------------------------------------------" +echo "1.) Compile HelloWorld.java in Guest's workdir /data: " +echo "docker run -it --rm -v $PWD/data:/data --workdir /data openkbs/jdk-mvn-py3 javac HelloWorld.java" $djavac HelloWorld.java -#docker run -it --rm --name some-jdk11-mvn-py3 -v /home/user1/github/Java8-Maven-Python/jdk11-mvn-py3/data:/data openkbs/jdk11-mvn-py3 java HelloWorld +echo +echo "----------------------------------------------------------------------" +echo "2.) Run HelloWorld.class in Guest's workdir /data: " +echo "docker run -it --rm -v $PWD/data:/data --workdir /data openkbs/jdk-mvn-py3 java HelloWorld" $djava HelloWorld diff --git a/tryNodeJS.sh b/tryNodeJS.sh new file mode 100755 index 0000000..7e288fe --- /dev/null +++ b/tryNodeJS.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +SCRIPT_FILE=SimpleServer.js +HOST_PORT=3000 +SERVER_PORT=3000 + +################################################### +#### ---- Change this only if want to use your own +################################################### +ORGANIZATION=openkbs + +################################################### +#### ---- Container package information ---- +################################################### +DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` +imageTag=${1:-"${ORGANIZATION}/${DOCKER_IMAGE_REPO}"} + +################################################### +#### ---- Mostly, you don't need change below ---- +################################################### +instanceName=some-jdk-mvn-py3 +function cleanup() { + if [ ! "`docker ps -a|grep ${instanceName}`" == "" ]; then + docker rm -f ${instanceName} + fi +} +cleanup + +mkdir -p ./data + +# To run: nodejs ./SimpleServer.js +# Then => Open http://localhost:3000/ +# To see: Hello World! + +cat > ./data/${SCRIPT_FILE} <<'EOF' +const http = require('http'); + +const hostname = '0.0.0.0'; +const port = 3000; + +const server = http.createServer((req, res) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end('Hello World from openkbs/jdk-mvn-py3 with NodeJS supports! \n See https://github.com/DrSnowbird/jdk-mvn-py3 \n'); +}); + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); +EOF + +# cat ./data/${SCRIPT_FILE} + +TIMEOUT_SEC=20 + +echo +echo "----------------------------------------------------------------------" +echo "Starting JavaScript NodeJS mini-webserver:" +echo "You have $TIMEOUT_SEC to test this NodeJS mini-server." +echo "... then, it will destroy itself (like 007 Bond's movie - self-clean up :-) " +echo "----------------------------------------------------------------------" +echo "---> Open your browser to : http://127.0.0.1:${HOST_PORT}/" +echo "---> Or, command line:" +echo " curl http://localhost:${HOST_PORT}/" +echo " curl http://127.0.0.1:${HOST_PORT}/" +echo +docker run -d --rm --name ${instanceName} -v $PWD/data:/data -p ${HOST_PORT}:${SERVER_PORT} --workdir /data openkbs/jdk-mvn-py3 node /data/${SCRIPT_FILE} + +echo "---> Testing the mini-server by the ${SCRIPT_FILE} script:" +echo +echo "---> 1.) curl GET http://127.0.0.1:${HOST_PORT}/" +curl -s GET http://127.0.0.1:${HOST_PORT}/ +echo +echo "---> 2.) curl http://127.0.0.1:${HOST_PORT}/" +curl -s http://127.0.0.1:${HOST_PORT}/ +echo +echo "---> 3.) curl http://localhost:${HOST_PORT}/" +curl -s http://localhost:${HOST_PORT}/ + +echo "... You have $TIMEOUT_SEC seconds to try out the above URL provided by JavaScript as Web Servers." +sleep $TIMEOUT_SEC + +cleanup + + diff --git a/tryPython.sh b/tryPython.sh old mode 100644 new mode 100755 index bb55247..ab23c44 --- a/tryPython.sh +++ b/tryPython.sh @@ -1,4 +1,4 @@ -#!/bin/bash -x +#!/bin/bash ################################################### #### ---- Change this only if want to use your own @@ -11,15 +11,23 @@ ORGANIZATION=openkbs DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` imageTag=${1:-"${ORGANIZATION}/${DOCKER_IMAGE_REPO}"} -docker run -it --rm ${imageTag} /usr/bin/python3 -c 'print("Hello World")' - -docker run --rm ${imageTag} /usr/bin/python3 -c 'print("Hello World")' +echo +echo "----------------------------------------------------------------------" +echo "1.) Way-1: Run python3 with command line: Hello World (command line):" +docker run --rm ${imageTag} python3 -c 'print("Hello World (command line) from ./tryPython.sh")' mkdir -p ./data -echo "print('Hello World')" > ./data/myPyScript.py - -docker run -it --rm --name some-jdk11-mvn-py3 -v "$PWD"/data:/data ${imageTag} /usr/bin/python3 myPyScript.py - -docker run -i --rm ${imageTag} /usr/bin/python3 < ./data/myPyScript.py - +echo "print('Hello World (./data/myPyScript.py) from ./tryPython.sh')" > ./data/myPyScript.py + +echo +echo "----------------------------------------------------------------------" +echo "2.) Way-2: Run python3 /data/myPyScript.py:" +echo "docker run --rm -v "$PWD"/data:/data --workdir /data ${imageTag} python3 myPyScript.py" +docker run --rm -v "$PWD"/data:/data --workdir /data ${imageTag} python3 myPyScript.py + +echo +echo "----------------------------------------------------------------------" +echo "3.) Way-3: Pipe file host's current data directory ./data/myPyScript.py into python3:" +echo "docker run --rm --workdir /data ${imageTag} python3 < ./data/myPyScript.py" +docker run --rm --workdir /data ${imageTag} python3 < ./data/myPyScript.py diff --git a/tryWebSocketServer.sh b/tryWebSocketServer.sh new file mode 100755 index 0000000..1a25fb1 --- /dev/null +++ b/tryWebSocketServer.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +SCRIPT_FILE=WebSocketServer.js +HOST_PORT=8080 +SERVER_PORT=8080 + +TIMEOUT_SEC=40 + +################################################### +#### ---- Change this only if want to use your own +################################################### +ORGANIZATION=openkbs + +################################################### +#### ---- Container package information ---- +################################################### +DOCKER_IMAGE_REPO=`echo $(basename $PWD)|tr '[:upper:]' '[:lower:]'|tr "/: " "_" ` +imageTag=${1:-"${ORGANIZATION}/${DOCKER_IMAGE_REPO}"} + +################################################### +#### ---- Mostly, you don't need change below ---- +################################################### +instanceName=some-jdk-mvn-py3 +function cleanup() { + if [ ! "`docker ps -a|grep ${instanceName}`" == "" ]; then + docker rm -f ${instanceName} + fi +} +cleanup + +mkdir -p ./data + +# To run: nodejs ./WebSocketServer.js +# Then => Open http://localhost:8080/ +# To see: Hello World! + +cat > ./data/${SCRIPT_FILE} <<'EOF' + +const WebSocketServer = require('ws').Server; +const wss = new WebSocketServer({ port: 8080, host : '0.0.0.0' }); + +// -- connection -- +wss.on('connection', function connection(ws) { + const ip = ws._socket.remoteAddress; + console.log('Server WebSocket was connected from: ' + ip); + ws.send('... Hello World from openkbs/jdk-mvn-py3 with WebSocket in NodeJS supports! \n... See https://github.com/DrSnowbird/jdk-mvn-py3 for more information.\n'); + + // -- open -- + ws.on('open', function open() { + console.log('connected'); + ws.send('time (open): ' + Date.now()); + }); + + // -- message -- + ws.on('message', function incoming(data) { + // console.log(`Roundtrip time: ${Date.now() - data} ms`); + ws.send('message: ' + data + ', received by Server at:' + Date.now()); + console.log('message: ' + data + ', received by Server at:' + Date.now()); + + // setTimeout(function timeout() { + // console.log('setTimeout at:' + Date.now()); + // ws.send('setTimeout at: ' + Date.now()); + // }, 500); + }); + + // -- close -- + ws.on('close', function close() { + console.log('time (close) at:' + Date.now()); + console.log('disconnected'); + }); + +}); + +EOF + +echo +echo "----------------------------------------------------------------------" +echo "Starting JavaScript NodeJS mini-webserver:" +echo "You have $TIMEOUT_SEC to test this NodeJS mini-server." +echo "... then, it will destroy itself (like 007 Bond's movie - self-clean up :-) " +echo "----------------------------------------------------------------------" +echo "---> Open your browser to : http://127.0.0.1:${HOST_PORT}/" +echo "---> Or, command line:" +echo " curl http://localhost:${HOST_PORT}/" +echo " curl http://127.0.0.1:${HOST_PORT}/" +echo +docker run -d --rm --name ${instanceName} -v $PWD/data:/data -p ${HOST_PORT}:${SERVER_PORT} --workdir /data openkbs/jdk-mvn-py3 nodejs /data/${SCRIPT_FILE} + +echo "---> Testing the mini-server by the ${SCRIPT_FILE} script:" +echo "Use Web Socket Client, e.g., Chrome plugins, WebSocketTestClient, SimpleWebSocketClient in brwoser to test:" +echo "For example," +echo " echo \"Some data to be sent\" | websocat ws://127.0.0.1:${HOST_PORT} " +echo "or," +echo " echo \"Some data to be sent\" | python3-wsdump ws://127.0.0.1:${HOST_PORT} " +echo "" +echo "Obviously, there are also alternatives like wscat (golang) or wscat (node)." +echo "To install python3-wsdump package: sudo apt install -y python3-websocket" +echo "... You have $TIMEOUT_SEC seconds to try out the above URL provided by JavaScript as Web Servers." +sleep $TIMEOUT_SEC + +cleanup + +