Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Marek Skalický
51a70ae84b
Pull changes from upstream repository. 2018-11-05 16:09:07 +00:00
34 changed files with 2341 additions and 487 deletions

1
5.26 Symbolic link
View file

@ -0,0 +1 @@
.

View file

@ -1,71 +1,73 @@
FROM registry.fedoraproject.org/fedora:25
FROM registry.fedoraproject.org/f27/s2i-base:latest
# Port on which will apache run
# This image provides a Perl 5.26 environment you can use to run your Perl applications.
EXPOSE 8080
# Image metadata
ENV NAME=perl \
PERL_VERSION=5.24 \
ENV PERL_VERSION=5.26 \
PERL_SHORT_VER=526 \
VERSION=0 \
RELEASE=1 \
ARCH=x86_64
NAME=perl
# 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" \
ENV SUMMARY="Platform for building and running Perl $PERL_VERSION applications" \
DESCRIPTION="Perl $PERL_VERSION available as container is a base platform for \
building and running various Perl $PERL_VERSION applications and frameworks. \
Perl is a high-level programming language with roots in C, sed, awk and shell scripting. \
Perl is good at handling processes and files, and is especially good at handling text. \
Perl's hallmarks are practicality and efficiency. While it is used to do a lot of \
different things, Perl's most common applications are system administration utilities \
and web programming."
LABEL summary="$SUMMARY" \
description="$DESCRIPTION" \
io.k8s.description="$DESCRIPTION" \
io.k8s.display-name="Apache 2.4 with mod_perl/$PERL_VERSION" \
io.openshift.expose-services="8080:http" \
io.openshift.tags="builder,perl,perl524" \
io.openshift.s2i.scripts-url=image:///usr/local/s2i
io.openshift.tags="builder,${NAME},${NAME}${PERL_SHORT_VER}" \
io.openshift.s2i.scripts-url="image:///usr/libexec/s2i" \
io.s2i.scripts-url="image:///usr/libexec/s2i" \
name="$FGC/$NAME" \
com.redhat.component="$NAME" \
version="$VERSION" \
maintainer="SoftwareCollections.org <sclorg@redhat.com>" \
help="For more information visit https://github.com/sclorg/s2i-${NAME}-container" \
usage="s2i build <SOURCE-REPOSITORY> centos/${NAME}-${PERL_SHORT_VER}-centos7:latest <APP-NAME>"
# Perl and build tools install + user addition
RUN INSTALL_PKGS="perl perl-devel mod_perl perl-CPAN perl-App-cpanminus httpd python2" && \
dnf install -y --setopt=tsflags=nodocs $INSTALL_PKGS && \
rpm -V $INSTALL_PKGS && \
dnf clean all
RUN BUILD_TOOlS="bsdtar \
findutils \
gcc \
make \
gettext \
tar \
wget \
python " && \
dnf install -y --setopt=tsflags=nodocs perl perl-devel mod_perl cpan cpanminus httpd \
$BUILD_TOOlS && \
dnf clean all && \
mkdir -p /opt/app-root/src/ && \
useradd -u 1001 -r -g 0 -d /opt/app-root/src -s /sbin/nologin \
-c "Default Application User" default && \
chown -R 1001:0 /opt/app-root
# Copy the S2I scripts from the specific language image to $STI_SCRIPTS_PATH
COPY ./s2i/bin/ $STI_SCRIPTS_PATH
# Copy s2i files
COPY ./s2i/bin/ /usr/local/s2i
# Copy extra files to the image.
COPY ./root/ /
# Copy apache configuration
COPY ./contrib/ /opt/app-root
# In order to drop the root user, we have to make some directories world
# writeable as OpenShift default security model is to run the container under
# random UID.
RUN mkdir -p ${APP_ROOT}/etc/httpd.d && \
sed -i -f ${APP_ROOT}/etc/httpdconf-fed.sed /etc/httpd/conf/httpd.conf && \
chmod -R og+rwx /var/run/httpd ${APP_ROOT}/etc/httpd.d && \
chown -R 1001:0 ${APP_ROOT} && chmod -R ug+rwx ${APP_ROOT} && \
rpm-file-permissions
# Fedora uses by default 'event' MPM module
# switch to 'prefork' to provide same user experience as with RHEL7 image
# Code taken from 'config_mpm()' function in sclorg/httpd-container repo
ENV HTTPD_MPM=prefork \
HTTPD_MAIN_CONF_MODULES_D_PATH=/etc/httpd/conf.modules.d
RUN if [ -v HTTPD_MPM -a -f ${HTTPD_MAIN_CONF_MODULES_D_PATH}/00-mpm.conf ]; then \
mpmconf=${HTTPD_MAIN_CONF_MODULES_D_PATH}/00-mpm.conf; \
sed -i -e 's,^LoadModule,#LoadModule,' ${mpmconf}; \
sed -i -e "/LoadModule mpm_${HTTPD_MPM}/s,^#LoadModule,LoadModule," ${mpmconf}; \
echo "---> Set MPM to ${HTTPD_MPM} in ${mpmconf}"; \
fi
# 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 1001:0 /opt/app-root && chmod -R ug+rwx /opt/app-root
USER 1001
# 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
# Set the default CMD to print the usage of the language image
CMD $STI_SCRIPTS_PATH/usage

1
Dockerfile.fedora Symbolic link
View file

@ -0,0 +1 @@
Dockerfile

128
README.md
View file

