diff --git a/2.7/Dockerfile b/2.7/Dockerfile index 642c40a..7a8dbd2 100644 --- a/2.7/Dockerfile +++ b/2.7/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/oryx/python:2.7-20190730.1 +FROM mcr.microsoft.com/oryx/python:2.7-20191018.1 LABEL maintainer="appsvc-images@microsoft.com" # Web Site Home @@ -13,6 +13,10 @@ RUN apt-get update \ wget \ # build-essential \ tcptraceroute \ + net-tools \ + dnsutils \ + tcpdump \ + iproute2 \ && pip install --upgrade pip \ && pip install gunicorn \ && pip install virtualenv \ @@ -28,7 +32,7 @@ ENV SSH_PORT 2222 # setup SSH RUN mkdir -p /home/LogFiles \ && echo "root:Docker!" | chpasswd \ - && echo "cd /home" >> /etc/bash.bashrc + && echo "cd /home" >> /root/.bashrc COPY sshd_config /etc/ssh/ RUN mkdir -p /opt/startup @@ -37,6 +41,7 @@ COPY init_container.sh /opt/startup/init_container.sh # setup default site RUN mkdir /opt/defaultsite COPY hostingstart.html /opt/defaultsite +COPY hostingstart_dep.html /opt/defaultsite COPY application.py /opt/defaultsite # configure startup diff --git a/2.7/application.py b/2.7/application.py index ae7b9e5..cf9c45a 100644 --- a/2.7/application.py +++ b/2.7/application.py @@ -1,6 +1,10 @@ from flask import Flask +import os app = Flask(__name__, static_folder='/opt/defaultsite') @app.route('/') def root(): - return app.send_static_file('hostingstart.html') + if os.path.isdir('/home/site/deployments') and len(os.walk('/home/site/deployments')[1]) > 1: + return app.send_static_file('hostingstart_dep.html') + else: + return app.send_static_file('hostingstart.html') diff --git a/2.7/hostingstart_dep.html b/2.7/hostingstart_dep.html new file mode 100644 index 0000000..51d61bb --- /dev/null +++ b/2.7/hostingstart_dep.html @@ -0,0 +1 @@ + Microsoft Azure App Service - Welcome

Hey, Python developers!


Your app service is up and running.

Time to take the next step and deploy your code.

Have your code ready?
Use deployment center to get code published from your client or setup continuous deployment.

Don't have your code yet?
Follow our quickstart guide and you'll have a full app ready in 5 minutes or less.

