diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5acb6f4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,74 @@ +FROM docker.io/baseruntime/baseruntime:latest + +# Port on which will apache run +EXPOSE 8080 + +# Image metadata +ENV NAME=perl \ + PERL_VERSION=5.24 \ + VERSION=0 \ + RELEASE=1 \ + ARCH=x86_64 + +# Set labels used in OpenShift to describe the builder images +LABEL com.redhat.component="$NAME" \ + name="$FGC/$NAME" \ + summary="Perl 5 is a highly capable, feature-rich programming language." \ + description="Perl 5 is a highly capable, feature-rich programming language with over 29 years of development. Perl 5 runs on over 100 platforms." \ + version="$VERSION" \ + release="$RELEASE.$DISTTAG" \ + architecture="$ARCH" \ + usage="s2i build file:///your/app modularitycontainers/perl your-app" \ + io.k8s.description="Platform for building and running Perl 5.24 applications" \ + io.k8s.display-name="Apache 2.4 with mod_perl/5.24" \ + io.openshift.expose-services="8080:http" \ + io.openshift.tags="builder,perl,perl524" \ + io.openshift.s2i.scripts-url=image:///usr/local/s2i + + +COPY repos/* /etc/yum.repos.d/ + +# Perl and build tools install + user addition +RUN BUILD_TOOlS="bsdtar \ + findutils \ + gcc \ + make \ + gettext \ + tar \ + wget \ + python " && \ + microdnf --nodocs --enablerepo perl install perl perl-devel && \ + microdnf --nodocs --enablerepo fedora install -y mod_perl cpan cpanminus httpd \ + $BUILD_TOOlS && \ + microdnf clean all +RUN mkdir -p /opt/app-root/src/ && \ + useradd -u 1002 -r -g 0 -d /opt/app-root/src -s /sbin/nologin \ + -c "Default Application User" default && \ + chown -R 1002:0 /opt/app-root + +# Copy s2i files +COPY ./s2i/bin/ /usr/local/s2i + +# Copy apache configuration +COPY ./contrib/ /opt/app-root + +# Drop the root user +RUN mkdir -p /opt/app-root/etc/httpd.d && \ + sed -i -f /opt/app-root/etc/httpdconf.sed etc/httpd/conf/httpd.conf && \ + chmod -R og+rwx /var/run/httpd /opt/app-root/etc/httpd.d && \ + chown -R 1002:0 /opt/app-root && chmod -R ug+rwx /opt/app-root + +USER 1002 + +# Copy executable utilities. +COPY bin/ /usr/bin/ + +# Copy help file +COPY root/help.1 / + +# Directory with the sources is set as the working directory so all STI scripts +# can execute relative to this path. +WORKDIR /opt/app-root/src + +# Set the default command to print the usage +CMD /usr/local/s2i/usage diff --git a/README.md b/README.md new file mode 100644 index 0000000..458f843 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# Perl S2I +This repository contains the sources for S2I builder image for perl 5.24 as well as some sample applications. This is a port to Fedora of software collections Perl located [here](https://github.com/sclorg/s2i-perl-container), that is now using base-runtime and Perl module. Please note that this solution uses httpd 2.4 to display results. + +# Usage +## Docker +First you need to build the builder image. After you have the builder image, you can use it for a S2I build. You can build and run the sample app like this: +``` +$ docker build --tag=koscicz/perl . +$ s2i build https://github.com/container-images/perl.git --context-dir=test/sample-test-app/ koscicz/perl perl-sample-app +$ docker run -p 8080:8080 perl-sample-app +``` +This will build and run you application on 127.0.0.1:8080. You can also build the builder image by running `make`. This will also tag your image as koscicz/perl. + +## Openshift +You can use the `openshift-template.yml` in this repository to run your application in Openshift. You can either use web interface, in which case you'll just have to fill out a form, requiring a source repository for your app and name of this app. In case you want to use cli, you can do it like this, after you login: +``` +$ oc new-project perl-test +$ oc new-app https://raw.githubusercontent.com/container-images/perl/master/openshift-template.yml -p APP_NAME="perl-example" -p SOURCE_REPOSITORY="https://github.com/kosciCZ/perl-example" +``` +These commands will build the sample application on your Openshift platform. + +# Testing +This repository contains a `test` folder with some tests to ensure proper functionality. You can run test by using Makefile. You have two options: +* `make all` This will build, tag and test the image +* `make test` This will run the test on the image, that is defined in the Makefile. By default it is koscicz/perl +Or you can execute the test script directly, as described below in [Repository organization](#repository-organization) + +Repository organization +------------------------ +* **Dockerfile** + + Fedora based Dockerfile. + +* **`s2i/bin/`** + + This folder contains scripts that are run by [S2I](https://github.com/openshift/source-to-image): + + * **assemble** + + Used to install the sources into a location where the application + will be run and prepare the application for deployment (eg. installing + modules, etc.). + In order to install application dependencies, the application must contain a + `cpanfile` file, in which the user specifies the modules and their versions. + An example of a [cpanfile](https://github.com/container-images/perl/blob/master/test/sample-test-app/cpanfile) is available within our test application. + + All files with `.cgi` and `.pl` extension are handled by mod_perl. + If exactly one file with `.psgi` extension exists in the top-level + directory, the mod_perl will be autoconfigured to execute the PSGI + application for any request URI path with Plack's mod_perl adaptor. + + * **run** + + This script is responsible for running the application, using the + Apache web server. + + * **usage*** + + This script prints the usage of this image. + +* **`contrib/`** + + This folder contains a file with commonly used modules. + +* **`test/`** + + This folder contains some sample applications you can use to test you image. + + * **`sample-test-app/`** + + A simple Perl application used for testing purposes by the [S2I](https://github.com/openshift/source-to-image) test framework. + + * **run** + + This is a script that runs a test suite on the builder image, to ensure it has all the necessary functionality. + Run it by `./run IMAGE`, where `IMAGE` is your builder image. + +Environment variables +--------------------- + +To set environment variables, you can place them as a key value pair into a `.sti/environment` +file inside your source code repository. + +* **ENABLE_CPAN_TEST** + + Allow the installation of all specified cpan packages and the running of their tests. The default value is `false`. + +* **CPAN_MIRROR** + + This variable specifies a mirror URL which will used by cpanminus to install dependencies. + By default the URL is not specified. + +* **PERL_APACHE2_RELOAD** + + Set this to "true" to enable automatic reloading of modified Perl modules. + +* **HTTPD_START_SERVERS** + + The [StartServers](https://httpd.apache.org/docs/2.4/mod/mpm_common.html#startservers) + directive sets the number of child server processes created on startup. Default is 8. + +* **HTTPD_MAX_REQUEST_WORKERS** + + Number of simultaneous requests that will be handled by Apache. The default + is 256, but it will be automatically lowered if memory is limited. + +* **PSGI_FILE** + + Override PSGI application detection. + + If the PSGI_FILE variable is set to empty value, no PSGI application will + be detected and mod_perl not be reconfigured. + + If the PSGI_FILE variable is set and non-empty, it will define path to + the PSGI application file. No detection will be used. + + If the PSGI_FILE variable does not exist, autodetection will be used: + If exactly one ./*.psgi file exists, mod_perl will be configured to + execute that file. + +* **PSGI_URI_PATH** + + This variable overrides location URI path that is handled path the PSGI + application. Default value is "/". \ No newline at end of file diff --git a/bin/cgroup-limits b/bin/cgroup-limits new file mode 100755 index 0000000..3107fbf --- /dev/null +++ b/bin/cgroup-limits @@ -0,0 +1,92 @@ +#!/usr/bin/python + +""" +Script for parsing cgroup information + +This script will read some limits from the cgroup system and parse +them, printing out "VARIABLE=VALUE" on each line for every limit that is +successfully read. Output of this script can be directly fed into +bash's export command. Recommended usage from a bash script: + + set -o errexit + export_vars=$(cgroup-limits) ; export $export_vars + +Variables currently supported: + MAX_MEMORY_LIMIT_IN_BYTES + Maximum possible limit MEMORY_LIMIT_IN_BYTES can have. This is + currently constant value of 9223372036854775807. + MEMORY_LIMIT_IN_BYTES + Maximum amount of user memory in bytes. If this value is set + to the same value as MAX_MEMORY_LIMIT_IN_BYTES, it means that + there is no limit set. The value is taken from + /sys/fs/cgroup/memory/memory.limit_in_bytes + NUMBER_OF_CORES + Number of detected CPU cores that can be used. This value is + calculated from /sys/fs/cgroup/cpuset/cpuset.cpus + NO_MEMORY_LIMIT + Set to "true" if MEMORY_LIMIT_IN_BYTES is so high that the caller + can act as if no memory limit was set. Undefined otherwise. +""" + +from __future__ import print_function +import sys + + +def _read_file(path): + try: + with open(path, 'r') as f: + return f.read().strip() + except IOError: + return None + + +def get_memory_limit(): + """ + Read memory limit, in bytes. + """ + + limit = _read_file('/sys/fs/cgroup/memory/memory.limit_in_bytes') + if limit is None or not limit.isdigit(): + print("Warning: Can't detect memory limit from cgroups", + file=sys.stderr) + return None + return int(limit) + + +def get_number_of_cores(): + """ + Read number of CPU cores. + """ + + core_count = 0 + + line = _read_file('/sys/fs/cgroup/cpuset/cpuset.cpus') + if line is None: + print("Warning: Can't detect number of CPU cores from cgroups", + file=sys.stderr) + return None + + for group in line.split(','): + core_ids = list(map(int, group.split('-'))) + if len(core_ids) == 2: + core_count += core_ids[1] - core_ids[0] + 1 + else: + core_count += 1 + + return core_count + + +if __name__ == "__main__": + env_vars = { + "MAX_MEMORY_LIMIT_IN_BYTES": 9223372036854775807, + "MEMORY_LIMIT_IN_BYTES": get_memory_limit(), + "NUMBER_OF_CORES": get_number_of_cores() + } + + env_vars = {k: v for k, v in env_vars.items() if v is not None} + + if env_vars.get("MEMORY_LIMIT_IN_BYTES", 0) >= 92233720368547: + env_vars["NO_MEMORY_LIMIT"] = "true" + + for key, value in env_vars.items(): + print("{0}={1}".format(key, value)) \ No newline at end of file diff --git a/bin/fix-permissions b/bin/fix-permissions new file mode 100755 index 0000000..b6ab3fd --- /dev/null +++ b/bin/fix-permissions @@ -0,0 +1,12 @@ +#!/bin/sh + +# Allow this script to fail without failing a build +set +e + +# Fix permissions on the given directory to allow group read/write of +# regular files and execute of directories. +chgrp -R 0 $1; +find -L $1 -xtype l -exec chgrp 0 {} \; +chmod -R g+rw $1; +find -L $1 -xtype l -exec chmod g+rw {} \; +find $1 -type d -exec chmod g+x {} + \ No newline at end of file diff --git a/contrib/etc/httpd.conf b/contrib/etc/httpd.conf new file mode 100644 index 0000000..66dc0fb --- /dev/null +++ b/contrib/etc/httpd.conf @@ -0,0 +1,18 @@ + +LoadModule perl_module modules/mod_perl.so +DirectoryIndex index.pl + +PerlSwitches -I./extlib/lib/perl5 + + + SetHandler perl-script + PerlResponseHandler ModPerl::PerlRun + Options +ExecCGI +SymLinksIfOwnerMatch + PerlSendHeader On + + + + Require all granted + + +IncludeOptional /opt/app-root/etc/httpd.d/*.conf \ No newline at end of file diff --git a/contrib/etc/httpd.d/50-mpm.conf.template b/contrib/etc/httpd.d/50-mpm.conf.template new file mode 100644 index 0000000..ca18659 --- /dev/null +++ b/contrib/etc/httpd.d/50-mpm.conf.template @@ -0,0 +1,11 @@ +# This value should mirror what is set in MinSpareServers. +StartServers ${HTTPD_START_SERVERS} +MinSpareServers ${HTTPD_START_SERVERS} +MaxSpareServers ${HTTPD_MAX_SPARE_SERVERS} +# The MaxRequestWorkers directive sets the limit on the number of +# simultaneous requests that will be served. +# The default value, when no cgroup limits are set is 256. +MaxRequestWorkers ${HTTPD_MAX_REQUEST_WORKERS} +ServerLimit ${HTTPD_MAX_REQUEST_WORKERS} +MaxConnectionsPerChild 4000 +MaxKeepAliveRequests 100 diff --git a/contrib/etc/httpdconf.sed b/contrib/etc/httpdconf.sed new file mode 100644 index 0000000..45f14db --- /dev/null +++ b/contrib/etc/httpdconf.sed @@ -0,0 +1,7 @@ +s/^Listen 80/Listen 0.0.0.0:8080/ +s/^User apache/User default/ +s/^Group apache/Group root/ +s%^DocumentRoot "/var/www/html"%DocumentRoot "/opt/app-root/src"% +s%^" "DATE 11.04.2017" "" +.BR perl (1) +\-\- S2I builder of perl applications +.SH USAGE +.PP +To pull the perl container run: +.PP +.RS +.nf + # docker pull koscicz/perl +.fi +.RE +.PP +To build your perl application use run: +.PP +.RS +.nf + # s2i build koscicz/perl +.fi +.RE +.PP +To run your application in docker container: +.PP +.RS +.nf + # docker run \-p 8080:8080 +.fi +.RE +.SH ENVIROMENT VARIABLES +.PP +To set environment variables, you can place them as a key value pair into a \fB\fC\&.sti/environment\fR +file inside your source code repository. +.PP +ENABLE_CPAN_TEST +.PP +.RS +.nf +Allow the installation of all specified cpan packages and the running of their tests. The default value is `false`. +.fi +.RE +.PP +CPAN_MIRROR +.PP +.RS +.nf +This variable specifies a mirror URL which will used by cpanminus to install dependencies. +By default the URL is not specified. +.fi +.RE +.PP +PERL_APACHE2_RELOAD +.PP +.RS +.nf +Set this to "true" to enable automatic reloading of modified Perl modules. +.fi +.RE +.PP +HTTPD_START_SERVERS +.PP +.RS +.nf +The [StartServers](https://httpd.apache.org/docs/2.4/mod/mpm_common.html#startservers) +directive sets the number of child server processes created on startup. Default is 8. +.fi +.RE +.PP +HTTPD_MAX_REQUEST_WORKERS +.PP +.RS +.nf +Number of simultaneous requests that will be handled by Apache. The default +is 256, but it will be automatically lowered if memory is limited. +.fi +.RE +.PP +PSGI_FILE +.PP +.RS +.nf +Override PSGI application detection. + +If the PSGI_FILE variable is set to empty value, no PSGI application will +be detected and mod_perl not be reconfigured. + +If the PSGI_FILE variable is set and non\-empty, it will define path to +the PSGI application file. No detection will be used. + +If the PSGI_FILE variable does not exist, autodetection will be used: +If exactly one ./*.psgi file exists, mod_perl will be configured to +execute that file. +.fi +.RE +.PP +PSGI_URI_PATH +.PP +.RS +.nf +This variable overrides location URI path that is handled path the PSGI +application. Default value is "/". +.fi +.RE diff --git a/root/help.md b/root/help.md new file mode 100644 index 0000000..bfb35d0 --- /dev/null +++ b/root/help.md @@ -0,0 +1,63 @@ +perl(1) -- S2I builder of perl applications +============================================= + +## USAGE + +To pull the perl container run: + + # docker pull koscicz/perl + +To build your perl application use run: + + # s2i build koscicz/perl + +To run your application in docker container: + + # docker run -p 8080:8080 + +## ENVIROMENT VARIABLES + +To set environment variables, you can place them as a key value pair into a `.sti/environment` +file inside your source code repository. + +ENABLE\_CPAN\_TEST + + Allow the installation of all specified cpan packages and the running of their tests. The default value is `false`. + +CPAN_MIRROR + + This variable specifies a mirror URL which will used by cpanminus to install dependencies. + By default the URL is not specified. + +PERL\_APACHE2\_RELOAD + + Set this to "true" to enable automatic reloading of modified Perl modules. + +HTTPD\_START\_SERVERS + + The [StartServers](https://httpd.apache.org/docs/2.4/mod/mpm_common.html#startservers) + directive sets the number of child server processes created on startup. Default is 8. + +HTTPD\_MAX\_REQUEST\_WORKERS + + Number of simultaneous requests that will be handled by Apache. The default + is 256, but it will be automatically lowered if memory is limited. + +PSGI_FILE + + Override PSGI application detection. + + If the PSGI_FILE variable is set to empty value, no PSGI application will + be detected and mod_perl not be reconfigured. + + If the PSGI_FILE variable is set and non-empty, it will define path to + the PSGI application file. No detection will be used. + + If the PSGI_FILE variable does not exist, autodetection will be used: + If exactly one ./*.psgi file exists, mod_perl will be configured to + execute that file. + +PSGI\_URI\_PATH + + This variable overrides location URI path that is handled path the PSGI + application. Default value is "/". \ No newline at end of file diff --git a/s2i/bin/assemble b/s2i/bin/assemble new file mode 100755 index 0000000..2363d37 --- /dev/null +++ b/s2i/bin/assemble @@ -0,0 +1,76 @@ +#!/bin/bash + +set -e + +shopt -s dotglob +echo "---> Installing application source ..." +mv /tmp/src/* ./ + +if [ -d ./cfg ]; then + echo "---> Copying configuration files..." + if [ "$(ls -A ./cfg/*.conf)" ]; then + cp -v ./cfg/*.conf /opt/app-root/etc/httpd.d/ + fi +fi + +# Allow for http proxy to be specified in uppercase +if [[ -n "${HTTP_PROXY:-}" && -z "${http_proxy:-}" ]]; then + export http_proxy=$HTTP_PROXY +fi + +export CPAN_MIRROR=${CPAN_MIRROR:-""} + +MIRROR_ARGS="" + +if [ -n "$CPAN_MIRROR" ]; then + MIRROR_ARGS="--mirror $CPAN_MIRROR" +fi + +# Don't test installed Perl modules by default +if [ "${ENABLE_CPAN_TEST}" = true ]; then + export ENABLE_CPAN_TEST="" +else + export ENABLE_CPAN_TEST="--notest" +fi + +# Configure mod_perl for PSGI. +# If PSGI_FILE variable is set but empty, skip it. +# If PSGI_FILE is set and non-empty, use it. +# If PSGI_FILE does not exist, check if exactly one ./*.psgi file exists and +# use that file. +# If PSGI_URI_PATH variable has a value, use it as a location. Default is "/". +if [ ! -v PSGI_FILE ]; then + PSGI_FILE=$(find -maxdepth 1 -name '*.psgi' -type f) +fi +PSGI_FILE_NUMBER=$(printf '%s' "$PSGI_FILE" | wc -l) +if [ -n "$PSGI_FILE" -a "$PSGI_FILE_NUMBER" -eq 0 ]; then + echo "---> PSGI application found in $PSGI_FILE" + cat >> cpanfile <<"EOF" +requires 'Plack::Handler::Apache2'; +EOF + # XXX: Escape PSGI_FILE value against httpd control characters + PSGI_FILE=$(printf '%s' "$PSGI_FILE" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g') + cat > /opt/app-root/etc/httpd.d/40-psgi.conf < + SetHandler perl-script + PerlResponseHandler Plack::Handler::Apache2 + PerlSetVar psgi_app "$PSGI_FILE" + +EOF +elif [ "$PSGI_FILE_NUMBER" -gt 0 ]; then + echo "---> Multiple PSGI applications found:" + printf '%s' "$PSGI_FILE" + echo "---> Skipping PSGI autoconfiguration!" +fi + +# Installing dependencies with cpanfile +if [ -f "cpanfile" ]; then + echo "---> Installing modules from cpanfile ..." + cpanm $MIRROR_ARGS $ENABLE_CPAN_TEST -l extlib Module::CoreList + cpanm $MIRROR_ARGS $ENABLE_CPAN_TEST -l extlib --installdeps . +else + echo "---> No cpanfile found, nothing to install" +fi + +# Fix source directory permissions +fix-permissions ./ \ No newline at end of file diff --git a/s2i/bin/run b/s2i/bin/run new file mode 100755 index 0000000..687feb1 --- /dev/null +++ b/s2i/bin/run @@ -0,0 +1,46 @@ +#!/bin/bash + +set -e + +# CPAN can install scripts. They should be available from mod_perl too. +export PATH=${PATH}:/opt/app-root/src/extlib/bin +# And we have to set Perl include path too because mod_perl's PerlSwitches +# does not apply to them. +export PERL5LIB=/opt/app-root/src/extlib/lib/perl5 + +# Warning: Please note that this will pass all environment variables available within the +# container to mod_perl by means of PerlPassEnv in the env.conf file +if [ ! -f /opt/app-root/etc/httpd.d/env.conf ]; then + env | awk -F'=' '{print "PerlPassEnv "$1}' > /opt/app-root/etc/httpd.d/env.conf +fi + +# Enable automatic reloading. This can be useful for debugging an application. +PERL_APACHE2_RELOAD=${PERL_APACHE2_RELOAD:-} +if [[ "${PERL_APACHE2_RELOAD,,}" == "true" ]]; then + cat > /opt/app-root/etc/httpd.d/50-autoreload.conf < 0 ? MAX_SERVER_LIMIT : 1)) + export HTTPD_MAX_REQUEST_WORKERS=$((MAX_SERVER_LIMIT > 256 ? 256 : MAX_SERVER_LIMIT)) +fi + +export HTTPD_START_SERVERS=${HTTPD_START_SERVERS:-8} +export HTTPD_MAX_SPARE_SERVERS=$((HTTPD_START_SERVERS+10)) + +envsubst < /opt/app-root/etc/httpd.d/50-mpm.conf.template > /opt/app-root/etc/httpd.d/50-mpm.conf + +exec httpd -C 'Include /opt/app-root/etc/httpd.conf' -D FOREGROUND \ No newline at end of file diff --git a/s2i/bin/usage b/s2i/bin/usage new file mode 100755 index 0000000..ddfa51b --- /dev/null +++ b/s2i/bin/usage @@ -0,0 +1,11 @@ + +#!/bin/sh + +cat <