@ -1,79 +1,52 @@
# 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). Please note that this solution uses httpd 2.4 to display results.
Perl 5.26 container image
=================
# 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:
This container image includes Perl 5.26 as a [S2I](https://github.com/openshift/source-to-image) base image for your Perl 5.26 applications.
Users can choose between RHEL and CentOS based builder images.
The RHEL image is available in the [Red Hat Container Catalog](https://access.redhat.com/containers/#/registry.access.redhat.com/rhscl/perl-526-rhel7)
as registry.access.redhat.com/rhscl/perl-526-rhel7.
The CentOS image is then available on [Docker Hub](https://hub.docker.com/r/centos/perl-526-centos7/)
as centos/perl-526-centos7.
The resulting image can be run using [Docker](http://docker.io).
Description
-----------
Perl 5.26 available as container is a base platform for
building and running various Perl 5.26 applications and frameworks.
Perl is a high-level programming language with roots in C, sed, awk and shell scripting.
Perl is good at handling processes and files, and is especially good at handling text.
Perl's hallmarks are practicality and efficiency. While it is used to do a lot of
different things, Perl's most common applications are system administration utilities
and web programming.
This container image includes an npm utility, so users can use it to install JavaScript
modules for their web applications. There is no guarantee for any specific npm or nodejs
version, that is included in the image; those versions can be changed anytime and
the nodejs itself is included just to make the npm work.
Usage
---------------------
To build a simple [perl-sample-app](https://github.com/sclorg/s2i-perl-container/tree/master/5.26/test/sample-test-app) application,
using standalone [S2I](https://github.com/openshift/source-to-image) and then run the
resulting image with [Docker](http://docker.io) execute:
* **For RHEL based image**
```
$ s2i build https://github.com/sclorg/s2i-perl-container.git --context-dir=5.26/test/sample-test-app/ rhscl/perl-526-rhel7 perl-sample-app
$ docker run -p 8080:8080 perl-sample-app
```
* **For CentOS based image**
```
$ s2i build https://github.com/sclorg/s2i-perl-container.git --context-dir=5.26/test/sample-test-app/ centos/perl-526-centos7 perl-sample-app
$ docker run -p 8080:8080 perl-sample-app
```
**Accessing the application:**
```
$ 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
$ curl 127.0.0.1:8080
```
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
---------------------
@ -121,4 +94,11 @@ file inside your source code repository.
* **PSGI_URI_PATH**
This variable overrides location URI path that is handled path the PSGI
application. Default value is "/".
application. Default value is "/".
See also
--------
Dockerfile and other sources are available on https://github.com/sclorg/s2i-perl-container.
In that repository you also can find another versions of Perl environment Dockerfiles.
Dockerfile for CentOS is called Dockerfile, Dockerfile for RHEL is called Dockerfile.rhel7.

View file

@ -1,92 +0,0 @@
#!/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))

View file

@ -1,12 +0,0 @@
#!/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 {} +

1
help.md Symbolic link
View file

@ -0,0 +1 @@
README.md

View file

@ -1,130 +0,0 @@
apiVersion: v1
kind: Template
metadata:
labels:
description: Perl 5 is a highly capable, feature-rich programming language with
over 29 years of development. Perl 5 runs on over 100 platforms.
tags: builder,perl,perl524
template: perl
iconClass: icon-docker
name: perl
objects:
- kind : ImageStream
apiVersion : v1
metadata :
name : ${APP_NAME}
labels :
appid : perl-s2i-${APP_NAME}
- kind : ImageStream
apiVersion : v1
metadata :
name : ${APP_NAME}-s2i
labels :
appid : perl-s2i-${APP_NAME}
spec:
tags:
- name: latest
from:
kind: DockerImage
name: koscicz/perl
- kind : BuildConfig
apiVersion : v1
metadata :
name : ${APP_NAME}
labels :
appid : perl-s2i-${APP_NAME}
spec :
triggers :
- type : ConfigChange
- type : ImageChange
- type : GitHub
github:
secret: secret42
source :
type : Git
git :
uri : ${SOURCE_REPOSITORY}
contextDir : ${SOURCE_REPOSITORY}
strategy :
type : Source
sourceStrategy :
from :
kind : ImageStreamTag
name : ${APP_NAME}-s2i:latest
output :
to :
kind : ImageStreamTag
name : ${APP_NAME}:latest
- apiVersion: v1
kind: DeploymentConfig
metadata:
name: ${APP_NAME}
labels :
appid : perl-s2i-${APP_NAME}
spec:
strategy:
type: Rolling
triggers:
- type: ConfigChange
- type: ImageChange
imageChangeParams:
automatic: true
containerNames:
- ${APP_NAME}
from:
kind: ImageStreamTag
name: ${APP_NAME}:latest
replicas: 1
selector :
deploymentconfig : ${APP_NAME}
template:
metadata :
labels :
appid: perl-s2i-${APP_NAME}
deploymentconfig : ${APP_NAME}
spec:
containers:
- env:
- name: PERL_VERSION
value: '5.24'
name : ${APP_NAME}
image : ${APP_NAME}:latest
ports:
- containerPort: 8080
- kind : Service
apiVersion : v1
metadata :
name : ${APP_NAME}
labels :
appid : perl-s2i-${APP_NAME}
spec :
ports :
- name: 8080-tcp
protocol : TCP
port : 8080
targetPort : 8080
selector :
deploymentconfig : ${APP_NAME}
- kind : Route
apiVersion : v1
metadata :
name : ${APP_NAME}
labels :
appid : perl-s2i-${APP_NAME}
spec :
host :
to :
kind : Service
name : ${APP_NAME}
weight : 100
port :
targetPort : 8080-tcp
parameters :
- name : APP_NAME
description : Name of application
value :
required : true
- name : SOURCE_REPOSITORY
description : Git source repository
value :
required : true

View file

@ -1,102 +1,148 @@
.TH "perl" "1" "" "Jan Koscielniak \<jkosciel@redhat.com\>" "DATE 11.04.2017" ""
.BR perl (1)
\-\- S2I builder of perl applications
.SH USAGE
.TH Perl 5.26 container image
.PP
To pull the perl container run:
This container image includes Perl 5.26 as a S2I
\[la]https://github.com/openshift/source-to-image\[ra] base image for your Perl 5.26 applications.
Users can choose between RHEL and CentOS based builder images.
The RHEL image is available in the Red Hat Container Catalog
\[la]https://access.redhat.com/containers/#/registry.access.redhat.com/rhscl/perl-526-rhel7\[ra]
as registry.access.redhat.com/rhscl/perl\-526\-rhel7.
The CentOS image is then available on Docker Hub
\[la]https://hub.docker.com/r/centos/perl-526-centos7/\[ra]
as centos/perl\-526\-centos7.
The resulting image can be run using Docker
\[la]http://docker.io\[ra]\&.
.SH Description
.PP
Perl 5.26 available as container is a base platform for
building and running various Perl 5.26 applications and frameworks.
Perl is a high\-level programming language with roots in C, sed, awk and shell scripting.
Perl is good at handling processes and files, and is especially good at handling text.
Perl's hallmarks are practicality and efficiency. While it is used to do a lot of
different things, Perl's most common applications are system administration utilities
and web programming.
.PP
This container image includes an npm utility, so users can use it to install JavaScript
modules for their web applications. There is no guarantee for any specific npm or nodejs
version, that is included in the image; those versions can be changed anytime and
the nodejs itself is included just to make the npm work.
.SH Usage
.PP
To build a simple perl\-sample\-app
\[la]https://github.com/sclorg/s2i-perl-container/tree/master/5.26/test/sample-test-app\[ra] application,
using standalone S2I
\[la]https://github.com/openshift/source-to-image\[ra] and then run the
resulting image with Docker
\[la]http://docker.io\[ra] execute:
.IP \(bu 2
.PP
\fBFor RHEL based image\fP
.PP
.RS
.nf
# docker pull koscicz/perl
$ s2i build https://github.com/sclorg/s2i\-perl\-container.git \-\-context\-dir=5.26/test/sample\-test\-app/ rhscl/perl\-526\-rhel7 perl\-sample\-app
$ docker run \-p 8080:8080 perl\-sample\-app
.fi
.RE
.IP \(bu 2
.PP
To build your perl application use run:
\fBFor CentOS based image\fP
.PP
.RS
.nf
# s2i build <SOURCE\-REPOSITORY> koscicz/perl <NAME\-OF\-APP>
$ s2i build https://github.com/sclorg/s2i\-perl\-container.git \-\-context\-dir=5.26/test/sample\-test\-app/ centos/perl\-526\-centos7 perl\-sample\-app
$ docker run \-p 8080:8080 perl\-sample\-app
.fi
.RE
.PP
To run your application in docker container:
\fBAccessing the application:\fP
.PP
.RS
.nf
# docker run \-p 8080:8080 <NAME\-OF\-APP>
$ curl 127.0.0.1:8080
.fi
.RE
.SH ENVIROMENT VARIABLES
.SH Environment 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.
.IP \(bu 2
.PP
ENABLE_CPAN_TEST
\fBENABLE\_CPAN\_TEST\fP
.PP
.RS
.nf
Allow the installation of all specified cpan packages and the running of their tests. The default value is `false`.
.fi
.RE
Allow the installation of all specified cpan packages and the running of their tests. The default value is \fB\fCfalse\fR\&.
.IP \(bu 2
.PP
CPAN_MIRROR
\fBCPAN\_MIRROR\fP
.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
.IP \(bu 2
.PP
PERL_APACHE2_RELOAD
\fBPERL\_APACHE2\_RELOAD\fP
.PP
.RS
.nf
Set this to "true" to enable automatic reloading of modified Perl modules.
.fi
.RE
.IP \(bu 2
.PP
HTTPD_START_SERVERS
\fBHTTPD\_START\_SERVERS\fP
.PP
.RS
.nf
The [StartServers](https://httpd.apache.org/docs/2.4/mod/mpm_common.html#startservers)
The StartServers
\[la]https://httpd.apache.org/docs/2.4/mod/mpm_common.html#startservers\[ra]
directive sets the number of child server processes created on startup. Default is 8.
.fi
.RE
.IP \(bu 2
.PP
HTTPD_MAX_REQUEST_WORKERS
\fBHTTPD\_MAX\_REQUEST\_WORKERS\fP
.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
.IP \(bu 2
.PP
PSGI_FILE
\fBPSGI\_FILE\fP
.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.
.PP
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
.PP
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
.PP
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
.IP \(bu 2
.PP
PSGI_URI_PATH
\fBPSGI\_URI\_PATH\fP
.PP
.RS
.nf
This variable overrides location URI path that is handled path the PSGI
application. Default value is "/".
.fi
.RE
.SH See also
.PP
Dockerfile and other sources are available on
\[la]https://github.com/sclorg/s2i-perl-container\[ra]\&.
In that repository you also can find another versions of Perl environment Dockerfiles.
Dockerfile for CentOS is called Dockerfile, Dockerfile for RHEL is called Dockerfile.rhel7.

View file

@ -1,63 +0,0 @@
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 <SOURCE-REPOSITORY> koscicz/perl <NAME-OF-APP>
To run your application in docker container:
# docker run -p 8080:8080 <NAME-OF-APP>
## 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 "/".

View file

@ -1,4 +1,3 @@
LoadModule perl_module modules/mod_perl.so
DirectoryIndex index.pl
@ -15,4 +14,4 @@ PerlSwitches -I./extlib/lib/perl5
Require all granted
</Directory>
IncludeOptional /opt/app-root/etc/httpd.d/*.conf
IncludeOptional /opt/app-root/etc/httpd.d/*.conf

View file

@ -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 "/opt/rh/httpd24/root/var/www/html"%DocumentRoot "/opt/app-root/src"%
s%^<Directory "/opt/rh/httpd24/root/var/html"%<Directory "/opt/app-root/src"%
s%^ErrorLog "logs/error_log"%ErrorLog "|/usr/bin/cat"%
s%CustomLog "logs/access_log"%CustomLog "|/usr/bin/cat"%

View file

@ -0,0 +1,6 @@
# IMPORTANT: Do not add more content to this file unless you know what you are
# doing. This file is sourced everytime the shell session is opened.
#
# This will make scl collection binaries work out of box.
unset BASH_ENV PROMPT_COMMAND ENV
source scl_source enable httpd24 rh-perl526 $NODEJS_SCL

View file

@ -73,4 +73,4 @@ else
fi
# Fix source directory permissions
fix-permissions ./
fix-permissions ./

View file

@ -43,4 +43,4 @@ 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
exec httpd -C 'Include /opt/app-root/etc/httpd.conf' -D FOREGROUND

View file

@ -1,11 +1,17 @@
#!/bin/sh
DISTRO=`cat /etc/*-release | grep ^ID= | grep -Po '".*?"' | tr -d '"'`
NAMESPACE=centos
[[ $DISTRO =~ rhel* ]] && NAMESPACE=rhscl
cat <<EOF
This is a S2I fedora base image:
This is a S2I ${IMAGE_DESCRIPTION} ${DISTRO} base image:
To use it, install S2I: https://github.com/openshift/source-to-image
Sample invocation:
s2i build https://github.com/container-images/perl.git --context-dir=test/sample-test-app/ perl-524 perl-sample-app
s2i build https://github.com/sclorg/s2i-perl-container.git --context-dir=5.26/test/sample-test-app/ ${NAMESPACE}/perl-526-${DISTRO}7 perl-sample-app
You can then run the resulting image via:
docker run -p 8080:8080 perl-sample-app
EOF
EOF

View file

1
test/binpath/cpanfile Normal file
View file

@ -0,0 +1 @@
requires 'Net::IP';

7
test/binpath/index.pl Executable file
View file

@ -0,0 +1,7 @@
#!/usr/bin/perl
print "Content-type: text/plain\n\n";
my $output = `ipcount 2>&1`;
if (!defined $output) {
die "No output from `ipcount' command";
}
print $output;

View file

@ -0,0 +1 @@
die "This should not be picked up.";

View file

@ -0,0 +1,3 @@
#!/usr/bin/env plackup
use Web::Paste::Simple;
Web::Paste::Simple->new->app;

View file

@ -0,0 +1 @@
requires 'Web::Paste::Simple';

3
test/psgi/application.psgi Executable file
View file

@ -0,0 +1,3 @@
#!/usr/bin/env plackup
use Web::Paste::Simple;
Web::Paste::Simple->new->app;

1
test/psgi/cpanfile Normal file
View file

@ -0,0 +1 @@
requires 'Web::Paste::Simple';

314
test/run Executable file
View file

@ -0,0 +1,314 @@
#!/bin/bash
#
# The 'run' performs a simple test that verifies that S2I image.
# The main focus here is to exercise the S2I scripts.
#
# IMAGE_NAME specifies a name of the candidate image used for testing.
# The image has to be available before this script is executed.
#
IMAGE_NAME=${IMAGE_NAME-centos/perl-526-centos-candidate}
# TODO: Make command compatible for Mac users
test_dir="$(readlink -zf $(dirname "${BASH_SOURCE[0]}"))"
image_dir=$(readlink -zf ${test_dir}/..)
source "${test_dir}/test-lib.sh"
# TODO: This should be part of the image metadata
test_port=8080
info() {
echo -e "\n\e[1m[INFO] $@...\e[0m\n"
}
image_exists() {
docker inspect $1 &>/dev/null
}
container_exists() {
image_exists $(cat $cid_file)
}
container_ip() {
docker inspect --format="{{ .NetworkSettings.IPAddress }}" $(cat $cid_file)
}
run_s2i_build() {
ct_s2i_build_as_df file://${test_dir}/${test_name} ${IMAGE_NAME} ${IMAGE_NAME}-testapp "$@"
}
prepare() {
if ! image_exists ${IMAGE_NAME}; then
echo "ERROR: The image ${IMAGE_NAME} must exist before this script is executed."
exit 1
fi
# TODO: S2I build require the application is a valid 'GIT' repository, we
# should remove this restriction in the future when a file:// is used.
info "Build the test application image"
pushd ${test_dir}/${test_name} >/dev/null
git init
git config user.email "build@localhost" && git config user.name "builder"
git add -A && git commit -m "Sample commit"
popd >/dev/null
}
run_test_application() {
docker run --user=100001 --rm --cidfile=${cid_file} ${IMAGE_NAME}-testapp
}
cleanup_test_app() {
info "Cleaning up the test application"
if [ -f $cid_file ]; then
if container_exists; then
docker stop $(cat $cid_file)
docker rm $(cat $cid_file)
fi
rm $cid_file
fi
}
cleanup() {
info "Cleaning up the test application image"
if image_exists ${IMAGE_NAME}-testapp; then
docker rmi -f ${IMAGE_NAME}-testapp
fi
rm -rf ${test_dir}/${test_name}/.git
}
check_result() {
local result="$1"
if [[ "$result" != "0" ]]; then
info "TEST FAILED (${result})"
cleanup
exit $result
fi
}
wait_for_cid() {
local max_attempts=10
local sleep_time=1
local attempt=1
local result=1
info "Waiting for application container to start"
while [ $attempt -le $max_attempts ]; do
[ -f $cid_file ] && [ -s $cid_file ] && break
attempt=$(( $attempt + 1 ))
sleep $sleep_time
done
}
test_s2i_usage() {
info "Testing 's2i usage'"
ct_s2i_usage ${IMAGE_NAME} ${s2i_args} &>/dev/null
}
test_docker_run_usage() {
info "Testing 'docker run' usage"
docker run ${IMAGE_NAME} &>/dev/null
}
test_scl_usage() {
local run_cmd="$1"
local expected="$2"
info "Testing the image SCL enable"
out=$(docker run --rm ${IMAGE_NAME} /bin/bash -c "${run_cmd}")
if ! echo "${out}" | grep -q "${expected}"; then
echo "ERROR[/bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'"
return 1
fi
out=$(docker exec $(cat ${cid_file}) /bin/bash -c "${run_cmd}" 2>&1)
if ! echo "${out}" | grep -q "${expected}"; then
echo "ERROR[exec /bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'"
return 1
fi
out=$(docker exec $(cat ${cid_file}) /bin/sh -ic "${run_cmd}" 2>&1)
if ! echo "${out}" | grep -q "${expected}"; then
echo "ERROR[exec /bin/sh -ic "${run_cmd}"] Expected '${expected}', got '${out}'"
return 1
fi
}
# Perform GET request to the application container.
# First argument is request URI path.
# Second argument is expected HTTP response code.
# Third argument is PCRE regular expression that must match the response body.
test_response() {
local uri_path="$1"
local expected_code="$2"
local body_regexp="$3"
local url="http://$(container_ip):${test_port}${uri_path}"
info "Testing the HTTP response for <${url}>"
local max_attempts=10
local sleep_time=1
local attempt=1
local result=1
while [ $attempt -le $max_attempts ]; do
response=$(curl -s -w '%{http_code}' "${url}")
status=$?
if [ $status -eq 0 ]; then
response_code=$(printf '%s' "$response" | tail -c 3)
response_body=$(printf '%s' "$response" | head -c -3)
if [ "$response_code" -eq "$expected_code" ]; then
result=0
fi
printf '%s' "$response_body" | grep -qP -e "$body_regexp" || result=1;
break
fi
attempt=$(( $attempt + 1 ))
sleep $sleep_time
done
return $result
}
# Match PCRE regular expression against container standard output.
# First argument is the PCRE regular expression.
# It expects standard output in ${tmp_dir}/out file.
test_stdout() {
local regexp="$1"
local output="${tmp_dir}/out"
info "Testing the container standard output for /${regexp}/"
grep -qP -e "$regexp" "$output";
}
# Match PCRE regular expression against container standard error output.
# First argument is the PCRE regular expression.
# It expects error output in ${tmp_dir}/err file.
test_stderr() {
local regexp="$1"
local output="${tmp_dir}/err"
info "Testing the container error output for /${regexp}/"
grep -qP -e "$regexp" "$output";
}
test_connection() {
test_response '/' 200 ''
}
test_application() {
# Verify that the HTTP connection can be established to test application container
run_test_application &
# Wait for the container to write it's CID file
wait_for_cid
test_scl_usage "perl --version" "v5.26."
check_result $?
test_connection
check_result $?
cleanup_test_app
}
# Build application, run it, perform test function, clean up.
# First argument is directory name.
# Second argument is function that expects running application. The function
# must return (or terminate with) non-zero value to report an failure,
# 0 otherwise.
# Other arguments are additional s2i options, like --env=FOO=bar.
# The test function have available container ID in $cid_file, container output
# in ${tmp_dir}/out, container stderr in ${tmp_dir}/err.
do_test() {
test_name="$1"
test_function="$2"
shift 2
info "Starting tests for ${test_name}."
tmp_dir=$(mktemp -d)
check_result $?
cid_file="${tmp_dir}/cid"
s2i_args="--pull-policy=never"
# Build and run the test application
prepare
run_s2i_build $s2i_args "$@"
check_result $?
run_test_application >"${tmp_dir}/out" 2>"${tmp_dir}/err" &
wait_for_cid
# Perform user-supplied test function
$test_function;
check_result $?
# Terminate the test application and clean up
cleanup_test_app
cleanup
rm -rf "$tmp_dir"
info "All tests for the ${test_name} finished successfully."
}
# List of tests to execute:
# This is original test that does more things like s2i API checks. Other tests
# executed by do_test() will not repeat these checks.
test_1() {
test_name='sample-test-app'
info "Starting tests for ${test_name}"
cid_file=$(mktemp -u --suffix=.cid)
# Since we built the candidate image locally, we don't want S2I attempt to pull
# it from Docker hub
s2i_args="--pull-policy=never"
prepare
run_s2i_build $s2i_args
check_result $?
# Verify the 'usage' script is working properly when running the base image with 's2i usage ...'
test_s2i_usage
check_result $?
# Verify the 'usage' script is working properly when running the base image with 'docker run ...'
test_docker_run_usage
check_result $?
# Test application with default UID
test_application
# Test application with random UID
CONTAINER_ARGS="-u 12345" test_application
info "All tests for the ${test_name} finished successfully."
cleanup
}
test_1
# Check scripts installed from CPAN are available to the application.
test_2_function() {
test_response '/' 200 'Usage'
}
do_test 'binpath' 'test_2_function'
# Check a single PSGI application is recognized a mod_perl is autoconfigured.
test_3_function() {
test_response '/' 200 '<title>Web::Paste::Simple'
}
do_test 'psgi' 'test_3_function'
# Check variables can select a PSGI application and set URI path.
test_4_function() {
test_response '/path' 200 '<title>Web::Paste::Simple' && \
test_response '/cpanfile' 200 'requires'
}
do_test 'psgi-variables' 'test_4_function' \
'--env=PSGI_FILE=./application2.psgi' '--env=PSGI_URI_PATH=/path'
# Check httpd access_log flows to stdout, error_log to stdout.
# TODO: send error_log to stderr after dropping support for broken
# docker < 1.9.
test_5_function() {
test_response '/' 200 'Text in HTTP body' && \
test_stdout '"GET /[^"]*" 200 ' && \
test_stdout 'Warning on stderr'
}
do_test 'warningonstderr' 'test_5_function'
echo "Testing npm availibility"
ct_npm_works
check_result $?
info "All tests finished successfully."

33
test/run-openshift Executable file
View file

@ -0,0 +1,33 @@
#!/bin/bash
#
# Test the Perl image in OpenShift.
#
# IMAGE_NAME specifies a name of the candidate image used for testing.
# The image has to be available before this script is executed.
#
THISDIR=$(dirname ${BASH_SOURCE[0]})
source ${THISDIR}/test-lib.sh
source ${THISDIR}/test-lib-openshift.sh
set -exo nounset
test -n "${IMAGE_NAME-}" || false 'make sure $IMAGE_NAME is defined'
test -n "${VERSION-}" || false 'make sure $VERSION is defined'
ct_os_cluster_up
# TODO: We should ideally use a local directory instead of ${VERSION}/test/sample-test-app,
# so we can test changes in that example app that are done as part of the PR
ct_os_test_s2i_app ${IMAGE_NAME} "https://github.com/sclorg/s2i-perl-container.git" ${VERSION}/test/sample-test-app "Everything is OK"
ct_os_test_s2i_app ${IMAGE_NAME} "https://github.com/sclorg/dancer-ex.git" . 'Welcome to your Dancer application on OpenShift'
# TODO: this was not working because the referenced example dir was added as part of this commit
ct_os_test_template_app ${IMAGE_NAME} \
${THISDIR}/sample-test-app.json \
perl \
"Everything is OK" \
8080 http 200 "-p SOURCE_REPOSITORY_REF=staging -p VERSION=${VERSION} -p NAME=perl-testing"

285
test/sample-test-app.json Normal file
View file

@ -0,0 +1,285 @@
{
"kind": "Template",
"apiVersion": "v1",
"metadata": {
"name": "sample-test-app",
"annotations": {
"openshift.io/display-name": "Perl Sample Application",
"description": "An example Perl application that serves static content. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/s2i-perl-container/blob/master/examples/README.md.",
"tags": "quickstart,perl",
"iconClass": "icon-perl",
"openshift.io/long-description": "This template defines resources needed to develop a simple application served by Perl, including a package Math::Round installation from cpan.",
"openshift.io/provider-display-name": "Red Hat, Inc.",
"openshift.io/documentation-url": "https://github.com/sclorg/s2i-perl-container",
"openshift.io/support-url": "https://access.redhat.com"
}
},
"message": "The following service(s) have been created in your project: ${NAME}.\n\nFor more information about using this template, including OpenShift considerations, see https://github.com/sclorg/s2i-perl-container/blob/master/examples/README.md.",
"labels": {
"template": "sample-test-app"
},
"objects": [
{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "${NAME}",
"annotations": {
"description": "Exposes and load balances the application pods"
}
},
"spec": {
"ports": [
{
"name": "web",
"port": 8080,
"targetPort": 8080
}
],
"selector": {
"name": "${NAME}"
}
}
},
{
"kind": "Route",
"apiVersion": "v1",
"metadata": {
"name": "${NAME}",
"annotations": {
"template.openshift.io/expose-uri": "http://{.spec.host}{.spec.path}"
}
},
"spec": {
"host": "${APPLICATION_DOMAIN}",
"to": {
"kind": "Service",
"name": "${NAME}"
}
}
},
{
"kind": "ImageStream",
"apiVersion": "v1",
"metadata": {
"name": "${NAME}",
"annotations": {
"description": "Keeps track of changes in the application image"
}
}
},
{
"kind": "BuildConfig",
"apiVersion": "v1",
"metadata": {
"name": "${NAME}",
"annotations": {
"description": "Defines how to build the application",
"template.alpha.openshift.io/wait-for-ready": "true"
}
},
"spec": {
"source": {
"type": "Git",
"git": {
"uri": "${SOURCE_REPOSITORY_URL}",
"ref": "${SOURCE_REPOSITORY_REF}"
},
"contextDir": "${CONTEXT_DIR}"
},
"strategy": {
"type": "Source",
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
"namespace": "${NAMESPACE}",
"name": "perl:${VERSION}"
}
}
},
"output": {
"to": {
"kind": "ImageStreamTag",
"name": "${NAME}:latest"
}
},
"triggers": [
{
"type": "ImageChange"
},
{
"type": "ConfigChange"
},
{
"type": "GitHub",
"github": {
"secret": "${GITHUB_WEBHOOK_SECRET}"
}
},
{
"type": "Generic",
"generic": {
"secret": "${GENERIC_WEBHOOK_SECRET}"
}
}
]
}
},
{
"kind": "DeploymentConfig",
"apiVersion": "v1",
"metadata": {
"name": "${NAME}",
"annotations": {
"description": "Defines how to deploy the application server",
"template.alpha.openshift.io/wait-for-ready": "true"
}
},
"spec": {
"strategy": {
"type": "Rolling"
},
"triggers": [
{
"type": "ImageChange",
"imageChangeParams": {
"automatic": true,
"containerNames": [
"nginx-example"
],
"from": {
"kind": "ImageStreamTag",
"name": "${NAME}:latest"
}
}
},
{
"type": "ConfigChange"
}
],
"replicas": 1,
"selector": {
"name": "${NAME}"
},
"template": {
"metadata": {
"name": "${NAME}",
"labels": {
"name": "${NAME}"
}
},
"spec": {
"containers": [
{
"name": "nginx-example",
"image": " ",
"ports": [
{
"containerPort": 8080
}
],
"readinessProbe": {
"timeoutSeconds": 3,
"initialDelaySeconds": 3,
"httpGet": {
"path": "/",
"port": 8080
}
},
"livenessProbe": {
"timeoutSeconds": 3,
"initialDelaySeconds": 30,
"httpGet": {
"path": "/",
"port": 8080
}
},
"resources": {
"limits": {
"memory": "${MEMORY_LIMIT}"
}
},
"env": [
],
"resources": {
"limits": {
"memory": "${MEMORY_LIMIT}"
}
}
}
]
}
}
}
}
],
"parameters": [
{
"name": "NAME",
"displayName": "Name",
"description": "The name assigned to all of the frontend objects defined in this template.",
"required": true,
"value": "sample-test-app"
},
{
"name": "VERSION",
"displayName": "Version",
"description": "The version of the image.",
"required": true,
"value": "5.24"
},
{
"name": "NAMESPACE",
"displayName": "Namespace",
"description": "The OpenShift Namespace where the ImageStream resides.",
"required": true,
"value": "centos"
},
{
"name": "MEMORY_LIMIT",
"displayName": "Memory Limit",
"description": "Maximum amount of memory the container can use.",
"required": true,
"value": "512Mi"
},
{
"name": "SOURCE_REPOSITORY_URL",
"displayName": "Git Repository URL",
"description": "The URL of the repository with your application source code.",
"required": true,
"value": "https://github.com/sclorg/s2i-perl-container.git"
},
{
"name": "SOURCE_REPOSITORY_REF",
"displayName": "Git Reference",
"description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch."
},
{
"name": "CONTEXT_DIR",
"displayName": "Context Directory",
"description": "Set this to the relative path to your project if it is not in the root of your repository.",
"value": "examples/sample-test-app"
},
{
"name": "APPLICATION_DOMAIN",
"displayName": "Application Hostname",
"description": "The exposed hostname that will route to the perl service, if left blank a value will be defaulted.",
"value": ""
},
{
"name": "GITHUB_WEBHOOK_SECRET",
"displayName": "GitHub Webhook Secret",
"description": "Github trigger secret. A difficult to guess string encoded as part of the webhook URL. Not encrypted.",
"generate": "expression",
"from": "[a-zA-Z0-9]{40}"
},
{
"name": "GENERIC_WEBHOOK_SECRET",
"displayName": "Generic Webhook Secret",
"description": "A secret string used to configure the Generic webhook.",
"generate": "expression",
"from": "[a-zA-Z0-9]{40}"
}
]
}

View file

@ -0,0 +1 @@
requires 'Math::Round', '== 0.06';

16
test/sample-test-app/index.pl Executable file
View file

@ -0,0 +1,16 @@
#!/usr/bin/perl
use Math::Round;
round(10.2) == 10 || die "round(10.2) != 10";
print "Content-type: text/html\n\n";
print <<EOF
<html>
<head><title>Everything is OK</title></head>
<body>
Everything is fine.
</body>
</html>
EOF
;

925
test/test-lib-openshift.sh Normal file
View file

@ -0,0 +1,925 @@
# Set of functions for testing docker images in OpenShift using 'oc' command
# ct_os_get_status
# --------------------
# Returns status of all objects to make debugging easier.
function ct_os_get_status() {
oc get all
oc status
}
# ct_os_print_logs
# --------------------
# Returns status of all objects and logs from all pods.
function ct_os_print_logs() {
ct_os_get_status
while read pod_name; do
echo "INFO: printing logs for pod ${pod_name}"
oc logs ${pod_name}
done < <(oc get pods --no-headers=true -o custom-columns=NAME:.metadata.name)
}
# ct_os_enable_print_logs
# --------------------
# Enables automatic printing of pod logs on ERR.
function ct_os_enable_print_logs() {
set -E
trap ct_os_print_logs ERR
}
# ct_get_public_ip
# --------------------
# Returns best guess for the IP that the node is accessible from other computers.
# This is a bit funny heuristic, simply goes through all IPv4 addresses that
# hostname -I returns and de-prioritizes IP addresses commonly used for local
# addressing. The rest of addresses are taken as public with higher probability.
function ct_get_public_ip() {
local hostnames=$(hostname -I)
local public_ip=''
local found_ip
for guess_exp in '127\.0\.0\.1' '192\.168\.[0-9\.]*' '172\.[0-9\.]*' \
'10\.[0-9\.]*' '[0-9\.]*' ; do
found_ip=$(echo "${hostnames}" | grep -oe "${guess_exp}")
if [ -n "${found_ip}" ] ; then
hostnames=$(echo "${hostnames}" | sed -e "s/${found_ip}//")
public_ip="${found_ip}"
fi
done
if [ -z "${public_ip}" ] ; then
echo "ERROR: public IP could not be guessed." >&2
return 1
fi
echo "${public_ip}"
}
# ct_os_run_in_pod POD_NAME CMD
# --------------------
# Runs [cmd] in the pod specified by prefix [pod_prefix].
# Arguments: pod_name - full name of the pod
# Arguments: cmd - command to be run in the pod
function ct_os_run_in_pod() {
local pod_name="$1" ; shift
oc exec "$pod_name" -- "$@"
}
# ct_os_get_service_ip SERVICE_NAME
# --------------------
# Returns IP of the service specified by [service_name].
# Arguments: service_name - name of the service
function ct_os_get_service_ip() {
local service_name="${1}" ; shift
oc get "svc/${service_name}" -o yaml | grep clusterIP | \
cut -d':' -f2 | grep -oe '172\.30\.[0-9\.]*'
}
# ct_os_get_all_pods_status
# --------------------
# Returns status of all pods.
function ct_os_get_all_pods_status() {
oc get pods -o custom-columns=Ready:status.containerStatuses[0].ready,NAME:.metadata.name
}
# ct_os_get_all_pods_name
# --------------------
# Returns the full name of all pods.
function ct_os_get_all_pods_name() {
oc get pods --no-headers -o custom-columns=NAME:.metadata.name
}
# ct_os_get_pod_status POD_PREFIX
# --------------------
# Returns status of the pod specified by prefix [pod_prefix].
# Note: Ignores -build and -deploy pods
# Arguments: pod_prefix - prefix or whole ID of the pod
function ct_os_get_pod_status() {
local pod_prefix="${1}" ; shift
ct_os_get_all_pods_status | grep -e "${pod_prefix}" | grep -Ev "(build|deploy)$" \
| awk '{print $1}' | head -n 1
}
# ct_os_get_pod_name POD_PREFIX
# --------------------
# Returns the full name of pods specified by prefix [pod_prefix].
# Note: Ignores -build and -deploy pods
# Arguments: pod_prefix - prefix or whole ID of the pod
function ct_os_get_pod_name() {
local pod_prefix="${1}" ; shift
ct_os_get_all_pods_name | grep -e "^${pod_prefix}" | grep -Ev "(build|deploy)$"
}
# ct_os_get_pod_ip POD_NAME
# --------------------
# Returns the ip of the pod specified by [pod_name].
# Arguments: pod_name - full name of the pod
function ct_os_get_pod_ip() {
local pod_name="${1}"
oc get pod "$pod_name" --no-headers -o custom-columns=IP:status.podIP
}
# ct_os_check_pod_readiness POD_PREFIX STATUS
# --------------------
# Checks whether the pod is ready.
# Arguments: pod_prefix - prefix or whole ID of the pod
# Arguments: status - expected status (true, false)
function ct_os_check_pod_readiness() {
local pod_prefix="${1}" ; shift
local status="${1}" ; shift
test "$(ct_os_get_pod_status ${pod_prefix})" == "${status}"
}
# ct_os_wait_pod_ready POD_PREFIX TIMEOUT
# --------------------
# Wait maximum [timeout] for the pod becomming ready.
# Arguments: pod_prefix - prefix or whole ID of the pod
# Arguments: timeout - how many seconds to wait seconds
function ct_os_wait_pod_ready() {
local pod_prefix="${1}" ; shift
local timeout="${1}" ; shift
SECONDS=0
echo -n "Waiting for ${pod_prefix} pod becoming ready ..."
while ! ct_os_check_pod_readiness "${pod_prefix}" "true" ; do
echo -n "."
[ ${SECONDS} -gt ${timeout} ] && echo " FAIL" && return 1
sleep 3
done
echo " DONE"
}
# ct_os_wait_rc_ready POD_PREFIX TIMEOUT
# --------------------
# Wait maximum [timeout] for the rc having desired number of replicas ready.
# Arguments: pod_prefix - prefix of the replication controller
# Arguments: timeout - how many seconds to wait seconds
function ct_os_wait_rc_ready() {
local pod_prefix="${1}" ; shift
local timeout="${1}" ; shift
SECONDS=0
echo -n "Waiting for ${pod_prefix} pod becoming ready ..."
while ! test "$((oc get --no-headers statefulsets; oc get --no-headers rc) 2>/dev/null \
| grep "^${pod_prefix}" | awk '$2==$3 {print "ready"}')" == "ready" ; do
echo -n "."
[ ${SECONDS} -gt ${timeout} ] && echo " FAIL" && return 1
sleep 3
done
echo " DONE"
}
# ct_os_deploy_pure_image IMAGE [ENV_PARAMS, ...]
# --------------------
# Runs [image] in the openshift and optionally specifies env_params
# as environment variables to the image.
# Arguments: image - prefix or whole ID of the pod to run the cmd in
# Arguments: env_params - environment variables parameters for the images.
function ct_os_deploy_pure_image() {
local image="${1}" ; shift
# ignore error exit code, because oc new-app returns error when image exists
oc new-app ${image} "$@" || :
# let openshift cluster to sync to avoid some race condition errors
sleep 3
}
# ct_os_deploy_s2i_image IMAGE APP [ENV_PARAMS, ... ]
# --------------------
# Runs [image] and [app] in the openshift and optionally specifies env_params
# as environment variables to the image.
# Arguments: image - prefix or whole ID of the pod to run the cmd in
# Arguments: app - url or local path to git repo with the application sources.
# Arguments: env_params - environment variables parameters for the images.
function ct_os_deploy_s2i_image() {
local image="${1}" ; shift
local app="${1}" ; shift
# ignore error exit code, because oc new-app returns error when image exists
oc new-app "${image}~${app}" "$@" || :
# let openshift cluster to sync to avoid some race condition errors
sleep 3
}
# ct_os_deploy_template_image TEMPLATE [ENV_PARAMS, ...]
# --------------------
# Runs template in the openshift and optionally gives env_params to use
# specific values in the template.
# Arguments: template - prefix or whole ID of the pod to run the cmd in
# Arguments: env_params - environment variables parameters for the template.
# Example usage: ct_os_deploy_template_image mariadb-ephemeral-template.yaml \
# DATABASE_SERVICE_NAME=mysql-57-centos7 \
# DATABASE_IMAGE=mysql-57-centos7 \
# MYSQL_USER=testu \
# MYSQL_PASSWORD=testp \
# MYSQL_DATABASE=testdb
function ct_os_deploy_template_image() {
local template="${1}" ; shift
oc process -f "${template}" "$@" | oc create -f -
# let openshift cluster to sync to avoid some race condition errors
sleep 3
}
# _ct_os_get_uniq_project_name
# --------------------
# Returns a uniq name of the OpenShift project.
function _ct_os_get_uniq_project_name() {
local r
while true ; do
r=${RANDOM}
mkdir /var/tmp/sclorg-test-${r} &>/dev/null && echo sclorg-test-${r} && break
done
}
# ct_os_new_project [PROJECT]
# --------------------
# Creates a new project in the openshfit using 'os' command.
# Arguments: project - project name, uses a new random name if omitted
# Expects 'os' command that is properly logged in to the OpenShift cluster.
# Not using mktemp, because we cannot use uppercase characters.
function ct_os_new_project() {
if [ "${CT_SKIP_NEW_PROJECT:-false}" == 'true' ] ; then
echo "Creating project skipped."
return
fi
local project_name="${1:-$(_ct_os_get_uniq_project_name)}" ; shift || :
oc new-project ${project_name}
# let openshift cluster to sync to avoid some race condition errors
sleep 3
}
# ct_os_delete_project [PROJECT]
# --------------------
# Deletes the specified project in the openshfit
# Arguments: project - project name, uses the current project if omitted
function ct_os_delete_project() {
if [ "${CT_SKIP_NEW_PROJECT:-false}" == 'true' ] ; then
echo "Deleting project skipped, cleaning objects only."
ct_delete_all_objects
return
fi
local project_name="${1:-$(oc project -q)}" ; shift || :
oc delete project "${project_name}"
}
# ct_delete_all_objects
# -----------------
# Deletes all objects within the project.
# Handy when we have one project and want to run more tests.
function ct_delete_all_objects() {
for x in bc builds dc is isimage istag po pv pvc rc routes secrets svc ; do
oc delete $x --all
done
# for some objects it takes longer to be really deleted, so a dummy sleep
# to avoid some races when other test can see not-yet-deleted objects and can fail
sleep 10
}
# ct_os_docker_login
# --------------------
# Logs in into docker daemon
# Uses global REGISRTY_ADDRESS environment variable for arbitrary registry address.
# Does not do anything if REGISTRY_ADDRESS is set.
function ct_os_docker_login() {
[ -n "${REGISTRY_ADDRESS:-}" ] && "REGISTRY_ADDRESS set, not trying to docker login." && return 0
# docker login fails with "404 page not found" error sometimes, just try it more times
for i in `seq 12` ; do
docker login -u developer -p $(oc whoami -t) ${REGISRTY_ADDRESS:-172.30.1.1:5000} && return 0 || :
sleep 5
done
return 1
}
# ct_os_upload_image IMAGE [IMAGESTREAM]
# --------------------
# Uploads image from local registry to the OpenShift internal registry.
# Arguments: image - image name to upload
# Arguments: imagestream - name and tag to use for the internal registry.
# In the format of name:tag ($image_name:latest by default)
# Uses global REGISRTY_ADDRESS environment variable for arbitrary registry address.
function ct_os_upload_image() {
local input_name="${1}" ; shift
local image_name=${input_name##*/}
local imagestream=${1:-$image_name:latest}
local output_name="${REGISRTY_ADDRESS:-172.30.1.1:5000}/$(oc project -q)/$imagestream"
ct_os_docker_login
docker tag ${input_name} ${output_name}
docker push ${output_name}
}
# ct_os_install_in_centos
# --------------------
# Installs os cluster in CentOS
function ct_os_install_in_centos() {
yum install -y centos-release-openshift-origin
yum install -y wget git net-tools bind-utils iptables-services bridge-utils\
bash-completion origin-clients docker origin-clients
}
# ct_os_cluster_up [DIR, IS_PUBLIC, CLUSTER_VERSION]
# --------------------
# Runs the local OpenShift cluster using 'oc cluster up' and logs in as developer.
# Arguments: dir - directory to keep configuration data in, random if omitted
# Arguments: is_public - sets either private or public hostname for web-UI,
# use "true" for allow remote access to the web-UI,
# "false" is default
# Arguments: cluster_version - version of the OpenShift cluster to use, empty
# means default version of `oc`; example value: 3.7;
# also can be specified outside by OC_CLUSTER_VERSION
function ct_os_cluster_up() {
ct_os_cluster_running && echo "Cluster already running. Nothing is done." && return 0
ct_os_logged_in && echo "Already logged in to a cluster. Nothing is done." && return 0
mkdir -p /var/tmp/openshift
local dir="${1:-$(mktemp -d /var/tmp/openshift/os-data-XXXXXX)}" ; shift || :
local is_public="${1:-'false'}" ; shift || :
local default_cluster_version=${OC_CLUSTER_VERSION:-}
local cluster_version=${1:-${default_cluster_version}} ; shift || :
if ! grep -qe '--insecure-registry.*172\.30\.0\.0' /etc/sysconfig/docker ; then
sed -i "s|OPTIONS='|OPTIONS='--insecure-registry 172.30.0.0/16 |" /etc/sysconfig/docker
fi
systemctl stop firewalld || :
setenforce 0
iptables -F
systemctl restart docker
local cluster_ip="127.0.0.1"
[ "${is_public}" == "true" ] && cluster_ip=$(ct_get_public_ip)
if [ -n "${cluster_version}" ] ; then
# if $cluster_version is not set, we simply use oc that is available
ct_os_set_path_oc "${cluster_version}"
fi
mkdir -p ${dir}/{config,data,pv}
case $(oc version| head -n 1) in
"oc v3.1"?.*)
oc cluster up --base-dir="${dir}/data" --public-hostname="${cluster_ip}"
;;
"oc v3."*)
oc cluster up --host-data-dir="${dir}/data" --host-config-dir="${dir}/config" \
--host-pv-dir="${dir}/pv" --use-existing-config --public-hostname="${cluster_ip}"
;;
*)
echo "ERROR: Unexpected oc version." >&2
return 1
;;
esac
oc version
oc login -u system:admin
oc project default
ct_os_wait_rc_ready docker-registry 180
ct_os_wait_rc_ready router 30
oc login -u developer -p developer
# let openshift cluster to sync to avoid some race condition errors
sleep 3
}
# ct_os_cluster_down
# --------------------
# Shuts down the local OpenShift cluster using 'oc cluster down'
function ct_os_cluster_down() {
oc cluster down
}
# ct_os_cluster_running
# --------------------
# Returns 0 if oc cluster is running
function ct_os_cluster_running() {
oc cluster status &>/dev/null
}
# ct_os_logged_in
# ---------------
# Returns 0 if logged in to a cluster (remote or local)
function ct_os_logged_in() {
oc whoami >/dev/null
}
# ct_os_set_path_oc OC_VERSION
# --------------------
# This is a trick that helps using correct version of the `oc`:
# The input is version of the openshift in format v3.6.0 etc.
# If the currently available version of oc is not of this version,
# it first takes a look into /usr/local/oc-<ver>/bin directory,
# and if not found there it downloads the community release from github.
# In the end the PATH variable is changed, so the other tests can still use just 'oc'.
# Arguments: oc_version - X.Y part of the version of OSE (e.g. 3.9)
function ct_os_set_path_oc() {
local oc_version=$(ct_os_get_latest_ver $1)
local oc_path
if oc version | grep -q "oc ${oc_version%.*}." ; then
echo "Binary oc found already available in version ${oc_version}: `which oc` Doing noting."
return 0
fi
# first check whether we already have oc available in /usr/local
local installed_oc_path="/usr/local/oc-${oc_version%.*}/bin"
if [ -x "${installed_oc_path}/oc" ] ; then
oc_path="${installed_oc_path}"
echo "Binary oc found in ${installed_oc_path}" >&2
else
# oc not available in /usr/local, try to download it from github (community release)
oc_path="/tmp/oc-${oc_version}-bin"
ct_os_download_upstream_oc "${oc_version}" "${oc_path}"
fi
if [ -z "${oc_path}/oc" ] ; then
echo "ERROR: oc not found installed, nor downloaded" >&1
return 1
fi
export PATH="${oc_path}:${PATH}"
if ! oc version | grep -q "oc ${oc_version%.*}." ; then
echo "ERROR: something went wrong, oc located at ${oc_path}, but oc of version ${oc_version} not found in PATH ($PATH)" >&1
return 1
else
echo "PATH set correctly, binary oc found in version ${oc_version}: `which oc`"
fi
}
# ct_os_get_latest_ver VERSION_PART_X
# --------------------
# Returns full version (vX.Y.Z) from part of the version (X.Y)
# Arguments: vxy - X.Y part of the version
# Returns vX.Y.Z variant of the version
function ct_os_get_latest_ver(){
local vxy="v$1"
for vz in {3..0} ; do
curl -sif "https://github.com/openshift/origin/releases/tag/${vxy}.${vz}" >/dev/null && echo "${vxy}.${vz}" && return 0
done
echo "ERROR: version ${vxy} not found in https://github.com/openshift/origin/tags" >&2
return 1
}
# ct_os_download_upstream_oc OC_VERSION OUTPUT_DIR
# --------------------
# Downloads a particular version of openshift-origin-client-tools from
# github into specified output directory
# Arguments: oc_version - version of OSE (e.g. v3.7.2)
# Arguments: output_dir - output directory
function ct_os_download_upstream_oc() {
local oc_version=$1
local output_dir=$2
# check whether we already have the binary in place
[ -x "${output_dir}/oc" ] && return 0
mkdir -p "${output_dir}"
# using html output instead of https://api.github.com/repos/openshift/origin/releases/tags/${oc_version},
# because API is limited for number of queries if not authenticated
tarball=$(curl -si "https://github.com/openshift/origin/releases/tag/${oc_version}" | grep -o -e "openshift-origin-client-tools-${oc_version}-[a-f0-9]*-linux-64bit.tar.gz" | head -n 1)
# download, unpack the binaries and then put them into output directory
echo "Downloading https://github.com/openshift/origin/releases/download/${oc_version}/${tarball} into ${output_dir}/" >&2
curl -sL https://github.com/openshift/origin/releases/download/${oc_version}/"${tarball}" | tar -C "${output_dir}" -xz
mv -f "${output_dir}"/"${tarball%.tar.gz}"/* "${output_dir}/"
rmdir "${output_dir}"/"${tarball%.tar.gz}"
}
# ct_os_test_s2i_app_func IMAGE APP CONTEXT_DIR CHECK_CMD [OC_ARGS]
# --------------------
# Runs [image] and [app] in the openshift and optionally specifies env_params
# as environment variables to the image. Then check the container by arbitrary
# function given as argument (such an argument may include <IP> string,
# that will be replaced with actual IP).
# Arguments: image - prefix or whole ID of the pod to run the cmd in (compulsory)
# Arguments: app - url or local path to git repo with the application sources (compulsory)
# Arguments: context_dir - sub-directory inside the repository with the application sources (compulsory)
# Arguments: check_command - CMD line that checks whether the container works (compulsory; '<IP>' will be replaced with actual IP)
# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app`
# command, typically environment variables (optional)
function ct_os_test_s2i_app_func() {
local image_name=${1}
local app=${2}
local context_dir=${3}
local check_command=${4}
local oc_args=${5:-}
local import_image=${6:-}
local image_name_no_namespace=${image_name##*/}
local service_name="${image_name_no_namespace}-testing"
local image_tagged="${image_name_no_namespace}:${VERSION}"
if [ $# -lt 4 ] || [ -z "${1}" -o -z "${2}" -o -z "${3}" -o -z "${4}" ]; then
echo "ERROR: ct_os_test_s2i_app_func() requires at least 4 arguments that cannot be emtpy." >&2
return 1
fi
ct_os_new_project
# Create a specific imagestream tag for the image so that oc cannot use anything else
if [ "${CT_SKIP_UPLOAD_IMAGE:-false}" == 'true' ] ; then
if [ -n "${import_image}" ] ; then
echo "Importing image ${import_image} as ${image_name}:${VERSION}"
oc import-image ${image_name}:${VERSION} --from ${import_image} --confirm
else
echo "Uploading and importing image skipped."
fi
else
if [ -n "${import_image}" ] ; then
echo "Warning: Import image ${import_image} requested, but uploading image ${image_name} instead."
fi
ct_os_upload_image "${image_name}" "${image_tagged}"
fi
local app_param="${app}"
if [ -d "${app}" ] ; then
# for local directory, we need to copy the content, otherwise too smart os command
# pulls the git remote repository instead
app_param=$(ct_obtain_input "${app}")
fi
ct_os_deploy_s2i_image "${image_tagged}" "${app_param}" \
--context-dir="${context_dir}" \
--name "${service_name}" \
${oc_args}
if [ -d "${app}" ] ; then
# in order to avoid weird race seen sometimes, let's wait shortly
# before starting the build explicitly
sleep 5
oc start-build "${service_name}" --from-dir="${app_param}"
fi
ct_os_wait_pod_ready "${service_name}" 300
local ip=$(ct_os_get_service_ip "${service_name}")
local check_command_exp=$(echo "$check_command" | sed -e "s/<IP>/$ip/g")
echo " Checking APP using $check_command_exp ..."
local result=0
eval "$check_command_exp" || result=1
if [ $result -eq 0 ] ; then
echo " Check passed."
else
echo " Check failed."
fi
ct_os_delete_project
return $result
}
# ct_os_test_s2i_app IMAGE APP CONTEXT_DIR EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, ... ]
# --------------------
# Runs [image] and [app] in the openshift and optionally specifies env_params
# as environment variables to the image. Then check the http response.
# Arguments: image - prefix or whole ID of the pod to run the cmd in (compulsory)
# Arguments: app - url or local path to git repo with the application sources (compulsory)
# Arguments: context_dir - sub-directory inside the repository with the application sources (compulsory)
# Arguments: expected_output - PCRE regular expression that must match the response body (compulsory)
# Arguments: port - which port to use (optional; default: 8080)
# Arguments: protocol - which protocol to use (optional; default: http)
# Arguments: response_code - what http response code to expect (optional; default: 200)
# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app`
# command, typically environment variables (optional)
function ct_os_test_s2i_app() {
local image_name=${1}
local app=${2}
local context_dir=${3}
local expected_output=${4}
local port=${5:-8080}
local protocol=${6:-http}
local response_code=${7:-200}
local oc_args=${8:-}
local import_image=${9:-}
if [ $# -lt 4 ] || [ -z "${1}" -o -z "${2}" -o -z "${3}" -o -z "${4}" ]; then
echo "ERROR: ct_os_test_s2i_app() requires at least 4 arguments that cannot be emtpy." >&2
return 1
fi
ct_os_test_s2i_app_func "${image_name}" \
"${app}" \
"${context_dir}" \
"ct_os_test_response_internal '${protocol}://<IP>:${port}' '${response_code}' '${expected_output}'" \
"${oc_args}" "${import_image}"
}
# ct_os_test_template_app_func IMAGE APP IMAGE_IN_TEMPLATE CHECK_CMD [OC_ARGS]
# --------------------
# Runs [image] and [app] in the openshift and optionally specifies env_params
# as environment variables to the image. Then check the container by arbitrary
# function given as argument (such an argument may include <IP> string,
# that will be replaced with actual IP).
# Arguments: image_name - prefix or whole ID of the pod to run the cmd in (compulsory)
# Arguments: template - url or local path to a template to use (compulsory)
# Arguments: name_in_template - image name used in the template
# Arguments: check_command - CMD line that checks whether the container works (compulsory; '<IP>' will be replaced with actual IP)
# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app`
# command, typically environment variables (optional)
# Arguments: other_images - some templates need other image to be pushed into the OpenShift registry,
# specify them in this parameter as "<image>|<tag>", where "<image>" is a full image name
# (including registry if needed) and "<tag>" is a tag under which the image should be available
# in the OpenShift registry.
function ct_os_test_template_app_func() {
local image_name=${1}
local template=${2}
local name_in_template=${3}
local check_command=${4}
local oc_args=${5:-}
local other_images=${6:-}
local import_image=${7:-}
if [ $# -lt 4 ] || [ -z "${1}" -o -z "${2}" -o -z "${3}" -o -z "${4}" ]; then
echo "ERROR: ct_os_test_template_app_func() requires at least 4 arguments that cannot be emtpy." >&2
return 1
fi
local service_name="${name_in_template}-testing"
local image_tagged="${name_in_template}:${VERSION}"
ct_os_new_project
# Create a specific imagestream tag for the image so that oc cannot use anything else
if [ "${CT_SKIP_UPLOAD_IMAGE:-false}" == 'true' ] ; then
if [ -n "${import_image}" ] ; then
echo "Importing image ${import_image} as ${image_name}:${VERSION}"
oc import-image ${image_name}:${VERSION} --from ${import_image} --confirm
else
echo "Uploading and importing image skipped."
fi
else
if [ -n "${import_image}" ] ; then
echo "Warning: Import image ${import_image} requested, but uploading image ${image_name} instead."
fi
ct_os_upload_image "${image_name}" "${image_tagged}"
# upload also other images, that template might need (list of pairs in the format <image>|<tag>
local images_tags_a
local i_t
for i_t in ${other_images} ; do
echo "${i_t}"
IFS='|' read -ra image_tag_a <<< "${i_t}"
docker pull "${image_tag_a[0]}"
ct_os_upload_image "${image_tag_a[0]}" "${image_tag_a[1]}"
done
fi
local local_template=$(ct_obtain_input "${template}")
local namespace=${CT_NAMESPACE:-$(oc project -q)}
oc new-app ${local_template} \
--name "${name_in_template}" \
-p NAMESPACE="${namespace}" \
${oc_args}
ct_os_wait_pod_ready "${service_name}" 300
local ip=$(ct_os_get_service_ip "${service_name}")
local check_command_exp=$(echo "$check_command" | sed -e "s/<IP>/$ip/g")
echo " Checking APP using $check_command_exp ..."
local result=0
eval "$check_command_exp" || result=1
if [ $result -eq 0 ] ; then
echo " Check passed."
else
echo " Check failed."
fi
ct_os_delete_project
return $result
}
# params:
# ct_os_test_template_app IMAGE APP IMAGE_IN_TEMPLATE EXPECTED_OUTPUT [PORT, PROTOCOL, RESPONSE_CODE, OC_ARGS, ... ]
# --------------------
# Runs [image] and [app] in the openshift and optionally specifies env_params
# as environment variables to the image. Then check the http response.
# Arguments: image_name - prefix or whole ID of the pod to run the cmd in (compulsory)
# Arguments: template - url or local path to a template to use (compulsory)
# Arguments: name_in_template - image name used in the template
# Arguments: expected_output - PCRE regular expression that must match the response body (compulsory)
# Arguments: port - which port to use (optional; default: 8080)
# Arguments: protocol - which protocol to use (optional; default: http)
# Arguments: response_code - what http response code to expect (optional; default: 200)
# Arguments: oc_args - all other arguments are used as additional parameters for the `oc new-app`
# command, typically environment variables (optional)
# Arguments: other_images - some templates need other image to be pushed into the OpenShift registry,
# specify them in this parameter as "<image>|<tag>", where "<image>" is a full image name
# (including registry if needed) and "<tag>" is a tag under which the image should be available
# in the OpenShift registry.
function ct_os_test_template_app() {
local image_name=${1}
local template=${2}
local name_in_template=${3}
local expected_output=${4}
local port=${5:-8080}
local protocol=${6:-http}
local response_code=${7:-200}
local oc_args=${8:-}
local other_images=${9:-}
local import_image=${10:-}
if [ $# -lt 4 ] || [ -z "${1}" -o -z "${2}" -o -z "${3}" -o -z "${4}" ]; then
echo "ERROR: ct_os_test_template_app() requires at least 4 arguments that cannot be emtpy." >&2
return 1
fi
ct_os_test_template_app_func "${image_name}" \
"${template}" \
"${name_in_template}" \
"ct_os_test_response_internal '${protocol}://<IP>:${port}' '${response_code}' '${expected_output}'" \
"${oc_args}" \
"${other_images}" \
"${import_image}"
}
# ct_os_test_image_update IMAGE_NAME OLD_IMAGE ISTAG CHECK_FUNCTION OC_ARGS
# --------------------
# Runs an image update test with [image] uploaded to [is] imagestream
# and checks the services using an arbitrary function provided in [check_function].
# Arguments: image_name - prefix or whole ID of the pod to run the cmd in (compulsory)
# Arguments: old_image - valid name of the image from the registry
# Arguments: istag - imagestream to upload the images into (compulsory)
# Arguments: check_function - command to be run to check functionality of created services (compulsory)
# Arguments: oc_args - arguments to use during oc new-app (compulsory)
ct_os_test_image_update() {
local image_name=$1; shift
local old_image=$1; shift
local istag=$1; shift
local check_function=$1; shift
local service_name=${image_name##*/}
local ip="" check_command_exp=""
echo "Running image update test for: $image_name"
ct_os_new_project
# Get current image from repository and create an imagestream
docker pull "$old_image:latest" 2>/dev/null
ct_os_upload_image "$old_image" "$istag"
# Setup example application with curent image
oc new-app "$@" --name "$service_name"
ct_os_wait_pod_ready "$service_name" 60
# Check application output
ip=$(ct_os_get_service_ip "$service_name")
check_command_exp=${check_function//<IP>/$ip}
ct_assert_cmd_success "$check_command_exp"
# Tag built image into the imagestream and wait for rebuild
ct_os_upload_image "$image_name" "$istag"
ct_os_wait_pod_ready "${service_name}-2" 60
# Check application output
ip=$(ct_os_get_service_ip "$service_name")
check_command_exp=${check_function//<IP>/$ip}
ct_assert_cmd_success "$check_command_exp"
ct_os_delete_project
}
# ct_os_deploy_cmd_image IMAGE_NAME
# --------------------
# Runs a special command pod, a pod that does nothing, but includes utilities for testing.
# A typical usage is a mysql pod that includes mysql commandline, that we need for testing.
# Running commands inside this command pod is done via ct_os_cmd_image_run function.
# The pod is not run again if already running.
# Arguments: image_name - image to be used as a command pod
function ct_os_deploy_cmd_image() {
local image_name=${1}
oc get pod command-app &>/dev/null && echo "command POD already running" && return 0
echo "command POD not running yet, will start one called command-app"
oc create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: command-app
spec:
containers:
- name: command-container
image: "${image_name}"
command: ["sleep"]
args: ["3h"]
restartPolicy: OnFailure
EOF
SECONDS=0
echo -n "Waiting for command POD ."
while [ $SECONDS -lt 180 ] ; do
sout="$(ct_os_cmd_image_run 'echo $((11*11))')"
grep -q '^121$' <<< "$sout" && echo "DONE" && return 0 || :
sleep 3
echo -n "."
done
echo "FAIL"
return 1
}
# ct_os_cmd_image_run CMD [ ARG ... ]
# --------------------
# Runs a command CMD inside a special command pod
# Arguments: cmd - shell command with args to run in a pod
function ct_os_cmd_image_run() {
oc exec command-app -- bash -c "$@"
}
# ct_os_test_response_internal
# ----------------
# Perform GET request to the application container, checks output with
# a reg-exp and HTTP response code.
# That all is done inside an image in the cluster, so the function is used
# typically in clusters that are not accessible outside.
# The interanal image is a python image that should include the most of the useful commands.
# The check is repeated until timeout.
# Argument: url - request URL path
# Argument: expected_code - expected HTTP response code
# Argument: body_regexp - PCRE regular expression that must match the response body
# Argument: max_attempts - Optional number of attempts (default: 20), three seconds sleep between
# Argument: ignore_error_attempts - Optional number of attempts when we ignore error output (default: 10)
ct_os_test_response_internal() {
local url="$1"
local expected_code="$2"
local body_regexp="$3"
local max_attempts=${4:-20}
local ignore_error_attempts=${5:-10}
: " Testing the HTTP(S) response for <${url}>"
local sleep_time=3
local attempt=1
local result=1
local status
local response_code
local response_file=$(mktemp /tmp/ct_test_response_XXXXXX)
local util_image_name='python:3.6'
ct_os_deploy_cmd_image "${util_image_name}"
while [ ${attempt} -le ${max_attempts} ]; do
ct_os_cmd_image_run "curl --connect-timeout 10 -s -w '%{http_code}' '${url}'" >${response_file} && status=0 || status=1
if [ ${status} -eq 0 ]; then
response_code=$(cat ${response_file} | tail -c 3)
if [ "${response_code}" -eq "${expected_code}" ]; then
result=0
fi
cat ${response_file} | grep -qP -e "${body_regexp}" || result=1;
# Some services return 40x code until they are ready, so let's give them
# some chance and not end with failure right away
# Do not wait if we already have expected outcome though
if [ ${result} -eq 0 -o ${attempt} -gt ${ignore_error_attempts} -o ${attempt} -eq ${max_attempts} ] ; then
break
fi
fi
attempt=$(( ${attempt} + 1 ))
sleep ${sleep_time}
done
rm -f ${response_file}
return ${result}
}
# ct_os_get_image_from_pod
# ------------------------
# Print image identifier from an existing pod to stdout
# Argument: pod_prefix - prefix or full name of the pod to get image from
ct_os_get_image_from_pod() {
local pod_prefix=$1 ; shift
local pod_name=$(ct_os_get_pod_name $pod_prefix)
oc get "po/${pod_name}" -o yaml | sed -ne 's/^\s*image:\s*\(.*\)\s*$/\1/ p' | head -1
}
# ct_os_check_cmd_internal
# ----------------
# Runs a specified command, checks exit code and compares the output with expected regexp.
# That all is done inside an image in the cluster, so the function is used
# typically in clusters that are not accessible outside.
# The check is repeated until timeout.
# Argument: util_image_name - name of the image in the cluster that is used for running the cmd
# Argument: service_name - kubernetes' service name to work with (IP address is taken from this one)
# Argument: check_command - command that is run within the util_image_name container
# Argument: expected_content_match - regexp that must be in the output (use .* to ignore check)
# Argument: timeout - number of seconds to wait till the check succeeds
function ct_os_check_cmd_internal() {
local util_image_name=$1 ; shift
local service_name=$1 ; shift
local check_command=$1 ; shift
local expected_content_match=${1:-.*} ; shift
local timeout=${1:-60} ; shift || :
: " Service ${service_name} check ..."
local output
local ret
local ip=$(ct_os_get_service_ip "${service_name}")
local check_command_exp=$(echo "$check_command" | sed -e "s/<IP>/$ip/g")
ct_os_deploy_cmd_image $(ct_os_get_image_from_pod "${util_image_name}" | head -n 1)
SECONDS=0
echo -n "Waiting for ${service_name} service becoming ready ..."
while true ; do
output=$(ct_os_cmd_image_run "$check_command_exp")
ret=$?
echo "${output}" | grep -qe "${expected_content_match}" || ret=1
if [ ${ret} -eq 0 ] ; then
echo " PASS"
return 0
fi
echo -n "."
[ ${SECONDS} -gt ${timeout} ] && break
sleep 3
done
echo " FAIL"
return 1
}

507
test/test-lib.sh Normal file
View file

@ -0,0 +1,507 @@
#
# Test a container image.
#
# Always use sourced from a specific container testfile
#
# reguires definition of CID_FILE_DIR
# CID_FILE_DIR=$(mktemp --suffix=<container>_test_cidfiles -d)
# reguires definition of TEST_LIST
# TEST_LIST="\
# ctest_container_creation
# ctest_doc_content"
# Container CI tests
# abbreviated as "ct"
# may be redefined in the specific container testfile
EXPECTED_EXIT_CODE=0
# ct_cleanup
# --------------------
# Cleans up containers used during tests. Stops and removes all containers
# referenced by cid_files in CID_FILE_DIR. Dumps logs if a container exited
# unexpectedly. Removes the cid_files and CID_FILE_DIR as well.
# Uses: $CID_FILE_DIR - path to directory containing cid_files
# Uses: $EXPECTED_EXIT_CODE - expected container exit code
function ct_cleanup() {
for cid_file in $CID_FILE_DIR/* ; do
local container=$(cat $cid_file)
: "Stopping and removing container $container..."
docker stop $container
exit_status=$(docker inspect -f '{{.State.ExitCode}}' $container)
if [ "$exit_status" != "$EXPECTED_EXIT_CODE" ]; then
: "Dumping logs for $container"
docker logs $container
fi
docker rm -v $container
rm $cid_file
done
rmdir $CID_FILE_DIR
: "Done."
}
# ct_enable_cleanup
# --------------------
# Enables automatic container cleanup after tests.
function ct_enable_cleanup() {
trap ct_cleanup EXIT SIGINT
}
# ct_get_cid [name]
# --------------------
# Prints container id from cid_file based on the name of the file.
# Argument: name - name of cid_file where the container id will be stored
# Uses: $CID_FILE_DIR - path to directory containing cid_files
function ct_get_cid() {
local name="$1" ; shift || return 1
echo $(cat "$CID_FILE_DIR/$name")
}
# ct_get_cip [id]
# --------------------
# Prints container ip address based on the container id.
# Argument: id - container id
function ct_get_cip() {
local id="$1" ; shift
docker inspect --format='{{.NetworkSettings.IPAddress}}' $(ct_get_cid "$id")
}
# ct_wait_for_cid [cid_file]
# --------------------
# Holds the execution until the cid_file is created. Usually run after container
# creation.
# Argument: cid_file - name of the cid_file that should be created
function ct_wait_for_cid() {
local cid_file=$1
local max_attempts=10
local sleep_time=1
local attempt=1
local result=1
while [ $attempt -le $max_attempts ]; do
[ -f $cid_file ] && [ -s $cid_file ] && return 0
: "Waiting for container start..."
attempt=$(( $attempt + 1 ))
sleep $sleep_time
done
return 1
}
# ct_assert_container_creation_fails [container_args]
# --------------------
# The invocation of docker run should fail based on invalid container_args
# passed to the function. Returns 0 when container fails to start properly.
# Argument: container_args - all arguments are passed directly to dokcer run
# Uses: $CID_FILE_DIR - path to directory containing cid_files
function ct_assert_container_creation_fails() {
local ret=0
local max_attempts=10
local attempt=1
local cid_file=assert
set +e
local old_container_args="${CONTAINER_ARGS-}"
CONTAINER_ARGS="$@"
ct_create_container $cid_file
if [ $? -eq 0 ]; then
local cid=$(ct_get_cid $cid_file)
while [ "$(docker inspect -f '{{.State.Running}}' $cid)" == "true" ] ; do
sleep 2
attempt=$(( $attempt + 1 ))
if [ $attempt -gt $max_attempts ]; then
docker stop $cid
ret=1
break
fi
done
exit_status=$(docker inspect -f '{{.State.ExitCode}}' $cid)
if [ "$exit_status" == "0" ]; then
ret=1
fi
docker rm -v $cid
rm $CID_FILE_DIR/$cid_file
fi
[ ! -z $old_container_args ] && CONTAINER_ARGS="$old_container_args"
set -e
return $ret
}
# ct_create_container [name, command]
# --------------------
# Creates a container using the IMAGE_NAME and CONTAINER_ARGS variables. Also
# stores the container id to a cid_file located in the CID_FILE_DIR, and waits
# for the creation of the file.
# Argument: name - name of cid_file where the container id will be stored
# Argument: command - optional command to be executed in the container
# Uses: $CID_FILE_DIR - path to directory containing cid_files
# Uses: $CONTAINER_ARGS - optional arguments passed directly to docker run
# Uses: $IMAGE_NAME - name of the image being tested
function ct_create_container() {
local cid_file="$CID_FILE_DIR/$1" ; shift
# create container with a cidfile in a directory for cleanup
docker run --cidfile="$cid_file" -d ${CONTAINER_ARGS:-} $IMAGE_NAME "$@"
ct_wait_for_cid $cid_file || return 1
: "Created container $(cat $cid_file)"
}
# ct_scl_usage_old [name, command, expected]
# --------------------
# Tests three ways of running the SCL, by looking for an expected string
# in the output of the command
# Argument: name - name of cid_file where the container id will be stored
# Argument: command - executed inside the container
# Argument: expected - string that is expected to be in the command output
# Uses: $CID_FILE_DIR - path to directory containing cid_files
# Uses: $IMAGE_NAME - name of the image being tested
function ct_scl_usage_old() {
local name="$1"
local command="$2"
local expected="$3"
local out=""
: " Testing the image SCL enable"
out=$(docker run --rm ${IMAGE_NAME} /bin/bash -c "${command}")
if ! echo "${out}" | grep -q "${expected}"; then
echo "ERROR[/bin/bash -c "${command}"] Expected '${expected}', got '${out}'" >&2
return 1
fi
out=$(docker exec $(ct_get_cid $name) /bin/bash -c "${command}" 2>&1)
if ! echo "${out}" | grep -q "${expected}"; then
echo "ERROR[exec /bin/bash -c "${command}"] Expected '${expected}', got '${out}'" >&2
return 1
fi
out=$(docker exec $(ct_get_cid $name) /bin/sh -ic "${command}" 2>&1)
if ! echo "${out}" | grep -q "${expected}"; then
echo "ERROR[exec /bin/sh -ic "${command}"] Expected '${expected}', got '${out}'" >&2
return 1
fi
}
# ct_doc_content_old [strings]
# --------------------
# Looks for occurence of stirngs in the documentation files and checks
# the format of the files. Files examined: help.1
# Argument: strings - strings expected to appear in the documentation
# Uses: $IMAGE_NAME - name of the image being tested
function ct_doc_content_old() {
local tmpdir=$(mktemp -d)
local f
: " Testing documentation in the container image"
# Extract the help files from the container
for f in help.1 ; do
docker run --rm ${IMAGE_NAME} /bin/bash -c "cat /${f}" >${tmpdir}/$(basename ${f})
# Check whether the files contain some important information
for term in $@ ; do
if ! cat ${tmpdir}/$(basename ${f}) | grep -F -q -e "${term}" ; then
echo "ERROR: File /${f} does not include '${term}'." >&2
return 1
fi
done
# Check whether the files use the correct format
for term in TH PP SH ; do
if ! grep -q "^\.${term}" ${tmpdir}/help.1 ; then
echo "ERROR: /help.1 is probably not in troff or groff format, since '${term}' is missing." >&2
return 1
fi
done
done
: " Success!"
}
# ct_npm_works
# --------------------
# Checks existance of the npm tool and runs it.
function ct_npm_works() {
local tmpdir=$(mktemp -d)
: " Testing npm in the container image"
docker run --rm ${IMAGE_NAME} /bin/bash -c "npm --version" >${tmpdir}/version
if [ $? -ne 0 ] ; then
echo "ERROR: 'npm --version' does not work inside the image ${IMAGE_NAME}." >&2
return 1
fi
docker run --rm ${IMAGE_NAME} /bin/bash -c "npm install jquery && test -f node_modules/jquery/src/jquery.js"
if [ $? -ne 0 ] ; then
echo "ERROR: npm could not install jquery inside the image ${IMAGE_NAME}." >&2
return 1
fi
: " Success!"
}
# ct_path_append PATH_VARNAME DIRECTORY
# -------------------------------------
# Append DIRECTORY to VARIABLE of name PATH_VARNAME, the VARIABLE must consist
# of colon-separated list of directories.
ct_path_append ()
{
if eval "test -n \"\${$1-}\""; then
eval "$1=\$2:\$$1"
else
eval "$1=\$2"
fi
}
# ct_path_foreach PATH ACTION [ARGS ...]
# --------------------------------------
# For each DIR in PATH execute ACTION (path is colon separated list of
# directories). The particular calls to ACTION will look like
# '$ ACTION directory [ARGS ...]'
ct_path_foreach ()
{
local dir dirlist action save_IFS
save_IFS=$IFS
IFS=:
dirlist=$1
action=$2
shift 2
for dir in $dirlist; do "$action" "$dir" "$@" ; done
IFS=$save_IFS
}
# ct_run_test_list
# --------------------
# Execute the tests specified by TEST_LIST
# Uses: $TEST_LIST - list of test names
function ct_run_test_list() {
for test_case in $TEST_LIST; do
: "Running test $test_case"
[ -f test/$test_case ] && source test/$test_case
[ -f ../test/$test_case ] && source ../test/$test_case
$test_case
done;
}
# ct_gen_self_signed_cert_pem
# ---------------------------
# Generates a self-signed PEM certificate pair into specified directory.
# Argument: output_dir - output directory path
# Argument: base_name - base name of the certificate files
# Resulted files will be those:
# <output_dir>/<base_name>-cert-selfsigned.pem -- public PEM cert
# <output_dir>/<base_name>-key.pem -- PEM private key
ct_gen_self_signed_cert_pem() {
local output_dir=$1 ; shift
local base_name=$1 ; shift
mkdir -p ${output_dir}
openssl req -newkey rsa:2048 -nodes -keyout ${output_dir}/${base_name}-key.pem -subj '/C=GB/ST=Berkshire/L=Newbury/O=My Server Company' > ${base_name}-req.pem
openssl req -new -x509 -nodes -key ${output_dir}/${base_name}-key.pem -batch > ${output_dir}/${base_name}-cert-selfsigned.pem
}
# ct_obtain_input FILE|DIR|URL
# --------------------
# Either copies a file or a directory to a tmp location for local copies, or
# downloads the file from remote location.
# Resulted file path is printed, so it can be later used by calling function.
# Arguments: input - local file, directory or remote URL
function ct_obtain_input() {
local input=$1
local extension="${input##*.}"
# Try to use same extension for the temporary file if possible
[[ "${extension}" =~ ^[a-z0-9]*$ ]] && extension=".${extension}" || extension=""
local output=$(mktemp "/var/tmp/test-input-XXXXXX$extension")
if [ -f "${input}" ] ; then
cp -f "${input}" "${output}"
elif [ -d "${input}" ] ; then
rm -f "${output}"
cp -r -LH "${input}" "${output}"
elif echo "${input}" | grep -qe '^http\(s\)\?://' ; then
curl "${input}" > "${output}"
else
echo "ERROR: file type not known: ${input}" >&2
return 1
fi
echo "${output}"
}
# ct_test_response
# ----------------
# Perform GET request to the application container, checks output with
# a reg-exp and HTTP response code.
# Argument: url - request URL path
# Argument: expected_code - expected HTTP response code
# Argument: body_regexp - PCRE regular expression that must match the response body
# Argument: max_attempts - Optional number of attempts (default: 20), three seconds sleep between
# Argument: ignore_error_attempts - Optional number of attempts when we ignore error output (default: 10)
ct_test_response() {
local url="$1"
local expected_code="$2"
local body_regexp="$3"
local max_attempts=${4:-20}
local ignore_error_attempts=${5:-10}
: " Testing the HTTP(S) response for <${url}>"
local sleep_time=3
local attempt=1
local result=1
local status
local response_code
local response_file=$(mktemp /tmp/ct_test_response_XXXXXX)
while [ ${attempt} -le ${max_attempts} ]; do
curl --connect-timeout 10 -s -w '%{http_code}' "${url}" >${response_file} && status=0 || status=1
if [ ${status} -eq 0 ]; then
response_code=$(cat ${response_file} | tail -c 3)
if [ "${response_code}" -eq "${expected_code}" ]; then
result=0
fi
cat ${response_file} | grep -qP -e "${body_regexp}" || result=1;
# Some services return 40x code until they are ready, so let's give them
# some chance and not end with failure right away
# Do not wait if we already have expected outcome though
if [ ${result} -eq 0 -o ${attempt} -gt ${ignore_error_attempts} -o ${attempt} -eq ${max_attempts} ] ; then
break
fi
fi
attempt=$(( ${attempt} + 1 ))
sleep ${sleep_time}
done
rm -f ${response_file}
return ${result}
}
# ct_registry_from_os OS
# ----------------
# Transform operating system string [os] into registry url
# Argument: OS - string containing the os version
ct_registry_from_os() {
local registry=""
case $1 in
rhel7)
registry=registry.access.redhat.com
;;
*)
registry=docker.io
;;
esac
echo "$registry"
}
# ct_assert_cmd_success CMD
# ----------------
# Evaluates [cmd] and fails if it does not succeed.
# Argument: CMD - Command to be run
function ct_assert_cmd_success() {
echo "Checking '$*' for success ..."
if ! eval "$@" &>/dev/null; then
echo " FAIL"
return 1
fi
echo " PASS"
return 0
}
# ct_assert_cmd_failure CMD
# ----------------
# Evaluates [cmd] and fails if it succeeds.
# Argument: CMD - Command to be run
function ct_assert_cmd_failure() {
echo "Checking '$*' for failure ..."
if eval "$@" &>/dev/null; then
echo " FAIL"
return 1
fi
echo " PASS"
return 0
}
# ct_random_string [LENGTH=10]
# ----------------------------
# Generate pseudorandom alphanumeric string of LENGTH bytes, the
# default length is 10. The string is printed on stdout.
ct_random_string()
(
export LC_ALL=C
dd if=/dev/urandom count=1 bs=10k 2>/dev/null \
| tr -dc 'a-z0-9' \
| fold -w "${1-10}" \
| head -n 1
)
# ct_s2i_usage IMG_NAME [S2I_ARGS]
# ----------------------------
# Create a container and run the usage script inside
# Argument: IMG_NAME - name of the image to be used for the container run
# Argument: S2I_ARGS - Additional list of source-to-image arguments, currently unused.
ct_s2i_usage()
{
local img_name=$1; shift
local s2i_args="$*";
local usage_command="/usr/libexec/s2i/usage"
docker run --rm "$img_name" bash -c "$usage_command"
}
# ct_s2i_build_as_df APP_PATH SRC_IMAGE DST_IMAGE [S2I_ARGS]
# ----------------------------
# Create a new s2i app image from local sources in a similar way as source-to-image would have used.
# Argument: APP_PATH - local path to the app sources to be used in the test
# Argument: SRC_IMAGE - image to be used as a base for the s2i build
# Argument: DST_IMAGE - image name to be used during the tagging of the s2i build result
# Argument: S2I_ARGS - Additional list of source-to-image arguments.
# Only used to check for pull-policy=never and environment variable definitions.
ct_s2i_build_as_df()
{
local app_path=$1; shift
local src_image=$1; shift
local dst_image=$1; shift
local s2i_args="$*";
local local_app=upload/src/
local local_scripts=upload/scripts/
local user_id=
local df_name=
local tmpdir=
# Use /tmp to not pollute cwd
tmpdir=$(mktemp -d)
df_name=$(mktemp -p "$tmpdir" Dockerfile.XXXX)
pushd "$tmpdir"
# Check if the image is available locally and try to pull it if it is not
docker images "$src_image" &>/dev/null || echo "$s2i_args" | grep -q "pull-policy=never" || docker pull "$src_image"
user_id=$(docker inspect -f "{{.ContainerConfig.User}}" "$src_image")
# Strip file:// from APP_PATH and copy its contents into current context
mkdir -p "$local_app"
cp -r "${app_path/file:\/\//}/." "$local_app"
[ -d "$local_app/.s2i/bin/" ] && mv "$local_app/.s2i/bin" "$local_scripts"
# Create a Dockerfile named df_name and fill it with proper content
#FIXME: Some commands could be combined into a single layer but not sure if worth the trouble for testing purposes
cat <<EOF >"$df_name"
FROM $src_image
LABEL "io.openshift.s2i.build.image"="$src_image" \\
"io.openshift.s2i.build.source-location"="$app_path"
USER root
COPY $local_app /tmp/src
EOF
[ -d "$local_scripts" ] && echo "COPY $local_scripts /tmp/scripts" >> "$df_name" &&
echo "RUN chown -R $user_id:0 /tmp/scripts" >>"$df_name"
echo "RUN chown -R $user_id:0 /tmp/src" >>"$df_name"
# Check for custom environment variables inside .s2i/ folder
if [ -e "$local_app/.s2i/environment" ]; then
# Remove any comments and add the contents as ENV commands to the Dockerfile
sed '/^\s*#.*$/d' "$local_app/.s2i/environment" | while read -r line; do
echo "ENV $line" >>"$df_name"
done
fi
# Filter out env var definitions from $s2i_args and create Dockerfile ENV commands out of them
echo "$s2i_args" | grep -o -e '\(-e\|--env\)[[:space:]=]\S*=\S*' | sed -e 's/-e /ENV /' -e 's/--env[ =]/ENV /' >>"$df_name"
echo "USER $user_id" >>"$df_name"
# If exists, run the custom assemble script, else default to /usr/libexec/s2i/assemble
if [ -x "$local_scripts/assemble" ]; then
echo "RUN /tmp/scripts/assemble" >>"$df_name"
else
echo "RUN /usr/libexec/s2i/assemble" >>"$df_name"
fi
# If exists, set the custom run script as CMD, else default to /usr/libexec/s2i/run
if [ -x "$local_scripts/run" ]; then
echo "CMD /tmp/scripts/run" >>"$df_name"
else
echo "CMD /usr/libexec/s2i/run" >>"$df_name"
fi
# Run the build and tag the result
docker build -f "$df_name" -t "$dst_image" .
popd
}

4
test/warningonstderr/index.pl Executable file
View file

@ -0,0 +1,4 @@
#!/usr/bin/perl
print "Content-type: text/plain\n\n";
warn "Warning on stderr\n";
print "Text in HTTP body\n";