diff --git a/2.7/init_container.sh b/2.7/init_container.sh index d69413e..e8da03a 100644 --- a/2.7/init_container.sh +++ b/2.7/init_container.sh @@ -54,6 +54,18 @@ else fi fi +debugArgs="" +if [ "$APPSVC_REMOTE_DEBUGGING" == "TRUE" ]; then + echo "App will launch in debug mode" + debugArgs="-debugAdapter ptvsd -debugPort $APPSVC_TUNNEL_PORT" + + if [ "$APPSVC_REMOTE_DEBUGGING_BREAK" == "TRUE" ]; then + debugArgs+=" -debugWait" + fi + + oryxArgs="$debugArgs $oryxArgs" +fi + echo "Launching oryx with: $oryxArgs" #invoke oryx to generate startup script eval "oryx $oryxArgs" diff --git a/3.6/Dockerfile b/3.6/Dockerfile index 51bd687..87d89d8 100644 --- a/3.6/Dockerfile +++ b/3.6/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/oryx/python:3.6-20190730.1 +FROM mcr.microsoft.com/oryx/python:3.6-20191018.1 LABEL maintainer="appsvc-images@microsoft.com" # Web Site Home @@ -12,6 +12,10 @@ RUN apt-get update \ curl \ wget \ tcptraceroute \ + net-tools \ + dnsutils \ + tcpdump \ + iproute2 \ && pip install --upgrade pip \ && pip install subprocess32 \ && pip install gunicorn \ @@ -27,7 +31,7 @@ ENV SSH_PORT 2222 # setup SSH RUN mkdir -p /home/LogFiles \ && echo "root:Docker!" | chpasswd \ - && echo "cd /home" >> /etc/bash.bashrc + && echo "cd /home" >> /root/.bashrc COPY sshd_config /etc/ssh/ RUN mkdir -p /opt/startup @@ -36,6 +40,7 @@ COPY init_container.sh /opt/startup/init_container.sh # setup default site RUN mkdir /opt/defaultsite COPY hostingstart.html /opt/defaultsite +COPY hostingstart_dep.html /opt/defaultsite COPY application.py /opt/defaultsite # configure startup diff --git a/3.6/application.py b/3.6/application.py index ae7b9e5..0737e26 100644 --- a/3.6/application.py +++ b/3.6/application.py @@ -1,6 +1,10 @@ from flask import Flask +import os app = Flask(__name__, static_folder='/opt/defaultsite') @app.route('/') def root(): - return app.send_static_file('hostingstart.html') + if os.path.isdir('/home/site/deployments') and len(next(os.walk('/home/site/deployments'))[1]) > 1: + return app.send_static_file('hostingstart_dep.html') + else: + return app.send_static_file('hostingstart.html') diff --git a/3.6/hostingstart_dep.html b/3.6/hostingstart_dep.html new file mode 100644 index 0000000..51d61bb --- /dev/null +++ b/3.6/hostingstart_dep.html @@ -0,0 +1 @@ + Microsoft Azure App Service - Welcome

Hey, Python developers!


Your app service is up and running.

Time to take the next step and deploy your code.

Have your code ready?
Use deployment center to get code published from your client or setup continuous deployment.

Don't have your code yet?
Follow our quickstart guide and you'll have a full app ready in 5 minutes or less.

diff --git a/3.6/init_container.sh b/3.6/init_container.sh index 9687cfb..fb9028a 100644 --- a/3.6/init_container.sh +++ b/3.6/init_container.sh @@ -54,6 +54,18 @@ else fi fi +debugArgs="" +if [ "$APPSVC_REMOTE_DEBUGGING" == "TRUE" ]; then + echo "App will launch in debug mode" + debugArgs="-debugAdapter ptvsd -debugPort $APPSVC_TUNNEL_PORT" + + if [ "$APPSVC_REMOTE_DEBUGGING_BREAK" == "TRUE" ]; then + debugArgs+=" -debugWait" + fi + + oryxArgs="$debugArgs $oryxArgs" +fi + echo "Launching oryx with: $oryxArgs" #invoke oryx to generate startup script eval "oryx $oryxArgs" diff --git a/3.7/Dockerfile b/3.7/Dockerfile index 7c5e091..79b38a9 100644 --- a/3.7/Dockerfile +++ b/3.7/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/oryx/python:3.7-20190730.1 +FROM mcr.microsoft.com/oryx/python:3.7-20191018.1 LABEL maintainer="appsvc-images@microsoft.com" # Web Site Home @@ -12,6 +12,10 @@ RUN apt-get update \ curl \ wget \ tcptraceroute \ + net-tools \ + dnsutils \ + tcpdump \ + iproute2 \ && pip install --upgrade pip \ && pip install subprocess32 \ && pip install gunicorn \ @@ -27,8 +31,8 @@ ENV SSH_PORT 2222 # setup SSH RUN mkdir -p /home/LogFiles \ && echo "root:Docker!" | chpasswd \ - && echo "cd /home" >> /etc/bash.bashrc - + && echo "cd /home" >> /root/.bashrc + COPY sshd_config /etc/ssh/ RUN mkdir -p /opt/startup COPY init_container.sh /opt/startup/init_container.sh @@ -36,6 +40,7 @@ COPY init_container.sh /opt/startup/init_container.sh # setup default site RUN mkdir /opt/defaultsite COPY hostingstart.html /opt/defaultsite +COPY hostingstart_dep.html /opt/defaultsite COPY application.py /opt/defaultsite # configure startup diff --git a/3.7/application.py b/3.7/application.py index ae7b9e5..0737e26 100644 --- a/3.7/application.py +++ b/3.7/application.py @@ -1,6 +1,10 @@ from flask import Flask +import os app = Flask(__name__, static_folder='/opt/defaultsite') @app.route('/') def root(): - return app.send_static_file('hostingstart.html') + if os.path.isdir('/home/site/deployments') and len(next(os.walk('/home/site/deployments'))[1]) > 1: + return app.send_static_file('hostingstart_dep.html') + else: + return app.send_static_file('hostingstart.html') diff --git a/3.7/hostingstart_dep.html b/3.7/hostingstart_dep.html new file mode 100644 index 0000000..51d61bb --- /dev/null +++ b/3.7/hostingstart_dep.html @@ -0,0 +1 @@ + Microsoft Azure App Service - Welcome

Hey, Python developers!


Your app service is up and running.

Time to take the next step and deploy your code.

Have your code ready?
Use deployment center to get code published from your client or setup continuous deployment.

Don't have your code yet?
Follow our quickstart guide and you'll have a full app ready in 5 minutes or less.

diff --git a/3.7/init_container.sh b/3.7/init_container.sh index 2f0d9e1..62f9816 100644 --- a/3.7/init_container.sh +++ b/3.7/init_container.sh @@ -25,7 +25,6 @@ eval $(printenv | sed -n "s/^\([^=]\+\)=\(.*\)$/export \1=\2/p" | sed 's/"/\\\"/ echo "$@" > /opt/startup/startupCommand chmod 755 /opt/startup/startupCommand -#oryx startup script generator oryxArgs="-appPath /home/site/wwwroot -output /opt/startup/startup.sh -virtualEnvName antenv -defaultApp /opt/defaultsite -bindPort $PORT" if [ $# -eq 0 ]; then echo 'App Command Line not configured, will attempt auto-detect' @@ -55,6 +54,18 @@ else fi fi +debugArgs="" +if [ "$APPSVC_REMOTE_DEBUGGING" == "TRUE" ]; then + echo "App will launch in debug mode" + debugArgs="-debugAdapter ptvsd -debugPort $APPSVC_TUNNEL_PORT" + + if [ "$APPSVC_REMOTE_DEBUGGING_BREAK" == "TRUE" ]; then + debugArgs+=" -debugWait" + fi + + oryxArgs="$debugArgs $oryxArgs" +fi + echo "Launching oryx with: $oryxArgs" #invoke oryx to generate startup script eval "oryx $oryxArgs" diff --git a/3.8/Dockerfile b/3.8/Dockerfile new file mode 100644 index 0000000..09fd6fb --- /dev/null +++ b/3.8/Dockerfile @@ -0,0 +1,50 @@ +FROM mcr.microsoft.com/oryx/python:3.8-20191018.1 +LABEL maintainer="appsvc-images@microsoft.com" + +# Web Site Home +ENV HOME_SITE "/home/site/wwwroot" + +#Install system dependencies +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + openssh-server \ + vim \ + curl \ + wget \ + tcptraceroute \ + net-tools \ + dnsutils \ + tcpdump \ + iproute2 \ + && pip install --upgrade pip \ + && pip install subprocess32 \ + && pip install gunicorn \ + && pip install virtualenv \ + && pip install flask + +WORKDIR ${HOME_SITE} + +EXPOSE 8000 +ENV PORT 8000 +ENV SSH_PORT 2222 + +# setup SSH +RUN mkdir -p /home/LogFiles \ + && echo "root:Docker!" | chpasswd \ + && echo "cd /home" >> /root/.bashrc + +COPY sshd_config /etc/ssh/ +RUN mkdir -p /opt/startup +COPY init_container.sh /opt/startup/init_container.sh + +# setup default site +RUN mkdir /opt/defaultsite +COPY hostingstart.html /opt/defaultsite +COPY hostingstart_dep.html /opt/defaultsite +COPY application.py /opt/defaultsite + +# configure startup +RUN chmod -R 777 /opt/startup +COPY entrypoint.py /usr/local/bin + +ENTRYPOINT ["/opt/startup/init_container.sh"] diff --git a/3.8/application.py b/3.8/application.py new file mode 100644 index 0000000..0737e26 --- /dev/null +++ b/3.8/application.py @@ -0,0 +1,10 @@ +from flask import Flask +import os +app = Flask(__name__, static_folder='/opt/defaultsite') + +@app.route('/') +def root(): + if os.path.isdir('/home/site/deployments') and len(next(os.walk('/home/site/deployments'))[1]) > 1: + return app.send_static_file('hostingstart_dep.html') + else: + return app.send_static_file('hostingstart.html') diff --git a/3.8/build.sh b/3.8/build.sh new file mode 100644 index 0000000..5bfe175 --- /dev/null +++ b/3.8/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -x -e + +buildnumber=${4-$(date -u +"%y%m%d%H%M")} + +docker build --no-cache -t "$1"/python:3.8_"$buildnumber" . +docker tag "$1"/python:3.7_"$buildnumber" "$1"/python:latest diff --git a/3.8/entrypoint.py b/3.8/entrypoint.py new file mode 100644 index 0000000..ca3b5a3 --- /dev/null +++ b/3.8/entrypoint.py @@ -0,0 +1,120 @@ +import subprocess +import os + +HOME_SITE="/home/site/wwwroot" +DEFAULT_SITE="/opt/defaultsite" +STARTUP_COMMAND_FILE="/opt/startup/startupCommand" +APPSVC_VIRTUAL_ENV="antenv" + +# Temp patch. Remove when Kudu script is available. +os.environ["PYTHONPATH"] = HOME_SITE + "/antenv/lib/python3.8/site-packages" + +def subprocess_cmd(command): + print ('executing:') + print (command) + + process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True) + proc_stdout = process.communicate()[0].strip() + print (proc_stdout.decode("utf-8")) + +## Check for custom startup command +def custom_check(): + with open(STARTUP_COMMAND_FILE, 'r') as myfile: + startupScript = myfile.read().rstrip() + if not startupScript: + return None + else: + if ".." not in startupScript: + startupFilePath = HOME_SITE + '/' + startupScript + print('startup script: ' + startupFilePath) + try: + startupFile = open(startupFilePath, 'r') + print ('identified startup script as a file on disk') + startArgs = startupFile.read() + print(startArgs) + if not startArgs: + return None + else: + return startArgs + + except: + # appCommandLine is not a file, assume it is the script to be started bu gunicorn + print('startup script is not a file, use it as gunicorn arg') + return startupScript + else: + print('invalid data in startup script, ignoring it.') + return None + + return startupScript + +## Django check: If 'wsgi.py' is provided, identify as Django. +def check_django(): + with os.scandir(HOME_SITE) as siteRoot: + for entry in siteRoot: + if not entry.name.startswith(APPSVC_VIRTUAL_ENV) and entry.is_dir(): + with os.scandir(HOME_SITE + '/'+ entry.name) as subFolder: + for subEntry in subFolder: + if subEntry.name == 'wsgi.py' and subEntry.is_file(): + print ("found django app") + return entry.name + '.wsgi' + return None + +## Flask check: If 'application.py' is provided or a .py module is present, identify as Flask. +def check_flask(): + with os.scandir(HOME_SITE) as siteRoot: + for entry in siteRoot: + if entry.is_file(): + if (entry.name == 'application.py'): + print("found flask app") + return "application:app" + else: + if (entry.name == 'app.py'): + print("found flask app") + return "app:app" + + return None + +def start_server(): + + cmd = custom_check() + if cmd is not None: + print('custom startup found: ' + cmd); + subprocess_cmd('. antenv/bin/activate') + if 'python' in cmd: + subprocess_cmd(cmd) + + elif 'gunicorn' in cmd: + subprocess_cmd(cmd) + + else: + subprocess_cmd( + 'GUNICORN_CMD_ARGS="--bind=0.0.0.0 --timeout 600" gunicorn ' + cmd + ) + return + + cmd = check_django() + if cmd is not None: + subprocess_cmd('. antenv/bin/activate') + subprocess_cmd( + 'GUNICORN_CMD_ARGS="--bind=0.0.0.0 --timeout 600" gunicorn ' + cmd + ) + return + + cmd = check_flask() + if cmd is not None: + subprocess_cmd('. antenv/bin/activate') + subprocess_cmd( + 'GUNICORN_CMD_ARGS="--bind=0.0.0.0 --timeout 600" gunicorn ' + cmd + ) + return + + else: + print('starting default app') + subprocess_cmd( + 'GUNICORN_CMD_ARGS="--bind=0.0.0.0 --chdir /opt/defaultsite" gunicorn application:app' + ) + return + +subprocess_cmd('python --version') +subprocess_cmd('pip --version') +start_server() diff --git a/3.8/hostingstart.html b/3.8/hostingstart.html new file mode 100644 index 0000000..5c89769 --- /dev/null +++ b/3.8/hostingstart.html @@ -0,0 +1 @@ +Microsoft Azure App Service - Welcome

Hey, Python developers!


Your app service is up and running.

Time to take the next step and deploy your code.

Have your code ready?
Use deployment center to get code published from your client or setup continuous deployment.

Don't have your code yet?
Follow our quickstart guide and you'll have a full app ready in 5 minutes or less.

diff --git a/3.8/hostingstart_dep.html b/3.8/hostingstart_dep.html new file mode 100644 index 0000000..51d61bb --- /dev/null +++ b/3.8/hostingstart_dep.html @@ -0,0 +1 @@ + Microsoft Azure App Service - Welcome

Hey, Python developers!


Your app service is up and running.

Time to take the next step and deploy your code.

Have your code ready?
Use deployment center to get code published from your client or setup continuous deployment.

Don't have your code yet?
Follow our quickstart guide and you'll have a full app ready in 5 minutes or less.

diff --git a/3.8/init.py b/3.8/init.py new file mode 100644 index 0000000..370c93e --- /dev/null +++ b/3.8/init.py @@ -0,0 +1,59 @@ +import subprocess +import os + +HOME_SITE="/home/site/wwwroot" +DEFAULT_SITE="/opt/defaultsite" +STARTUP_COMMAND_FILE="/opt/startup/startupCommand" +APPSVC_VIRTUAL_ENV="antenv" + +def subprocess_cmd(command): + print ('executing:') + print (command) + + process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True) + proc_stdout = process.communicate()[0].strip() + print (proc_stdout.decode("utf-8")) + +def getStartupArgs(): + with open(STARTUP_COMMAND_FILE, 'r') as myfile: + startupScript = myfile.read().rstrip() + if not startupScript: + print('App startup command not specified, will use defaults..') + return None + else: + if ".." not in startupScript: + startupFilePath = HOME_SITE + '/' + startupScript + print('checking for startup script file: ' + startupFilePath) + try: + startupFile = open(startupFilePath, 'r') + print ('identified startup script as a file on disk') + startArgs = startupFile.read() + print(startArgs) + if not startArgs: + return None + else: + return startArgs + + except: + # appCommandLine is not a file, assume it is the script to be started bu gunicorn + print('startup script is not a file, use it as gunicorn arg') + return startupScript + +def find_and_launch_entrypoint(): + if os.path.isdir(HOME_SITE + '/antenv'): + print('Executing entrypoint.py script:') + subprocess_cmd('python -u /usr/local/bin/entrypoint.py') + else: + oryxCmd = "oryx -appPath /home/site/wwwroot -output /opt/startup/startup.sh -defaultApp /opt/defaultsite " + cmd = getStartupArgs() + if cmd is not None: + oryxCmd += ' -userStartupCommand ' + '\'' + cmd + '\'' + + print('Generating startup command with oryxCmd ' + oryxCmd) + subprocess_cmd(oryxCmd) + print('Launching oryx-Startup script ') + subprocess_cmd('chmod +x /opt/startup/startup.sh') + subprocess_cmd('/opt/startup/startup.sh') + +subprocess_cmd('python --version') +find_and_launch_entrypoint() \ No newline at end of file diff --git a/3.8/init_container.sh b/3.8/init_container.sh new file mode 100644 index 0000000..6d466b8 --- /dev/null +++ b/3.8/init_container.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +cat >/etc/motd < + \/ \/ \/ + +A P P S E R V I C E O N L I N U X + +Documentation: http://aka.ms/webapp-linux + +EOL +cat /etc/motd + +sed -i "s/SSH_PORT/$SSH_PORT/g" /etc/ssh/sshd_config +service ssh start + +# Get environment variables to show up in SSH session +eval $(printenv | sed -n "s/^\([^=]\+\)=\(.*\)$/export \1=\2/p" | sed 's/"/\\\"/g' | sed '/=/s//="/' | sed 's/$/"/' >> /etc/profile) + +echo "$@" > /opt/startup/startupCommand +chmod 755 /opt/startup/startupCommand + +oryxArgs='-appPath /home/site/wwwroot -output /opt/startup/startup.sh -virtualEnvName antenv -defaultApp /opt/defaultsite' +if [ $# -eq 0 ]; then + echo 'App Command Line not configured, will attempt auto-detect' +else + echo "Site's appCommandLine: $@" + if [ $# -eq 1 ]; then + echo "Checking of $1 is a file" + if [ -f $1 ]; then + echo 'App command line is a file on disk' + fileContents=$(head -1 $1) + #if the file ends with .sh + if [ ${1: -3} == ".sh" ]; then + echo 'App command line is a shell script, will execute this script as startup script' + chmod +x $1 + oryxArgs+=" -userStartupCommand $1" + else + echo "$1 file exists on disk, reading its contents to run as startup arguments" + echo "Contents of startupScript: $fileContents" + oryxArgs+=" -userStartupCommand '$fileContents'" + fi + else + echo 'App command line is not a file on disk, using it as the startup command.' + oryxArgs+=" -userStartupCommand '$1'" + fi + else + oryxArgs+=" -userStartupCommand '$@'" + fi +fi + +debugArgs="" +if [ "$APPSVC_REMOTE_DEBUGGING" == "TRUE" ]; then + echo "App will launch in debug mode" + debugArgs="-debugAdapter ptvsd -debugPort $APPSVC_TUNNEL_PORT" + + if [ "$APPSVC_REMOTE_DEBUGGING_BREAK" == "TRUE" ]; then + debugArgs+=" -debugWait" + fi + + oryxArgs="$debugArgs $oryxArgs" +fi + +echo "Launching oryx with: $oryxArgs" +#invoke oryx to generate startup script +eval "oryx $oryxArgs" +chmod +x /opt/startup/startup.sh +#launch startup script +/opt/startup/startup.sh + + diff --git a/3.8/sshd_config b/3.8/sshd_config new file mode 100644 index 0000000..04b53f9 --- /dev/null +++ b/3.8/sshd_config @@ -0,0 +1,16 @@ +# This is ssh server systemwide configuration file. +# +# /etc/sshd_config + +Port SSH_PORT +ListenAddress 0.0.0.0 +LoginGraceTime 180 +X11Forwarding yes +Ciphers aes128-cbc,3des-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr +MACs hmac-sha1,hmac-sha1-96 +StrictModes yes +SyslogFacility DAEMON +PasswordAuthentication yes +PermitEmptyPasswords no +PermitRootLogin yes +Subsystem sftp internal-sftp