diff --git a/README.md b/README.md index c20c3335f5254fcf9cf4f0a16e8d1e0486f0d0c1..7d9bae9a2b74622ae0a7ad5c1ab1a2800fe5b9ab 100644 --- a/README.md +++ b/README.md @@ -66,3 +66,192 @@ su -s /bin/sh tomcat8 cd /opt/repository/sources/dspace/utilities/project_helpers/scripts /etc/init.d/tomcat8 start ``` + +# DSpace directory structure + +This is the directory structure + +``` +├── clarin-dspace-docker +│ └── commul-customization +└── kubernetes + ├── helm + │ └── dspace + │ ├── charts + │ │ └── postgresql + │ │ └── templates + │ └── templates + └── yaml +``` + + +* `/clarin-dspace-docker` - Docker files, copied from https://github.com/commul/clarin-dspace-docker @ 74d06d3 . **With this directory, you can build images** +* `/kubernetes/helm` - Chart Directory. Refer to https://github.com/kubernetes/helm/blob/master/docs/charts.md#the-chart-file-structure :exclamation: **not working yet** :exclamation: +* `/kubernetes/yaml` - Useful for tests, but helm is a more flexible approach **With this directory, you can deploy images in kubernetes cluster** +# Introduction +# Prerequisites + +* Kubernetes cluster + +## To interact with cluster + +* Kubernetes credentials +* a working [kubectl] (https://kubernetes.io/docs/tasks/tools/install-kubectl/) + +## To interact with Helm server (tiller) -- Coming soon + +* Kubernetes credentials +* a working [kubectl] (https://kubernetes.io/docs/tasks/tools/install-kubectl/) +* a working [helm client] (https://docs.helm.sh/using_helm/#quickstart-guide) + +# How to use it +## Workflow +First of all, check if a docker image already exists. +If it exist, pull, otherwise, you need to build one. +If necessary create a [personal token](https://gitlab.scientificnet.org/profile/personal_access_tokens) and login into GitLab Registry. + +```console +docker login gitlab.scientificnet.org:4567 +``` + +### Use existing Docker image +```console +docker image ls -a gitlab.scientificnet.org:4567/commul/dspace/* +docker pull gitlab.scientificnet.org:4567/commul/dspace/<my_image>:<my tag> +``` + +### Build images +* build your docker image from `/clarin-dspace-docker` directory +* push to `https://gitlab.scientificnet.org/commul/dspace/container_registry` + +```console +docker build -t gitlab.scientificnet.org:4567/commul/dspace/<my_image>:<my tag> -f Dockerfile.XX . +docker push gitlab.scientificnet.org:4567/commul/dspace/<my_image>:<my tag> +``` + +# Installation +This step in a "workspace preparation" and is not directly related with dspace. + +Provide gitlab & RBD credentials + +```console +$ kubectl -n dspace-dev create -f gitlab-scientificnet-org-registry.yaml +``` +```console +$ kubectl -n dspace-dev create -f rbd-secret.yaml +``` +## PostgreSQL +To install PostgreSQL, we need some passwords, so we need Kubernetes secrets + +```console +$ kubectl -n dspace-dev create -f postgresql-secret.yaml +secret "dspace-postgresql" created +``` + +```console +$ kubectl -n dspace-dev create -f dspace-secrets.yaml +secret "dspace-secrets" created +``` + +```console +$ kubectl -n dspace-dev get secrets +NAME TYPE DATA AGE +ceph-rbd-secret kubernetes.io/rbd 1 14m +default-token-c1pd4 kubernetes.io/service-account-token 3 4d +dspace-postgresql Opaque 1 1h +dspace-secrets Opaque 1 1m +gitlab-scientificnet-org-registry kubernetes.io/dockerconfigjson 1 46m +``` + + + +Next, create a RBD Claim +```console +$ kubectl -n dspace-dev create -f postgresql-pvc.yaml +persistentvolumeclaim "dspace-postgresql" created + +$ kubectl -n dspace-dev get pvc +NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE +dspace-postgresql Bound pvc-610c358d-a518-11e7-b21c-b60483de6a40 8Gi RWO auto 2s +``` + +Ok, now we are ready to deploy our PostgreSQL! +```console +$ kubectl -n dspace-dev create -f postgresql.yaml + +$ kubectl -n dspace-dev get po +NAME READY STATUS RESTARTS AGE +dspace-postgresql-1806947503-cj077 0/1 ContainerCreating 0 2s + +$ kubectl -n dspace-dev get po +NAME READY STATUS RESTARTS AGE +dspace-postgresql-1806947503-qw8q4 0/1 Running 0 9s + +$ kubectl -n dspace-dev get po +NAME READY STATUS RESTARTS AGE +dspace-postgresql-1806947503-qw8q4 1/1 Running 0 46s +``` + +We have now launched the pod but we have to wait until the pod is up before accessing it +```console +$ kubectl -n dspace-dev exec -it dspace-postgresql-1806947503-qw8q4 bash +root@dspace-postgresql-1806947503-qw8q4:/# +``` + +Also, we can also test our PostgreSQL installation with a port forward +```console +$ kubectl -n dspace-dev port-forward dspace-postgresql-1806947503-qw8q4 5432:5432 +``` +now we are able to connect to localhost:5432 + + + +```console +$ kubectl -n dspace-dev create -f postgresql-service.yaml +service "dspace-postgresql" created +``` +```console +$ kubectl -n dspace-dev exec -it servlet env | grep POSTGRESQL +DSPACE_POSTGRESQL_SERVICE_PORT_POSTGRESQL=5432 +DSPACE_POSTGRESQL_PORT_5432_TCP_ADDR=10.13.0.173 +DSPACE_POSTGRESQL_SERVICE_HOST=10.13.0.173 +DSPACE_POSTGRESQL_PORT_5432_TCP_PROTO=tcp +DSPACE_POSTGRESQL_PORT_5432_TCP_PORT=5432 +DSPACE_POSTGRESQL_SERVICE_PORT=5432 +DSPACE_POSTGRESQL_PORT=tcp://10.13.0.173:5432 +DSPACE_POSTGRESQL_PORT_5432_TCP=tcp://10.13.0.173:5432 +``` + +## DSpace +```console +$ kubectl -n dspace-dev create -f servlet.yaml +pod "servlet" created +``` +```console +$ kubectl -n dspace-dev get po +NAME READY STATUS RESTARTS AGE +dspace-postgresql-1806947503-qw8q4 1/1 Running 0 4d +servlet 1/1 Running 0 34s +``` +```console +$ kubectl -n dspace-dev logs servlet +[Wed Oct 4 12:24:25 UTC 2017] Starting +[Wed Oct 4 12:24:25 UTC 2017] Cloning lindat-dspace repository...skip! /opt/repository/sources/dspace already exists +[Wed Oct 4 12:24:25 UTC 2017] Creating workspace...skip! /opt/repository/workspace already exists +[Wed Oct 4 12:24:25 UTC 2017] Copy makefile...skip! /opt/repository/workspace/config/variable.makefile already exists +[Wed Oct 4 12:24:25 UTC 2017] Copy local.properties...skip! /opt/repository/workspace/sources/local.properties already exists +[Wed Oct 4 12:24:25 UTC 2017] Compilation and Deployment of DSpace...already done +already done +already done +[Wed Oct 4 12:24:25 UTC 2017] Copy aai_config.js...skip! /opt/lindat-dspace/installation/webapps/xmlui/themes/UFAL/lib/js/aai_config.js already exists +[Wed Oct 4 12:24:25 UTC 2017] Create dspace admin...already done +[Wed Oct 4 12:24:25 UTC 2017] Starting Tomcat...Starting Tomcat 8... +Tomcat started. +``` +```console +$ kubectl -n dspace-dev port-forward servlet 8080:8080 +Forwarding from 127.0.0.1:8080 -> 8080 +Forwarding from [::1]:8080 -> 8080 +``` + +connect to http://localhost:8080/repository/xmlui/# diff --git a/clarin-dspace-docker/.gitignore b/clarin-dspace-docker/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..9b1fae385e2cbbdb984f7be267578ecc4bf58b04 --- /dev/null +++ b/clarin-dspace-docker/.gitignore @@ -0,0 +1,2 @@ +commul-customization/certs/* +password_mod.sh diff --git a/clarin-dspace-docker/Dockerfile.dspace b/clarin-dspace-docker/Dockerfile.dspace new file mode 100644 index 0000000000000000000000000000000000000000..b8e3d6de43428791271816ea9274d0ee5b280b78 --- /dev/null +++ b/clarin-dspace-docker/Dockerfile.dspace @@ -0,0 +1,71 @@ +############################################################ +# Dockerfile to build LINDAT Dspace container images +# Based on Ubuntu +############################################################ + +FROM ubuntu +LABEL maintainer="Alexander König <Alexander.Koenig@eurac.edu>" + +# install cpanm and dependencies +RUN apt-get update &&\ + apt-get -y upgrade &&\ + apt-get -y install make gcc wget cpanminus + +RUN cpanm File::Spec::Functions +RUN cpanm Term::ReadLine + +# install jdk, ant, psql, mvn, make, libxml, xsltproc, zip, wget +RUN apt-get -y install openjdk-8-jdk ant maven libxml2-utils xsltproc unzip curl git + +# install convenience apps for debug purposes (vim and jsbeautifier) +RUN apt-get -y install vim less python-pip +RUN pip install --upgrade pip +RUN pip install jsbeautifier +COPY commul-customization/__init__.py /usr/local/lib/python2.7/dist-packages/jsbeautifier/ + +# set up a proper locale +RUN apt-get -y install locales +RUN locale-gen en_US.UTF-8 +COPY commul-customization/default_locale /etc/default/locale +RUN chmod 0755 /etc/default/locale + +ENV LC_ALL=en_US.UTF-8 +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US.UTF-8 + +# build tomcat 8.0.35, version recommended by LINDAT +RUN wget -O /tmp/apache-tomcat-8.0.35.tar.gz https://archive.apache.org/dist/tomcat/tomcat-8/v8.0.35/bin/apache-tomcat-8.0.35.tar.gz +RUN mkdir /opt/tomcat8 +RUN tar xvzf /tmp/apache-tomcat-8.0.35.tar.gz -C /opt/tomcat8/ --strip-components=1 +RUN groupadd tomcat8 +RUN useradd -s /bin/false -g tomcat8 -d /opt/tomcat8/ tomcat8 + +# change permissions +RUN chown -R tomcat8:tomcat8 /opt/tomcat8 +# copy config files etc. (init script,catalina.sh,setenv.sh,server.xml +COPY commul-customization/tomcat8 /etc/init.d/ +RUN chmod a+x /etc/init.d/tomcat8 +COPY commul-customization/catalina.sh /opt/tomcat8/bin/ +COPY commul-customization/setenv.sh /opt/tomcat8/bin/ +COPY commul-customization/tomcat-server.xml /opt/tomcat8/conf/server.xml + + +# install Dspace +RUN mkdir /tmp/commul-customization +COPY commul-customization/variable.makefile /tmp/commul-customization/ + +# copy over configs and startup scripts +COPY commul-customization/start_stack.sh /tmp/commul-customization/ +COPY commul-customization/stop_stack.sh /tmp/commul-customization/ +COPY commul-customization/local.properties /tmp/commul-customization/ + +# copy over config files +COPY commul-customization/aai_config.js /tmp/commul-customization/ +#CMD tail -f /opt/repository/sources/dspace/dspace-api/src/test/data/dspaceFolder/log/dspace.log + +COPY dspace-entrypoint.sh / +RUN chmod +x dspace-entrypoint.sh + +ENTRYPOINT ["/dspace-entrypoint.sh"] + + diff --git a/clarin-dspace-docker/Dockerfile.handle b/clarin-dspace-docker/Dockerfile.handle new file mode 100644 index 0000000000000000000000000000000000000000..4ef76df340f823eab1aba0565a152f69fd2e9cf4 --- /dev/null +++ b/clarin-dspace-docker/Dockerfile.handle @@ -0,0 +1,32 @@ +############################################################ +# Dockerfile to build a handle server +# Based on Ubuntu +############################################################ + +FROM ubuntu +LABEL maintainer="Alexander König <Alexander.Koenig@eurac.edu>" + +ENV TERM linux + +RUN apt-get update &&\ + apt-get -y upgrade &&\ + apt-get -y install openjdk-8-jdk unzip wget make cpanminus gcc + +# convenience packages +RUN apt-get -y install less vim + +RUN cpanm Term::ReadLine +RUN cpanm File::Spec::Functions + +RUN mkdir -p /hs/svr_1 +WORKDIR /hs +RUN wget http://www.handle.net/hnr-source/hsj-8.1.1.tar.gz +RUN tar -xzf /hs/hsj-8.1.1.tar.gz +RUN mv hsj-8.1.1 hsj-8 +COPY commul-customization/hdl-setup.tmp /tmp/ +WORKDIR /hs/hsj-8 +RUN cat /tmp/hdl-setup.tmp | bin/hdl-setup-server /hs/svr_1/ +RUN perl -pi -e 's/YOUR_PREFIX/20.500.12084/' /hs/svr_1/config.dct +RUN perl -pi -e 's/case_sensitive" = "no/case_sensitive" = "yes/' /hs/svr_1/config.dct +RUN perl -i -plne 'print " \"storage_type\" = \"CUSTOM\"\n\ \"storage_class\" = \"org.dspace.handle.HandlePlugin\"" if(/max_session/);' /hs/svr_1/config.dct +# ENTRYPOINT ["/bin/bash"] diff --git a/clarin-dspace-docker/Dockerfile.nginx b/clarin-dspace-docker/Dockerfile.nginx new file mode 100644 index 0000000000000000000000000000000000000000..5cb9399dbeae1dbf8594a95ba469394ce1e92bd4 --- /dev/null +++ b/clarin-dspace-docker/Dockerfile.nginx @@ -0,0 +1,103 @@ +####################################################################### +# Dockerfile to build nginx and shibboleth for LINDAT Dspace container +# Based on Ubuntu +####################################################################### + +FROM ubuntu +LABEL maintainer="Alexander König <Alexander.Koenig@eurac.edu>" + +# install cpanm and dependencies +RUN apt-get update &&\ + apt-get -y upgrade &&\ + apt-get -y install make gcc wget cpanminus + +RUN cpanm File::Spec::Functions +RUN cpanm Term::ReadLine + +# install jdk, ant, psql, mvn, make, libxml, xsltproc, zip, wget +RUN apt-get -y install openjdk-8-jdk ant maven libxml2-utils xsltproc unzip curl + +# install convenience apps for debug purposes (vim and jsbeautifier) +RUN apt-get -y install vim less python-pip +RUN pip install --upgrade pip +RUN pip install jsbeautifier +COPY commul-customization/__init__.py /usr/local/lib/python2.7/dist-packages/jsbeautifier/ + +# set up a proper locale +RUN apt-get -y install locales +RUN locale-gen en_US.UTF-8 +COPY commul-customization/default_locale /etc/default/locale +RUN chmod 0755 /etc/default/locale + +ENV LC_ALL=en_US.UTF-8 +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US.UTF-8 + +# build nginx +RUN apt-get install -y zlib1g zlib1g-dev libpcre3 libpcre3-dev curl +RUN cpanm File::Spec::Functions +RUN cpanm Term::ReadLine +COPY commul-customization/nginx_build.sh /tmp/nginx_build.sh +RUN chmod a+x /tmp/nginx_build.sh +WORKDIR /tmp/ +RUN ./nginx_build.sh +# copy the init script +COPY commul-customization/nginx /etc/init.d/ +RUN chmod a+x /etc/init.d/nginx +# add a symlink +RUN ln -s /opt/nginx/sbin/nginx /usr/sbin/nginx +# copy over static html +RUN mkdir /opt/nginx/html/img +COPY commul-customization/index.html /opt/nginx/html/ +COPY commul-customization/eurac.png /opt/nginx/html/img/ +# create the test secure folder +RUN mkdir /opt/nginx/html/secure +COPY commul-customization/shib_test.pl /opt/nginx/html/secure/ + +# get aai project +RUN apt-get install -y git +RUN git clone https://github.com/ufal/lindat-aai-discovery.git /opt/repository/sources/lindat-aai-discovery + +# install shibboleth +COPY commul-customization/shibboleth_sp_with_fastcgi.sh /tmp/ +WORKDIR /tmp +RUN chmod u+x /tmp/shibboleth_sp_with_fastcgi.sh +RUN /tmp/shibboleth_sp_with_fastcgi.sh +# copy the init script +RUN cp /opt/shibboleth-sp-fastcgi/etc/shibboleth/shibd-debian /etc/init.d/shibd +RUN chmod a+x /etc/init.d/shibd + + +# install supervisor +RUN apt-get install -y python-setuptools +RUN easy_install supervisor +COPY commul-customization/supervisord.conf /etc/ +RUN mkdir -p /var/log/supervisor + +# copy over config files +COPY commul-customization/default-ssl /opt/nginx/conf/ +COPY commul-customization/nginx.conf /opt/nginx/conf/ +COPY commul-customization/repository_auth /opt/nginx/conf/ +COPY commul-customization/shibboleth2.xml /opt/shibboleth-sp-fastcgi/etc/shibboleth/ +COPY commul-customization/clarin.eurac.edu.template.metadata.xml /opt/shibboleth-sp-fastcgi/etc/shibboleth/ +COPY commul-customization/shib_clear_headers /opt/nginx/conf/ + +# copy over config files +COPY commul-customization/aai.js /opt/repository/sources/lindat-aai-discovery/ +WORKDIR /opt/repository/sources/lindat-aai-discovery/ +RUN touch /opt/repository/sources/lindat-aai-discovery/aai.js +RUN make aai.min.js + +# copy certificate for clarin-dev +COPY commul-customization/certs/lindat-dev.key /etc/ssl/private/ +COPY commul-customization/certs/lindat-dev_eurac_edu.pem /etc/ssl/certs/ + +# if deployed on clarin instead of clarin-dev comment the two lines above and uncomment the following lines + +## copy certificate for clarin +# COPY commul-customization/certs/lindat_eurac_edu.key /etc/ssl/private/ +# COPY commul-customization/certs/lindat_eurac_edu.crt /etc/ssl/certs/ + + +ENTRYPOINT ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf"] + diff --git a/clarin-dspace-docker/Dockerfile.psql b/clarin-dspace-docker/Dockerfile.psql new file mode 100644 index 0000000000000000000000000000000000000000..ada2b7d424c6ee43e2bb4b12b6b56360c6d0fd0b --- /dev/null +++ b/clarin-dspace-docker/Dockerfile.psql @@ -0,0 +1,15 @@ +FROM postgres:9.6.3 +LABEL maintainer="Alexander König <Alexander.Koenig@eurac.edu>" + +# install git +RUN apt-get update &&\ + apt-get -y install git + +# copy int db script +RUN mkdir -p /docker-entrypoint-initdb.d +COPY commul-customization/init-dspace-dbs.sh /docker-entrypoint-initdb.d/ + +# copy utilities generating script +RUN mkdir /tmp/sql/ +COPY adapt_utilities_sql.sh /tmp/sql/ +RUN /tmp/sql/adapt_utilities_sql.sh diff --git a/clarin-dspace-docker/README.md b/clarin-dspace-docker/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c20c3335f5254fcf9cf4f0a16e8d1e0486f0d0c1 --- /dev/null +++ b/clarin-dspace-docker/README.md @@ -0,0 +1,68 @@ +# dockerized CLARIN DSpace + +This is meant to provide an easy way to install [CLARIN DSpace](https://github.com/ufal/clarin-dspace) by providing a Docker Compose setup that automatizes the installation as far as possible. Additionally all files that need to be customized or added are also part of this repository. + +# How to use it + +You need a Linux server (we tested on Ubuntu 16.04 and CentOS 7.3) with a recent install of Docker (we tested with 17.05.0-ce). Clone this git repository onto the server. Then you need to look through all the files in commul-customization and adapt the configuration to your server (domain name, user names, etc). + +None of the files contain passwords, but all files which need passwords in them are in the repository as .dist files. To insert the passwords you need to remove the dist ending and then run password_mod.sh (after you've modified it with your passwords). + +# Exemplary workflow + +## get Dockerfiles +``` +git clone https://github.com/commul/clarin-dspace-docker +cd clarin-dspace-docker +``` + +## get my versions of dist files +``` +cp password_mod.sh.dist password_mod.sh +cp commul-customization/init-dspace-dbs.sh.dist commul-customization/init-dspace-dbs.sh +cp commul-customization/local.properties.dist commul-customization/local.properties +``` + +## change passwords +``` +vi password_mod.sh +chmod +x password_mod.sh +./password_mod.sh +``` + +## make sure the certificate and key are there +``` +cp -r /tmp/certs ./commul-customization/ +``` + +## build the images +``` +docker-compose up -d --build +``` + +## enter the DSpace container +``` +docker exec -ti clarindspacedocker_dspace_1 bash +``` + +## deploy DSpace +``` +make new_deploy +``` + +## copy over modified aai_config.js +``` +cp /tmp/aai_config.js /opt/lindat-dspace/installation/webapps/xmlui/themes/UFAL/lib/js/ +``` + +## create dspace admin as tomcat8, so that the log files have the right owner +``` +su -s /bin/sh tomcat8 +/opt/lindat-dspace/installation/bin/dspace create-administrator +``` + +## start the dspace webapp +``` +cd /opt/repository/sources/dspace/utilities/project_helpers/scripts +/etc/init.d/tomcat8 start +``` diff --git a/clarin-dspace-docker/adapt_utilities_sql.sh b/clarin-dspace-docker/adapt_utilities_sql.sh new file mode 100755 index 0000000000000000000000000000000000000000..efd6727a0fb376adc84ee71e0f2f1f953d9609ef --- /dev/null +++ b/clarin-dspace-docker/adapt_utilities_sql.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +git clone https://github.com/commul/clarin-dspace.git -b clarin /tmp/dspace +cp /tmp/dspace/utilities/utilities.sql /tmp/sql/ +cp /tmp/dspace/utilities/license_definition.txt /tmp/sql/ +perl -pi -e "s#afile :utildir '/license_definition.txt'#afile '/tmp/sql/license_definition.txt'#;" /tmp/sql/utilities.sql +rm -rf /tmp/dspace diff --git a/clarin-dspace-docker/changelog.txt b/clarin-dspace-docker/changelog.txt new file mode 100644 index 0000000000000000000000000000000000000000..cac070f947f078c68852aab5b220206e57fccf40 --- /dev/null +++ b/clarin-dspace-docker/changelog.txt @@ -0,0 +1,25 @@ +Releases +======== + +gitlab.scientificnet.org:4567/commul/dspace/dspace + +0.3 +added dspace-entrypoint.sh v0.2 + +0.2 +Removed DSpace's installation from the Dockerfile. +Moved commul-customization into /tmp , so is possible to mount external volumes into /opt/repository/ and then copying files again from /tmp + +0.1 +Original build from https://github.com/commul/clarin-dspace-docker @ e5ad160 + + +gitlab.scientificnet.org:4567/commul/dspace/postgres + +0.3 +added env variables + +0.2 + +0.1 +Original build from https://github.com/commul/clarin-dspace-docker @ e5ad160 diff --git a/clarin-dspace-docker/clarin-dev2clarin.sh b/clarin-dspace-docker/clarin-dev2clarin.sh new file mode 100755 index 0000000000000000000000000000000000000000..303c5806b3d34249c0f274316f1f55f5ede06efa --- /dev/null +++ b/clarin-dspace-docker/clarin-dev2clarin.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# outside the docker +perl -pi -e 's/clarin-dev/clarin/g' commul-customization/shibboleth2.xml +perl -pi -e 's/clarin-dev/clarin/g' commul-customization/local.properties +perl -pi -e 's/lindat-dev_eurac_edu.pem/lindat_eurac_edu.crt/;' commul-customization/default-ssl +perl -pi -e 's/lindat-dev.key/lindat_eurac_edu.key/;' commul-customization/default-ssl +perl -pi -e 's/clarin-dev/clarin/g' commul-customization/default-ssl + +perl -pi -e 's/clarin-dev/clarin/g' commul-customization/aai.js +perl -pi -e 's/clarin-dev/clarin/g' commul-customization/aai_config.js +perl -pi -e 's/clarin-dev/clarin/g' commul-customization/lindat.eurac.edu.template.metadata.xml + +echo "Modified all files to point to clarin.eurac.edu instead of clarin-dev.eurac.edu. Do not forget to also edit docker-compose.yml and Dockerfile.nginx." diff --git a/clarin-dspace-docker/commul-customization/__init__.py b/clarin-dspace-docker/commul-customization/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1555a9c7790e61f1cbe489c1e10c12abde56ba3f --- /dev/null +++ b/clarin-dspace-docker/commul-customization/__init__.py @@ -0,0 +1,2353 @@ +from __future__ import print_function +import sys +import os +import io +import getopt +import re +import string +import errno +import copy +import locale +from jsbeautifier.__version__ import __version__ + +# +# The MIT License (MIT) + +# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors. + +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Originally written by Einar Lielmanis et al., +# Conversion to python by Einar Lielmanis, einar@jsbeautifier.org, +# Parsing improvement for brace-less and semicolon-less statements +# by Liam Newman <bitwiseman@gmail.com> +# Python is not my native language, feel free to push things around. +# +# Use either from command line (script displays its usage when run +# without any parameters), +# +# +# or, alternatively, use it as a module: +# +# import jsbeautifier +# res = jsbeautifier.beautify('your javascript string') +# res = jsbeautifier.beautify_file('some_file.js') +# +# you may specify some options: +# +# opts = jsbeautifier.default_options() +# opts.indent_size = 2 +# res = jsbeautifier.beautify('some javascript', opts) +# +# +# Here are the available options: (read source) + + +class BeautifierOptions: + def __init__(self): + self.indent_size = 4 + self.indent_char = ' ' + self.indent_with_tabs = False + self.eol = 'auto' + self.preserve_newlines = True + self.max_preserve_newlines = 10 + self.space_in_paren = False + self.space_in_empty_paren = False + self.e4x = False + self.jslint_happy = False + self.space_after_anon_function = False + self.brace_style = 'collapse' + self.keep_array_indentation = False + self.keep_function_indentation = False + self.eval_code = False + self.unescape_strings = False + self.wrap_line_length = 0 + self.break_chained_methods = False + self.end_with_newline = False + self.comma_first = False + self.operator_position = 'before-newline' + + self.css = None + self.js = None + self.html = None + + # For testing of beautify ignore:start directive + self.test_output_raw = False + self.editorconfig = False + + + + def mergeOpts(self, targetType): + finalOpts = copy.copy(self) + + local = getattr(finalOpts, targetType) + if (local): + delattr(finalOpts, targetType) + for key in local: + setattr(finalOpts, key, local[key]) + + return finalOpts + + def __repr__(self): + return \ +"""indent_size = %d +indent_char = [%s] +preserve_newlines = %s +max_preserve_newlines = %d +space_in_paren = %s +jslint_happy = %s +space_after_anon_function = %s +indent_with_tabs = %s +brace_style = %s +keep_array_indentation = %s +eval_code = %s +wrap_line_length = %s +unescape_strings = %s +""" % ( self.indent_size, + self.indent_char, + self.preserve_newlines, + self.max_preserve_newlines, + self.space_in_paren, + self.jslint_happy, + self.space_after_anon_function, + self.indent_with_tabs, + self.brace_style, + self.keep_array_indentation, + self.eval_code, + self.wrap_line_length, + self.unescape_strings, + ) + + +class BeautifierFlags: + def __init__(self, mode): + self.mode = mode + self.parent = None + self.last_text = '' + self.last_word = '' + self.declaration_statement = False + self.declaration_assignment = False + self.multiline_frame = False + self.inline_frame = False + self.if_block = False + self.else_block = False + self.do_block = False + self.do_while = False + self.import_block = False + self.in_case = False + self.in_case_statement = False + self.case_body = False + self.indentation_level = 0 + self.line_indent_level = 0 + self.start_line_index = 0 + self.ternary_depth = 0 + + def apply_base(self, flags_base, added_newline): + next_indent_level = flags_base.indentation_level + if not added_newline and \ + flags_base.line_indent_level > next_indent_level: + next_indent_level = flags_base.line_indent_level + + self.parent = flags_base + self.last_text = flags_base.last_text + self.last_word = flags_base.last_word + self.indentation_level = next_indent_level + +class Acorn: + def __init__(self): + # This is not pretty, but given how we did the version import + # it is the only way to do this without having setup.py fail on a missing six dependency. + self.six = __import__("six") + # This section of code was translated to python from acorn (javascript). + # + # Acorn was written by Marijn Haverbeke and released under an MIT + # license. The Unicode regexps (for identifiers and whitespace) were + # taken from [Esprima](http://esprima.org) by Ariya Hidayat. + # + # Git repositories for Acorn are available at + # + # http://marijnhaverbeke.nl/git/acorn + # https://github.com/marijnh/acorn.git + + # ## Character categories + + # Big ugly regular expressions that match characters in the + # whitespace, identifier, and identifier-start categories. These + # are only applied when a character is found to actually have a + # code point above 128. + + self.nonASCIIwhitespace = re.compile(self.six.u("[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]")) + self.nonASCIIidentifierStartChars = self.six.u("\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc") + self.nonASCIIidentifierChars = self.six.u("\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f") + self.nonASCIIidentifierStart = re.compile("[" + self.nonASCIIidentifierStartChars + "]") + self.nonASCIIidentifier = re.compile("[" + self.nonASCIIidentifierStartChars + self.nonASCIIidentifierChars + "]") + + # Whether a single character denotes a newline. + + self.newline = re.compile(self.six.u("[\n\r\u2028\u2029]")) + + # Matches a whole line break (where CRLF is considered a single + # line break). Used to count lines. + + # in javascript, these two differ + # in python they are the same, different methods are called on them + self.lineBreak = re.compile(self.six.u("\r\n|[\n\r\u2028\u2029]")) + self.allLineBreaks = self.lineBreak + + + # Test whether a given character code starts an identifier. + def isIdentifierStart(self, code): + if code < 65: + return code in [36, 64] # permit $ (36) and @ (64). @ is used in ES7 decorators. + if code < 91: + return True # 65 through 91 are uppercase letters + if code < 97: + return code == 95 # permit _ (95) + if code < 123: + return True # 97 through 123 are lowercase letters + return code >= 0xaa and self.nonASCIIidentifierStart.match(self.six.unichr(code)) != None + + # Test whether a given character is part of an identifier. + def isIdentifierChar(self, code): + if code < 48: + return code == 36 + if code < 58: + return True + if code < 65: + return False + if code < 91: + return True + if code < 97: + return code == 95 + if code < 123: + return True + return code >= 0xaa and self.nonASCIIidentifier.match(self.six.unichr(code)) != None + +class Token: + def __init__(self, type, text, newlines = 0, whitespace_before = '', mode = None, parent = None): + self.type = type + self.text = text + self.comments_before = [] + self.newlines = newlines + self.wanted_newline = newlines > 0 + self.whitespace_before = whitespace_before + self.parent = None + self.opened = None + self.directives = None + + +def default_options(): + return BeautifierOptions() + + +def beautify(string, opts = default_options() ): + b = Beautifier() + return b.beautify(string, opts) + +def set_file_editorconfig_opts(filename, js_options): + from editorconfig import get_properties, EditorConfigError + try: + _ecoptions = get_properties(os.path.abspath(filename)) + + if _ecoptions.get("indent_style") == "tab": + js_options.indent_with_tabs = True + elif _ecoptions.get("indent_style") == "space": + js_options.indent_with_tabs = False + + if _ecoptions.get("indent_size"): + js_options.indent_size = int(_ecoptions["indent_size"]) + + if _ecoptions.get("max_line_length"): + if _ecoptions.get("max_line_length") == "off": + js_options.wrap_line_length = 0 + else: + js_options.wrap_line_length = int(_ecoptions["max_line_length"]) + + if _ecoptions.get("insert_final_newline") == 'true': + js_options.end_with_newline = True + elif _ecoptions.get("insert_final_newline") == 'false': + js_options.end_with_newline = False + + if _ecoptions.get("end_of_line"): + if _ecoptions["end_of_line"] == "cr": + js_options.eol = '\r' + elif _ecoptions["end_of_line"] == "lf": + js_options.eol = '\n' + elif _ecoptions["end_of_line"] == "crlf": + js_options.eol = '\r\n' + + except EditorConfigError as ex: + # do not error on bad editor config + print("Error loading EditorConfig. Ignoring.", file=sys.stderr) + + +def beautify_file(file_name, opts = default_options() ): + input_string = '' + if file_name == '-': # stdin + try: + if sys.stdin.isatty(): + raise Exception() + + stream = sys.stdin + input_string = ''.join(stream.readlines()) + except Exception as ex: + print("Must pipe input or define at least one file.", file=sys.stderr) + usage(sys.stderr) + raise Exception() + else: + stream = io.open(file_name, 'rt', newline='') + input_string = ''.join(stream.readlines()) + + return beautify(input_string, opts) + + +def usage(stream=sys.stdout): + + print("jsbeautifier.py@" + __version__ + """ + +Javascript beautifier (http://jsbeautifier.org/) + +Usage: jsbeautifier.py [options] <infile> + + <infile> can be "-", which means stdin. + <outfile> defaults to stdout + +Input options: + + -i, --stdin Read input from stdin + +Output options: + + -s, --indent-size=NUMBER Indentation size. (default 4). + -c, --indent-char=CHAR Character to indent with. (default space). + -e, --eol=STRING Character(s) to use as line terminators. + (default first newline in file, otherwise "\\n") + -t, --indent-with-tabs Indent with tabs, overrides -s and -c + -d, --disable-preserve-newlines Do not preserve existing line breaks. + -P, --space-in-paren Add padding spaces within paren, ie. f( a, b ) + -E, --space-in-empty-paren Add a single space inside empty paren, ie. f( ) + -j, --jslint-happy More jslint-compatible output + -a, --space_after_anon_function Add a space before an anonymous function's parens, ie. function () + -b, --brace-style=collapse Brace style (collapse, expand, end-expand, none)(,preserve-inline) + -k, --keep-array-indentation Keep array indentation. + -r, --replace Write output in-place, replacing input + -o, --outfile=FILE Specify a file to output to (default stdout) + -f, --keep-function-indentation Do not re-indent function bodies defined in var lines. + -x, --unescape-strings Decode printable chars encoded in \\xNN notation. + -X, --e4x Pass E4X xml literals through untouched + -w, --wrap-line-length Attempt to wrap line when it exceeds this length. + NOTE: Line continues until next wrap point is found. + -n, --end_with_newline End output with newline + --editorconfig Enable setting configuration from EditorConfig + +Rarely needed options: + + --eval-code evaluate code if a JS interpreter is + installed. May be useful with some obfuscated + script but poses a potential security issue. + + -l, --indent-level=NUMBER Initial indentation level. (default 0). + + -h, --help, --usage Prints this help statement. + -v, --version Show the version + +""", file=stream) + if stream == sys.stderr: + return 1 + else: + return 0 + +OPERATOR_POSITION = { + 'before_newline': 'before-newline', + 'after_newline': 'after-newline', + 'preserve_newline': 'preserve-newline' +} +OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION['before_newline'], OPERATOR_POSITION['preserve_newline']]; + +def sanitizeOperatorPosition(opPosition): + if not opPosition: + return OPERATOR_POSITION['before_newline'] + elif opPosition not in OPERATOR_POSITION.values(): + raise ValueError("Invalid Option Value: The option 'operator_position' must be one of the following values\n" + + str(OPERATOR_POSITION.values()) + + "\nYou passed in: '" + opPosition + "'") + + return opPosition + +class MODE: + BlockStatement, Statement, ObjectLiteral, ArrayLiteral, \ + ForInitializer, Conditional, Expression = range(7) + +class Beautifier: + + def __init__(self, opts = default_options() ): + + self.opts = copy.copy(opts) + self.acorn = Acorn() + self.blank_state() + + def blank_state(self, js_source_text = None): + + # internal flags + self.flags = None + self.previous_flags = None + self.flag_store = [] + self.tokens = [] + self.token_pos = 0 + + + # force opts.space_after_anon_function to true if opts.jslint_happy + if self.opts.jslint_happy: + self.opts.space_after_anon_function = True + + if self.opts.indent_with_tabs: + self.opts.indent_char = "\t" + self.opts.indent_size = 1 + + if self.opts.eol == 'auto': + self.opts.eol = '\n' + if self.acorn.lineBreak.search(js_source_text or ''): + self.opts.eol = self.acorn.lineBreak.search(js_source_text).group() + + self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n') + + self.indent_string = self.opts.indent_char * self.opts.indent_size + + self.baseIndentString = '' + self.last_type = 'TK_START_BLOCK' # last token type + self.last_last_text = '' # pre-last token text + + preindent_index = 0; + if not js_source_text == None and len(js_source_text) > 0: + while preindent_index < len(js_source_text) and \ + js_source_text[preindent_index] in [' ', '\t'] : + self.baseIndentString += js_source_text[preindent_index] + preindent_index += 1 + js_source_text = js_source_text[preindent_index:] + + self.output = Output(self.indent_string, self.baseIndentString) + # If testing the ignore directive, start with output disable set to true + self.output.raw = self.opts.test_output_raw; + + self.set_mode(MODE.BlockStatement) + return js_source_text + + def beautify(self, s, opts = None ): + + if opts != None: + opts = opts.mergeOpts('js') + self.opts = copy.copy(opts) + + + #Compat with old form + if self.opts.brace_style == 'collapse-preserve-inline': + self.opts.brace_style = 'collapse,preserve-inline' + + split = re.compile("[^a-zA-Z0-9_\-]+").split(self.opts.brace_style) + self.opts.brace_style = split[0] + self.opts.brace_preserve_inline = (True if bool(split[1] == 'preserve-inline') else None) if len(split) > 1 else False + + if self.opts.brace_style not in ['expand', 'collapse', 'end-expand', 'none']: + raise(Exception('opts.brace_style must be "expand", "collapse", "end-expand", or "none".')) + + if self.opts.brace_preserve_inline == None: + raise(Exception('opts.brace_style second item must be "preserve-inline"')) + + s = self.blank_state(s) + + input = self.unpack(s, self.opts.eval_code) + + self.handlers = { + 'TK_START_EXPR': self.handle_start_expr, + 'TK_END_EXPR': self.handle_end_expr, + 'TK_START_BLOCK': self.handle_start_block, + 'TK_END_BLOCK': self.handle_end_block, + 'TK_WORD': self.handle_word, + 'TK_RESERVED': self.handle_word, + 'TK_SEMICOLON': self.handle_semicolon, + 'TK_STRING': self.handle_string, + 'TK_EQUALS': self.handle_equals, + 'TK_OPERATOR': self.handle_operator, + 'TK_COMMA': self.handle_comma, + 'TK_BLOCK_COMMENT': self.handle_block_comment, + 'TK_COMMENT': self.handle_comment, + 'TK_DOT': self.handle_dot, + 'TK_UNKNOWN': self.handle_unknown, + 'TK_EOF': self.handle_eof + } + + self.tokens = Tokenizer(input, self.opts, self.indent_string).tokenize() + self.token_pos = 0 + + current_token = self.get_token() + while current_token != None: + self.handlers[current_token.type](current_token) + + self.last_last_text = self.flags.last_text + self.last_type = current_token.type + self.flags.last_text = current_token.text + self.token_pos += 1 + current_token = self.get_token() + + + sweet_code = self.output.get_code() + if self.opts.end_with_newline: + sweet_code += '\n' + + if not self.opts.eol == '\n': + sweet_code = sweet_code.replace('\n', self.opts.eol) + + return sweet_code + + + def handle_whitespace_and_comments(self, local_token, preserve_statement_flags = False): + newlines = local_token.newlines + keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode) + + for comment_token in local_token.comments_before: + # The cleanest handling of inline comments is to treat them as though they aren't there. + # Just continue formatting and the behavior should be logical. + # Also ignore unknown tokens. Again, this should result in better behavior. + self.handle_whitespace_and_comments(comment_token, preserve_statement_flags) + self.handlers[comment_token.type](comment_token, preserve_statement_flags) + + + if keep_whitespace: + for i in range(newlines): + self.print_newline(i > 0, preserve_statement_flags) + else: # not keep_whitespace + if self.opts.max_preserve_newlines != 0 and newlines > self.opts.max_preserve_newlines: + newlines = self.opts.max_preserve_newlines + + if self.opts.preserve_newlines and newlines > 1: + self.print_newline(False, preserve_statement_flags) + for i in range(1, newlines): + self.print_newline(True, preserve_statement_flags) + + + def unpack(self, source, evalcode=False): + import jsbeautifier.unpackers as unpackers + try: + return unpackers.run(source, evalcode) + except unpackers.UnpackingError as error: + return source + + def is_special_word(self, s): + return s in ['case', 'return', 'do', 'if', 'throw', 'else'] + + def is_array(self, mode): + return mode == MODE.ArrayLiteral + + + def is_expression(self, mode): + return mode in [MODE.Expression, MODE.ForInitializer, MODE.Conditional] + + + _newline_restricted_tokens = ['break','continue','return', 'throw'] + def allow_wrap_or_preserved_newline(self, current_token, force_linewrap = False): + # never wrap the first token of a line. + if self.output.just_added_newline(): + return + + shouldPreserveOrForce = (self.opts.preserve_newlines and current_token.wanted_newline) or force_linewrap + operatorLogicApplies = self.flags.last_text in Tokenizer.positionable_operators or current_token.text in Tokenizer.positionable_operators + + if operatorLogicApplies: + shouldPrintOperatorNewline = (self.flags.last_text in Tokenizer.positionable_operators and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE) \ + or current_token.text in Tokenizer.positionable_operators + shouldPreserveOrForce = shouldPreserveOrForce and shouldPrintOperatorNewline + + if shouldPreserveOrForce: + self.print_newline(preserve_statement_flags = True) + elif self.opts.wrap_line_length > 0: + if self.last_type == 'TK_RESERVED' and self.flags.last_text in self._newline_restricted_tokens: + # These tokens should never have a newline inserted between + # them and the following expression. + return + proposed_line_length = self.output.current_line.get_character_count() + len(current_token.text) + if self.output.space_before_token: + proposed_line_length += 1 + + if proposed_line_length >= self.opts.wrap_line_length: + self.print_newline(preserve_statement_flags = True) + + + def print_newline(self, force_newline = False, preserve_statement_flags = False): + if not preserve_statement_flags: + if self.flags.last_text != ';' and self.flags.last_text != ',' and self.flags.last_text != '=' and self.last_type != 'TK_OPERATOR': + next_token = self.get_token(1) + while (self.flags.mode == MODE.Statement and + not (self.flags.if_block and next_token and next_token.type == 'TK_RESERVED' and next_token.text == 'else') and + not self.flags.do_block): + self.restore_mode() + + if self.output.add_new_line(force_newline): + self.flags.multiline_frame = True + + def print_token_line_indentation(self, current_token): + if self.output.just_added_newline(): + line = self.output.current_line + if self.opts.keep_array_indentation and self.is_array(self.flags.mode) and current_token.wanted_newline: + line.push(current_token.whitespace_before) + self.output.space_before_token = False + elif self.output.set_indent(self.flags.indentation_level): + self.flags.line_indent_level = self.flags.indentation_level + + + def print_token(self, current_token, s=None): + if self.output.raw: + self.output.add_raw_token(current_token) + return + + if self.opts.comma_first and self.last_type == 'TK_COMMA' and self.output.just_added_newline(): + if self.output.previous_line.last() == ',': + # if the comma was already at the start of the line, + # pull back onto that line and reprint the indentation + popped = self.output.previous_line.pop() + if self.output.previous_line.is_empty(): + self.output.previous_line.push(popped) + self.output.trim(True) + self.output.current_line.pop() + self.output.trim() + + # add the comma in front of the next token + self.print_token_line_indentation(current_token) + self.output.add_token(',') + self.output.space_before_token = True + + if s == None: + s = current_token.text + + self.print_token_line_indentation(current_token) + self.output.add_token(s); + + + def indent(self): + self.flags.indentation_level += 1 + + def deindent(self): + allow_deindent = self.flags.indentation_level > 0 and ((self.flags.parent == None) or self.flags.indentation_level > self.flags.parent.indentation_level) + + if allow_deindent: + self.flags.indentation_level -= 1 + + def set_mode(self, mode): + if self.flags: + self.flag_store.append(self.flags) + self.previous_flags = self.flags + else: + self.previous_flags = BeautifierFlags(mode) + + self.flags = BeautifierFlags(mode) + self.flags.apply_base(self.previous_flags, self.output.just_added_newline()) + self.flags.start_line_index = self.output.get_line_number(); + + def restore_mode(self): + if len(self.flag_store) > 0: + self.previous_flags = self.flags + self.flags = self.flag_store.pop() + if self.previous_flags.mode == MODE.Statement: + self.output.remove_redundant_indentation(self.previous_flags) + + + def start_of_object_property(self): + return self.flags.parent.mode == MODE.ObjectLiteral and self.flags.mode == MODE.Statement and \ + ((self.flags.last_text == ':' and self.flags.ternary_depth == 0) or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set'])) + + def start_of_statement(self, current_token): + if ( + (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const'] and current_token.type == 'TK_WORD') \ + or (self.last_type == 'TK_RESERVED' and self.flags.last_text== 'do') \ + or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['return', 'throw'] and not current_token.wanted_newline) \ + or (self.last_type == 'TK_RESERVED' and self.flags.last_text == 'else' \ + and not (current_token.type == 'TK_RESERVED' and current_token.text == 'if' and not len(current_token.comments_before))) \ + or (self.last_type == 'TK_END_EXPR' and (self.previous_flags.mode == MODE.ForInitializer or self.previous_flags.mode == MODE.Conditional)) \ + or (self.last_type == 'TK_WORD' and self.flags.mode == MODE.BlockStatement \ + and not self.flags.in_case + and not (current_token.text == '--' or current_token.text == '++') + and self.last_last_text != 'function' + and current_token.type != 'TK_WORD' and current_token.type != 'TK_RESERVED') \ + or (self.flags.mode == MODE.ObjectLiteral and \ + ((self.flags.last_text == ':' and self.flags.ternary_depth == 0) or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set']))) + ): + + self.set_mode(MODE.Statement) + self.indent() + + self.handle_whitespace_and_comments(current_token, True); + + # Issue #276: + # If starting a new statement with [if, for, while, do], push to a new line. + # if (a) if (b) if(c) d(); else e(); else f(); + if not self.start_of_object_property(): + self.allow_wrap_or_preserved_newline(current_token, current_token.type == 'TK_RESERVED' and current_token.text in ['do', 'for', 'if', 'while']) + + return True + else: + return False + + def get_token(self, offset = 0): + index = self.token_pos + offset + if index < 0 or index >= len(self.tokens): + return None + else: + return self.tokens[index] + + + def handle_start_expr(self, current_token): + if self.start_of_statement(current_token): + # The conditional starts the statement if appropriate. + pass + else: + self.handle_whitespace_and_comments(current_token) + + next_mode = MODE.Expression + + if current_token.text == '[': + if self.last_type == 'TK_WORD' or self.flags.last_text == ')': + if self.last_type == 'TK_RESERVED' and self.flags.last_text in Tokenizer.line_starters: + self.output.space_before_token = True + self.set_mode(next_mode) + self.print_token(current_token) + self.indent() + if self.opts.space_in_paren: + self.output.space_before_token = True + return + + next_mode = MODE.ArrayLiteral + + if self.is_array(self.flags.mode): + if self.flags.last_text == '[' or ( + self.flags.last_text == ',' and (self.last_last_text == ']' or self.last_last_text == '}')): + # ], [ goes to a new line + # }, [ goes to a new line + if not self.opts.keep_array_indentation: + self.print_newline() + + else: + if self.last_type == 'TK_RESERVED' and self.flags.last_text == 'for': + next_mode = MODE.ForInitializer + elif self.last_type == 'TK_RESERVED' and self.flags.last_text in ['if', 'while']: + next_mode = MODE.Conditional + else: + next_mode = MODE.Expression + + + if self.flags.last_text == ';' or self.last_type == 'TK_START_BLOCK': + self.print_newline() + elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.flags.last_text == '.': + # do nothing on (( and )( and ][ and ]( and .( + # TODO: Consider whether forcing this is required. Review failing tests when removed. + self.allow_wrap_or_preserved_newline(current_token, current_token.wanted_newline) + + elif not (self.last_type == 'TK_RESERVED' and current_token.text == '(') and self.last_type not in ['TK_WORD', 'TK_OPERATOR']: + self.output.space_before_token = True + elif (self.last_type == 'TK_RESERVED' and (self.flags.last_word == 'function' or self.flags.last_word == 'typeof')) or \ + (self.flags.last_text == '*' and ( + self.last_last_text in ['function', 'yield'] or + (self.flags.mode == MODE.ObjectLiteral and self.last_last_text in ['{', ',']))): + # function() vs function (), typeof() vs typeof () + # function*() vs function* (), yield*() vs yield* () + if self.opts.space_after_anon_function: + self.output.space_before_token = True + elif self.last_type == 'TK_RESERVED' and (self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == 'catch'): + # TODO: option space_before_conditional + self.output.space_before_token = True + + elif current_token.text == '(' and self.last_type == 'TK_RESERVED' and self.flags.last_word == 'await': + self.output.space_before_token = True + + + # Support of this kind of newline preservation: + # a = (b && + # (c || d)); + if self.last_type in ['TK_EQUALS', 'TK_OPERATOR']: + if not self.start_of_object_property(): + self.allow_wrap_or_preserved_newline(current_token) + + + # Support preserving wrapped arrow function expressions + # a.b('c', + # () => d.e + # ) + if current_token.text == '(' and self.last_type not in ['TK_WORD', 'TK_RESERVED']: + self.allow_wrap_or_preserved_newline(current_token) + + + self.set_mode(next_mode) + self.print_token(current_token) + + if self.opts.space_in_paren: + self.output.space_before_token = True + + # In all cases, if we newline while inside an expression it should be indented. + self.indent() + + + + def handle_end_expr(self, current_token): + # statements inside expressions are not valid syntax, but... + # statements must all be closed when their container closes + while self.flags.mode == MODE.Statement: + self.restore_mode() + + self.handle_whitespace_and_comments(current_token) + + if self.flags.multiline_frame: + self.allow_wrap_or_preserved_newline(current_token, current_token.text == ']' and self.is_array(self.flags.mode) and not self.opts.keep_array_indentation) + + if self.opts.space_in_paren: + if self.last_type == 'TK_START_EXPR' and not self.opts.space_in_empty_paren: + # empty parens are always "()" and "[]", not "( )" or "[ ]" + self.output.space_before_token = False + self.output.trim() + else: + self.output.space_before_token = True + + if current_token.text == ']' and self.opts.keep_array_indentation: + self.print_token(current_token) + self.restore_mode() + else: + self.restore_mode() + self.print_token(current_token) + + self.output.remove_redundant_indentation(self.previous_flags) + + # do {} while () // no statement required after + if self.flags.do_while and self.previous_flags.mode == MODE.Conditional: + self.previous_flags.mode = MODE.Expression + self.flags.do_block = False + self.flags.do_while = False + + def handle_start_block(self, current_token): + self.handle_whitespace_and_comments(current_token) + + # Check if this is a BlockStatement that should be treated as a ObjectLiteral + next_token = self.get_token(1) + second_token = self.get_token(2) + if second_token != None and \ + ((second_token.text in [':', ','] and next_token.type in ['TK_STRING', 'TK_WORD', 'TK_RESERVED']) \ + or (next_token.text in ['get', 'set', '...'] and second_token.type in ['TK_WORD', 'TK_RESERVED'])): + # We don't support TypeScript,but we didn't break it for a very long time. + # We'll try to keep not breaking it. + if not self.last_last_text in ['class','interface']: + self.set_mode(MODE.ObjectLiteral) + else: + self.set_mode(MODE.BlockStatement) + elif self.last_type == 'TK_OPERATOR' and self.flags.last_text == '=>': + # arrow function: (param1, paramN) => { statements } + self.set_mode(MODE.BlockStatement) + elif self.last_type in ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR'] or \ + (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['return', 'throw', 'import', 'default']): + # Detecting shorthand function syntax is difficult by scanning forward, + # so check the surrounding context. + # If the block is being returned, imported, export default, passed as arg, + # assigned with = or assigned in a nested object, treat as an ObjectLiteral. + self.set_mode(MODE.ObjectLiteral) + else: + self.set_mode(MODE.BlockStatement) + + empty_braces = (not next_token == None) and len(next_token.comments_before) == 0 and next_token.text == '}' + empty_anonymous_function = empty_braces and self.flags.last_word == 'function' and \ + self.last_type == 'TK_END_EXPR' + + if self.opts.brace_preserve_inline: # check for inline, set inline_frame if so + # search forward for newline wanted inside this block + index = 0 + check_token = None + self.flags.inline_frame = True + do_loop = True + while (do_loop): + index += 1 + check_token = self.get_token(index) + if check_token.wanted_newline: + self.flags.inline_frame = False + + do_loop = (check_token.type != 'TK_EOF' and + not (check_token.type == 'TK_END_BLOCK' and check_token.opened == current_token)) + + if (self.opts.brace_style == 'expand' or \ + (self.opts.brace_style == 'none' and current_token.wanted_newline)) and \ + not self.flags.inline_frame: + if self.last_type != 'TK_OPERATOR' and \ + (empty_anonymous_function or + self.last_type == 'TK_EQUALS' or + (self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text) and self.flags.last_text != 'else')): + self.output.space_before_token = True + else: + self.print_newline(preserve_statement_flags = True) + else: # collapse || inline_frame + if self.is_array(self.previous_flags.mode) and (self.last_type == 'TK_START_EXPR' or self.last_type == 'TK_COMMA'): + # if we're preserving inline, + # allow newline between comma and next brace. + if self.flags.inline_frame: + self.allow_wrap_or_preserved_newline(current_token) + self.flags.inline_frame = True + self.previous_flags.multiline_frame = self.previous_flags.multiline_frame or self.flags.multiline_frame + self.flags.multiline_frame = False + elif self.last_type == 'TK_COMMA': + self.output.space_before_token = True + + elif self.last_type not in ['TK_OPERATOR', 'TK_START_EXPR']: + if self.last_type == 'TK_START_BLOCK' and not self.flags.inline_frame: + self.print_newline() + else: + self.output.space_before_token = True + + self.print_token(current_token) + self.indent() + + + def handle_end_block(self, current_token): + # statements must all be closed when their container closes + self.handle_whitespace_and_comments(current_token) + + while self.flags.mode == MODE.Statement: + self.restore_mode() + + empty_braces = self.last_type == 'TK_START_BLOCK' + + if self.flags.inline_frame and not empty_braces: # try inline_frame (only set if opt.braces-preserve-inline) first + self.output.space_before_token = True; + elif self.opts.brace_style == 'expand': + if not empty_braces: + self.print_newline() + else: + # skip {} + if not empty_braces: + if self.is_array(self.flags.mode) and self.opts.keep_array_indentation: + self.opts.keep_array_indentation = False + self.print_newline() + self.opts.keep_array_indentation = True + else: + self.print_newline() + + self.restore_mode() + self.print_token(current_token) + + + def handle_word(self, current_token): + if current_token.type == 'TK_RESERVED': + if current_token.text in ['set', 'get'] and self.flags.mode != MODE.ObjectLiteral: + current_token.type = 'TK_WORD' + elif current_token.text in ['as', 'from'] and not self.flags.import_block: + current_token.type = 'TK_WORD' + elif self.flags.mode == MODE.ObjectLiteral: + next_token = self.get_token(1) + if next_token.text == ':': + current_token.type = 'TK_WORD' + + if self.start_of_statement(current_token): + # The conditional starts the statement if appropriate. + if self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const'] and current_token.type == 'TK_WORD': + self.flags.declaration_statement = True + + elif current_token.wanted_newline and \ + not self.is_expression(self.flags.mode) and \ + (self.last_type != 'TK_OPERATOR' or (self.flags.last_text == '--' or self.flags.last_text == '++')) and \ + self.last_type != 'TK_EQUALS' and \ + (self.opts.preserve_newlines or not (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const', 'set', 'get'])): + self.handle_whitespace_and_comments(current_token) + self.print_newline() + else: + self.handle_whitespace_and_comments(current_token) + + + if self.flags.do_block and not self.flags.do_while: + if current_token.type == 'TK_RESERVED' and current_token.text == 'while': + # do {} ## while () + self.output.space_before_token = True + self.print_token(current_token) + self.output.space_before_token = True + self.flags.do_while = True + return + else: + # do {} should always have while as the next word. + # if we don't see the expected while, recover + self.print_newline() + self.flags.do_block = False + + # if may be followed by else, or not + # Bare/inline ifs are tricky + # Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e(); + if self.flags.if_block: + if (not self.flags.else_block) and (current_token.type == 'TK_RESERVED' and current_token.text == 'else'): + self.flags.else_block = True + else: + while self.flags.mode == MODE.Statement: + self.restore_mode() + + self.flags.if_block = False + + if current_token.type == 'TK_RESERVED' and (current_token.text == 'case' or (current_token.text == 'default' and self.flags.in_case_statement)): + self.print_newline() + if self.flags.case_body or self.opts.jslint_happy: + self.flags.case_body = False + self.deindent() + self.print_token(current_token) + self.flags.in_case = True + self.flags.in_case_statement = True + return + + if self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']: + if not self.start_of_object_property(): + self.allow_wrap_or_preserved_newline(current_token) + + if current_token.type == 'TK_RESERVED' and current_token.text == 'function': + if (self.flags.last_text in ['}', ';'] or + (self.output.just_added_newline() and not (self.flags.last_text in ['(', '[', '{', ':', '=', ','] or self.last_type == 'TK_OPERATOR'))): + # make sure there is a nice clean space of at least one blank line + # before a new function definition, except in arrays + if not self.output.just_added_blankline() and len(current_token.comments_before) == 0: + self.print_newline() + self.print_newline(True) + + if self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD': + if self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set', 'new', 'return', 'export', 'async']: + self.output.space_before_token = True + elif self.last_type == 'TK_RESERVED' and self.flags.last_text == 'default' and self.last_last_text == 'export': + self.output.space_before_token = True + else: + self.print_newline() + elif self.last_type == 'TK_OPERATOR' or self.flags.last_text == '=': + # foo = function + self.output.space_before_token = True + elif not self.flags.multiline_frame and (self.is_expression(self.flags.mode) or self.is_array(self.flags.mode)): + # (function + pass + else: + self.print_newline() + + self.print_token(current_token) + self.flags.last_word = current_token.text + return + + prefix = 'NONE' + + if self.last_type == 'TK_END_BLOCK': + if self.previous_flags.inline_frame: + prefix = 'SPACE' + elif not (current_token.type == 'TK_RESERVED' and current_token.text in ['else', 'catch', 'finally', 'from']): + prefix = 'NEWLINE' + else: + if self.opts.brace_style in ['expand', 'end-expand'] or \ + (self.opts.brace_style == 'none' and current_token.wanted_newline): + prefix = 'NEWLINE' + else: + prefix = 'SPACE' + self.output.space_before_token = True + elif self.last_type == 'TK_SEMICOLON' and self.flags.mode == MODE.BlockStatement: + # TODO: Should this be for STATEMENT as well? + prefix = 'NEWLINE' + elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode): + prefix = 'SPACE' + elif self.last_type == 'TK_STRING': + prefix = 'NEWLINE' + elif self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD' or \ + (self.flags.last_text == '*' and ( + self.last_last_text in ['function', 'yield'] or + (self.flags.mode == MODE.ObjectLiteral and self.last_last_text in ['{', ',']))): + prefix = 'SPACE' + elif self.last_type == 'TK_START_BLOCK': + if self.flags.inline_frame: + prefix = 'SPACE' + else: + prefix = 'NEWLINE' + elif self.last_type == 'TK_END_EXPR': + self.output.space_before_token = True + prefix = 'NEWLINE' + + if current_token.type == 'TK_RESERVED' and current_token.text in Tokenizer.line_starters and self.flags.last_text != ')': + if self.flags.inline_frame or self.flags.last_text == 'else ' or self.flags.last_text == 'export': + prefix = 'SPACE' + else: + prefix = 'NEWLINE' + + if current_token.type == 'TK_RESERVED' and current_token.text in ['else', 'catch', 'finally']: + if ((not (self.last_type == 'TK_END_BLOCK' and self.previous_flags.mode == MODE.BlockStatement)) \ + or self.opts.brace_style == 'expand' \ + or self.opts.brace_style == 'end-expand' \ + or (self.opts.brace_style == 'none' and current_token.wanted_newline)) \ + and not self.flags.inline_frame: + self.print_newline() + else: + self.output.trim(True) + # If we trimmed and there's something other than a close block before us + # put a newline back in. Handles '} // comment' scenario. + if self.output.current_line.last() != '}': + self.print_newline() + + self.output.space_before_token = True + + elif prefix == 'NEWLINE': + if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text): + # no newline between return nnn + self.output.space_before_token = True + elif self.last_type != 'TK_END_EXPR': + if (self.last_type != 'TK_START_EXPR' or not (current_token.type == 'TK_RESERVED' and current_token.text in ['var', 'let', 'const'])) and self.flags.last_text != ':': + # no need to force newline on VAR - + # for (var x = 0... + if current_token.type == 'TK_RESERVED' and current_token.text == 'if' and self.flags.last_text == 'else': + self.output.space_before_token = True + else: + self.print_newline() + elif current_token.type == 'TK_RESERVED' and current_token.text in Tokenizer.line_starters and self.flags.last_text != ')': + self.print_newline() + elif self.flags.multiline_frame and self.is_array(self.flags.mode) and self.flags.last_text == ',' and self.last_last_text == '}': + self.print_newline() # }, in lists get a newline + elif prefix == 'SPACE': + self.output.space_before_token = True + + + self.print_token(current_token) + self.flags.last_word = current_token.text + + if current_token.type == 'TK_RESERVED': + if current_token.text == 'do': + self.flags.do_block = True + elif current_token.text == 'if': + self.flags.if_block = True + elif current_token.text == 'import': + self.flags.import_block = True + elif current_token.text == 'from' and self.flags.import_block: + self.flags.import_block = False + + + def handle_semicolon(self, current_token): + if self.start_of_statement(current_token): + # The conditional starts the statement if appropriate. + # Semicolon can be the start (and end) of a statement + self.output.space_before_token = False + else: + self.handle_whitespace_and_comments(current_token) + + next_token = self.get_token(1) + while (self.flags.mode == MODE.Statement and + not (self.flags.if_block and next_token and next_token.type == 'TK_RESERVED' and next_token.text == 'else') and + not self.flags.do_block): + self.restore_mode() + + if self.flags.import_block: + self.flags.import_block = False + + self.print_token(current_token) + + + def handle_string(self, current_token): + if self.start_of_statement(current_token): + # The conditional starts the statement if appropriate. + # One difference - strings want at least a space before + self.output.space_before_token = True + else: + self.handle_whitespace_and_comments(current_token) + + if self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD' or self.flags.inline_frame: + self.output.space_before_token = True + elif self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']: + if not self.start_of_object_property(): + self.allow_wrap_or_preserved_newline(current_token) + else: + self.print_newline() + + self.print_token(current_token) + + + def handle_equals(self, current_token): + if self.start_of_statement(current_token): + # The conditional starts the statement if appropriate. + pass + else: + self.handle_whitespace_and_comments(current_token) + + + if self.flags.declaration_statement: + # just got an '=' in a var-line, different line breaking rules will apply + self.flags.declaration_assignment = True + + self.output.space_before_token = True + self.print_token(current_token) + self.output.space_before_token = True + + + def handle_comma(self, current_token): + self.handle_whitespace_and_comments(current_token, True) + + self.print_token(current_token) + self.output.space_before_token = True + + if self.flags.declaration_statement: + if self.is_expression(self.flags.parent.mode): + # do not break on comma, for ( var a = 1, b = 2 + self.flags.declaration_assignment = False + + if self.flags.declaration_assignment: + self.flags.declaration_assignment = False + self.print_newline(preserve_statement_flags = True) + elif self.opts.comma_first: + # for comma-first, we want to allow a newline before the comma + # to turn into a newline after the comma, which we will fixup later + self.allow_wrap_or_preserved_newline(current_token) + + elif self.flags.mode == MODE.ObjectLiteral \ + or (self.flags.mode == MODE.Statement and self.flags.parent.mode == MODE.ObjectLiteral): + if self.flags.mode == MODE.Statement: + self.restore_mode() + + if not self.flags.inline_frame: + self.print_newline() + elif self.opts.comma_first: + # EXPR or DO_BLOCK + # for comma-first, we want to allow a newline before the comma + # to turn into a newline after the comma, which we will fixup later + self.allow_wrap_or_preserved_newline(current_token) + + + def handle_operator(self, current_token): + isGeneratorAsterisk = current_token.text == '*' and \ + ((self.last_type == 'TK_RESERVED' and self.flags.last_text in ['function', 'yield']) or + (self.last_type in ['TK_START_BLOCK', 'TK_COMMA', 'TK_END_BLOCK', 'TK_SEMICOLON'])) + isUnary = current_token.text in ['+', '-'] \ + and (self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR'] \ + or self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == ',') + + if self.start_of_statement(current_token): + # The conditional starts the statement if appropriate. + pass + else: + preserve_statement_flags = not isGeneratorAsterisk + self.handle_whitespace_and_comments(current_token, preserve_statement_flags) + + if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text): + # return had a special handling in TK_WORD + self.output.space_before_token = True + self.print_token(current_token) + return + + # hack for actionscript's import .*; + if current_token.text == '*' and self.last_type == 'TK_DOT': + self.print_token(current_token) + return + + if current_token.text == '::': + # no spaces around the exotic namespacing syntax operator + self.print_token(current_token) + return + + # Allow line wrapping between operators when operator_position is + # set to before or preserve + if self.last_type == 'TK_OPERATOR' and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE: + self.allow_wrap_or_preserved_newline(current_token) + + if current_token.text == ':' and self.flags.in_case: + self.flags.case_body = True + self.indent() + self.print_token(current_token) + self.print_newline() + self.flags.in_case = False + return + + space_before = True + space_after = True + in_ternary = False + + if current_token.text == ':': + if self.flags.ternary_depth == 0: + # Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant. + space_before = False + else: + self.flags.ternary_depth -= 1 + in_ternary = True + elif current_token.text == '?': + self.flags.ternary_depth += 1 + + # let's handle the operator_position option prior to any conflicting logic + if (not isUnary) and (not isGeneratorAsterisk) and \ + self.opts.preserve_newlines and current_token.text in Tokenizer.positionable_operators: + + isColon = current_token.text == ':' + isTernaryColon = isColon and in_ternary + isOtherColon = isColon and not in_ternary + + if self.opts.operator_position == OPERATOR_POSITION['before_newline']: + # if the current token is : and it's not a ternary statement then we set space_before to false + self.output.space_before_token = not isOtherColon + + self.print_token(current_token) + + if (not isColon) or isTernaryColon: + self.allow_wrap_or_preserved_newline(current_token) + + self.output.space_before_token = True + + return + + elif self.opts.operator_position == OPERATOR_POSITION['after_newline']: + # if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement, + # then print a newline. + self.output.space_before_token = True + + if (not isColon) or isTernaryColon: + if self.get_token(1).wanted_newline: + self.print_newline(preserve_statement_flags = True) + else: + self.allow_wrap_or_preserved_newline(current_token) + else: + self.output.space_before_token = False + + self.print_token(current_token) + + self.output.space_before_token = True + return + + elif self.opts.operator_position == OPERATOR_POSITION['preserve_newline']: + if not isOtherColon: + self.allow_wrap_or_preserved_newline(current_token) + + # if we just added a newline, or the current token is : and it's not a ternary statement, + # then we set space_before to false + self.output.space_before_token = not (self.output.just_added_newline() or isOtherColon) + + self.print_token(current_token) + + self.output.space_before_token = True + return + + if isGeneratorAsterisk: + self.allow_wrap_or_preserved_newline(current_token) + space_before = False + next_token = self.get_token(1) + space_after = next_token and next_token.type in ['TK_WORD','TK_RESERVED'] + elif current_token.text == '...': + self.allow_wrap_or_preserved_newline(current_token) + space_before = self.last_type == 'TK_START_BLOCK' + space_after = False + elif current_token.text in ['--', '++', '!', '~'] or isUnary: + space_before = False + space_after = False + + # http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1 + # if there is a newline between -- or ++ and anything else we should preserve it. + if current_token.wanted_newline and (current_token.text == '--' or current_token.text == '++'): + self.print_newline(preserve_statement_flags = True) + + if self.flags.last_text == ';' and self.is_expression(self.flags.mode): + # for (;; ++i) + # ^^ + space_before = True + + if self.last_type == 'TK_RESERVED': + space_before = True + elif self.last_type == 'TK_END_EXPR': + space_before = not (self.flags.last_text == ']' and current_token.text in ['--', '++']) + elif self.last_type == 'TK_OPERATOR': + # a++ + ++b + # a - -b + space_before = current_token.text in ['--', '-','++', '+'] and self.flags.last_text in ['--', '-','++', '+'] + # + and - are not unary when preceeded by -- or ++ operator + # a-- + b + # a * +b + # a - -b + if current_token.text in ['-', '+'] and self.flags.last_text in ['--', '++']: + space_after = True + + if (((self.flags.mode == MODE.BlockStatement and not self.flags.inline_frame) or self.flags.mode == MODE.Statement) + and self.flags.last_text in ['{', ';']): + # { foo: --i } + # foo(): --bar + self.print_newline() + + if space_before: + self.output.space_before_token = True + + self.print_token(current_token) + + if space_after: + self.output.space_before_token = True + + + + def handle_block_comment(self, current_token, preserve_statement_flags): + if self.output.raw: + self.output.add_raw_token(current_token) + if current_token.directives and current_token.directives.get('preserve') == 'end': + # If we're testing the raw output behavior, do not allow a directive to turn it off. + self.output.raw = self.opts.test_output_raw + return + + if current_token.directives: + self.print_newline(preserve_statement_flags = preserve_statement_flags) + self.print_token(current_token) + if current_token.directives.get('preserve') == 'start': + self.output.raw = True + + self.print_newline(preserve_statement_flags = True) + return + + # inline block + if not self.acorn.newline.search(current_token.text) and not current_token.wanted_newline: + self.output.space_before_token = True + self.print_token(current_token) + self.output.space_before_token = True + return + + lines = self.acorn.allLineBreaks.split(current_token.text) + javadoc = False + starless = False + last_indent = current_token.whitespace_before + last_indent_length = len(last_indent) + + # block comment starts with a new line + self.print_newline(preserve_statement_flags = preserve_statement_flags) + if len(lines) > 1: + javadoc = not any(l for l in lines[1:] if ( l.strip() == '' or (l.lstrip())[0] != '*')) + starless = all(l.startswith(last_indent) or l.strip() == '' for l in lines[1:]) + + # first line always indented + self.print_token(current_token, lines[0]) + for line in lines[1:]: + self.print_newline(preserve_statement_flags = True) + if javadoc: + # javadoc: reformat and re-indent + self.print_token(current_token, ' ' + line.lstrip()) + elif starless and len(line) > last_indent_length: + # starless: re-indent non-empty content, avoiding trim + self.print_token(current_token, line[last_indent_length:]) + else: + # normal comments output raw + self.output.add_token(line) + + self.print_newline(preserve_statement_flags = preserve_statement_flags) + + def handle_comment(self, current_token, preserve_statement_flags): + if current_token.wanted_newline: + self.print_newline(preserve_statement_flags = preserve_statement_flags) + + if not current_token.wanted_newline: + self.output.trim(True) + + self.output.space_before_token = True + self.print_token(current_token) + self.print_newline(preserve_statement_flags = preserve_statement_flags) + + + def handle_dot(self, current_token): + if self.start_of_statement(current_token): + # The conditional starts the statement if appropriate. + pass + else: + self.handle_whitespace_and_comments(current_token, True) + + if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text): + self.output.space_before_token = True + else: + # allow preserved newlines before dots in general + # force newlines on dots after close paren when break_chained - for bar().baz() + self.allow_wrap_or_preserved_newline(current_token, + self.flags.last_text == ')' and self.opts.break_chained_methods) + + self.print_token(current_token) + + def handle_unknown(self, current_token, preserve_statement_flags): + self.print_token(current_token) + if current_token.text[-1] == '\n': + self.print_newline(preserve_statement_flags = preserve_statement_flags) + + def handle_eof(self, current_token): + # Unwind any open statements + while self.flags.mode == MODE.Statement: + self.restore_mode() + + self.handle_whitespace_and_comments(current_token) + + + +def mkdir_p(path): + try: + if path: + os.makedirs(path) + except OSError as exc: # Python >2.5 + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise Exception() + +# Using object instead of string to allow for later expansion of info about each line +class OutputLine: + def __init__(self, parent): + self.__parent = parent + self.__character_count = 0 + self.__indent_count = -1 + + self.__items = [] + self.__empty = True + + def get_character_count(self): + return self.__character_count + + def is_empty(self): + return self.__empty + + def set_indent(self, level): + self.__character_count = self.__parent.baseIndentLength + level * self.__parent.indent_length + self.__indent_count = level; + + def last(self): + if not self.is_empty(): + return self.__items[-1] + else: + return None + + def push(self, input): + self.__items.append(input) + self.__character_count += len(input) + self.__empty = False + + + def pop(self): + item = None + if not self.is_empty(): + item = self.__items.pop() + self.__character_count -= len(item) + self.__empty = len(self.__items) == 0 + return item + + def remove_indent(self): + if self.__indent_count > 0: + self.__indent_count -= 1 + self.__character_count -= self.__parent.indent_length + + def trim(self): + while self.last() == ' ': + item = self._items.pop() + self.__character_count -= 1 + self.__empty = len(self.__items) == 0 + + def toString(self): + result = '' + if not self.is_empty(): + if self.__indent_count >= 0: + result = self.__parent.indent_cache[self.__indent_count] + result += ''.join(self.__items) + return result + + +class Output: + def __init__(self, indent_string, baseIndentString = ''): + + self.indent_string = indent_string + self.baseIndentString = baseIndentString + self.indent_cache = [ baseIndentString ] + self.baseIndentLength = len(baseIndentString) + self.indent_length = len(indent_string) + self.raw = False + self.lines = [] + self.previous_line = None + self.current_line = None + self.space_before_token = False + self.add_outputline() + + def add_outputline(self): + self.previous_line = self.current_line + self.current_line = OutputLine(self) + self.lines.append(self.current_line) + + def get_line_number(self): + return len(self.lines) + + def add_new_line(self, force_newline): + if len(self.lines) == 1 and self.just_added_newline(): + # no newline on start of file + return False + + if force_newline or not self.just_added_newline(): + if not self.raw: + self.add_outputline() + return True + return False + + def get_code(self): + sweet_code = "\n".join(line.toString() for line in self.lines) + return re.sub('[\r\n\t ]+$', '', sweet_code) + + def set_indent(self, level): + # Never indent your first output indent at the start of the file + if len(self.lines) > 1: + while level >= len(self.indent_cache): + self.indent_cache.append(self.indent_cache[-1] + self.indent_string) + + + self.current_line.set_indent(level) + return True + self.current_line.set_indent(0) + return False + + def add_raw_token(self, token): + for _ in range(token.newlines): + self.add_outputline() + + self.current_line.push(token.whitespace_before) + self.current_line.push(token.text) + self.space_before_token = False + + def add_token(self, printable_token): + self.add_space_before_token() + self.current_line.push(printable_token) + + def add_space_before_token(self): + if self.space_before_token and not self.just_added_newline(): + self.current_line.push(' ') + self.space_before_token = False + + def remove_redundant_indentation(self, frame): + # This implementation is effective but has some issues: + # - can cause line wrap to happen too soon due to indent removal + # after wrap points are calculated + # These issues are minor compared to ugly indentation. + + if frame.multiline_frame or frame.mode == MODE.ForInitializer or frame.mode == MODE.Conditional: + return + + # remove one indent from each line inside this section + index = frame.start_line_index + while index < len(self.lines): + self.lines[index].remove_indent() + index += 1 + + def trim(self, eat_newlines = False): + self.current_line.trim() + + while eat_newlines and len(self.lines) > 1 and self.current_line.is_empty(): + self.lines.pop() + self.current_line = self.lines[-1] + self.current_line.trim() + + if len(self.lines) > 1: + self.previous_line = self.lines[-2] + else: + self.previous_line = None + + def just_added_newline(self): + return self.current_line.is_empty() + + def just_added_blankline(self): + if self.just_added_newline(): + if len(self.lines) == 1: + return True + + line = self.lines[-2] + return line.is_empty() + + return False + +class InputScanner: + def __init__(self, input): + self.__input = input + self.__input_length = len(self.__input) + self.__position = 0 + + def back(self): + self.__position -= 1 + + def hasNext(self): + return self.__position < self.__input_length + + def next(self): + val = None + if self.hasNext(): + val = self.__input[self.__position] + self.__position += 1 + + return val; + + def peek(self, index = 0): + val = None + index += self.__position; + if index >= 0 and index < self.__input_length: + val = self.__input[index]; + + return val; + + def peekCharCode(self, index = 0): + val = 0 + index += self.__position; + if index >= 0 and index < self.__input_length: + val = ord(self.__input[index]) + + return val + + def test(self, pattern, index = 0): + index += self.__position; + return index >= 0 and index < self.__input_length and pattern.match(self.__input, index) + + def testChar(self, pattern, index = 0): + val = self.peek(index) + return val != None and pattern.match(val) + + def match(self, pattern): + pattern_match = None + if self.hasNext(): + pattern_match = pattern.match(self.__input, self.__position) + if pattern_match: + self.__position += len(pattern_match.group(0)); + + return pattern_match + + +class Tokenizer: + + whitespace = ["\n", "\r", "\t", " "] + digit = re.compile('[0-9]') + digit_bin = re.compile('[01]') + digit_oct = re.compile('[01234567]') + digit_hex = re.compile('[0123456789abcdefABCDEF]') + + positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ') + punct = (positionable_operators + + # non-positionable operators - these do not follow operator position settings + '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...'.split(' ')) + + # Words which always should start on a new line + line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',') + reserved_words = line_starters + ['do', 'in', 'of', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as'] + + def __init__ (self, input_string, opts, indent_string): + self.input = InputScanner(input_string) + self.opts = opts + self.indent_string = indent_string + self.acorn = Acorn() + # /* ... */ comment ends with nearest */ or end of file + self.block_comment_pattern = re.compile('([\s\S]*?)((?:\*\/)|$)') + + # comment ends just before nearest linefeed or end of file + self.comment_pattern = re.compile(self.acorn.six.u('([^\n\r\u2028\u2029]*)')) + + self.directives_block_pattern = re.compile('\/\* beautify( \w+[:]\w+)+ \*\/') + self.directive_pattern = re.compile(' (\w+)[:](\w+)') + self.directives_end_ignore_pattern = re.compile('([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)') + + self.template_pattern = re.compile('((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)') + + def tokenize(self): + self.in_html_comment = False + self.tokens = [] + + next = None + last = None + open = None + open_stack = [] + comments = [] + + while not (not last == None and last.type == 'TK_EOF'): + token_values = self.__tokenize_next() + next = Token(token_values[1], token_values[0], self.n_newlines, self.whitespace_before_token) + + while next.type == 'TK_COMMENT' or next.type == 'TK_BLOCK_COMMENT' or next.type == 'TK_UNKNOWN': + if next.type == 'TK_BLOCK_COMMENT': + next.directives = token_values[2] + + comments.append(next) + token_values = self.__tokenize_next() + next = Token(token_values[1], token_values[0], self.n_newlines, self.whitespace_before_token) + + if len(comments) > 0: + next.comments_before = comments + comments = [] + + if next.type == 'TK_START_BLOCK' or next.type == 'TK_START_EXPR': + next.parent = last + open_stack.append(open) + open = next + elif (next.type == 'TK_END_BLOCK' or next.type == 'TK_END_EXPR') and \ + (not open == None and ( \ + (next.text == ']' and open.text == '[') or \ + (next.text == ')' and open.text == '(') or \ + (next.text == '}' and open.text == '{'))): + next.parent = open.parent + next.opened = open + open = open_stack.pop() + + self.tokens.append(next) + last = next + return self.tokens + + def get_directives (self, text): + if not self.directives_block_pattern.match(text): + return None + + directives = {} + directive_match = self.directive_pattern.search(text) + while directive_match: + directives[directive_match.group(1)] = directive_match.group(2) + directive_match = self.directive_pattern.search(text, directive_match.end()) + + return directives + + + def __tokenize_next(self): + + whitespace_on_this_line = [] + self.n_newlines = 0 + self.whitespace_before_token = '' + + c = self.input.next() + + if c == None: + return '', 'TK_EOF' + + if len(self.tokens) > 0: + last_token = self.tokens[-1] + else: + # For the sake of tokenizing we can pretend that there was on open brace to start + last_token = Token('TK_START_BLOCK', '{') + + while c in self.whitespace: + if self.acorn.newline.match(c): + # treat \r\n as one newline + if not (c == '\n' and self.input.peek(-2) == '\r'): + self.n_newlines += 1 + whitespace_on_this_line = [] + else: + whitespace_on_this_line.append(c) + + c = self.input.next() + + if c == None: + return '', 'TK_EOF' + + if len(whitespace_on_this_line) != 0: + self.whitespace_before_token = ''.join(whitespace_on_this_line) + + if self.digit.match(c) or (c == '.' and self.input.testChar(self.digit)): + allow_decimal = True + allow_e = True + local_digit = self.digit + + if c == '0' and self.input.testChar(re.compile('[XxOoBb]')): + # switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits + allow_decimal = False + allow_e = False + if self.input.testChar(re.compile('[Bb]')): + local_digit = self.digit_bin + elif self.input.testChar(re.compile('[Oo]')): + local_digit = self.digit_oct + else: + local_digit = self.digit_hex + c += self.input.next() + elif c == '.': + # Already have a decimal for this literal, don't allow another + allow_decimal = False + else: + # we know this first loop will run. It keeps the logic simpler. + c = '' + self.input.back() + + # Add the digits + while self.input.testChar(local_digit): + c += self.input.next() + + if allow_decimal and self.input.peek() == '.': + c += self.input.next() + allow_decimal = False + + # a = 1.e-7 is valid, so we test for . then e in one loop + if allow_e and self.input.testChar(re.compile('[Ee]')): + c += self.input.next() + + if self.input.testChar(re.compile('[+-]')): + c += self.input.next() + + allow_e = False + allow_decimal = False + + return c, 'TK_WORD' + + if self.acorn.isIdentifierStart(self.input.peekCharCode(-1)): + if self.input.hasNext(): + while self.acorn.isIdentifierChar(self.input.peekCharCode()): + c += self.input.next() + if not self.input.hasNext(): + break + + if not (last_token.type == 'TK_DOT' \ + or (last_token.type == 'TK_RESERVED' and last_token.text in ['set', 'get'])) \ + and c in self.reserved_words: + if c == 'in' or c == 'of': # in and of are operators, need to hack + return c, 'TK_OPERATOR' + + return c, 'TK_RESERVED' + + return c, 'TK_WORD' + + if c in '([': + return c, 'TK_START_EXPR' + + if c in ')]': + return c, 'TK_END_EXPR' + + if c == '{': + return c, 'TK_START_BLOCK' + + if c == '}': + return c, 'TK_END_BLOCK' + + if c == ';': + return c, 'TK_SEMICOLON' + + if c == '/': + comment = '' + inline_comment = True + if self.input.peek() == '*': # peek /* .. */ comment + self.input.next() + comment_match = self.input.match(self.block_comment_pattern) + comment = '/*' + comment_match.group(0) + + directives = self.get_directives(comment) + if directives and directives.get('ignore') == 'start': + comment_match = self.input.match(self.directives_end_ignore_pattern) + comment += comment_match.group(0) + comment = re.sub(self.acorn.allLineBreaks, '\n', comment) + return comment, 'TK_BLOCK_COMMENT', directives + + if self.input.peek() == '/': # peek // comment + self.input.next() + comment_match = self.input.match(self.comment_pattern) + comment = '//' + comment_match.group(0) + return comment, 'TK_COMMENT' + + startXmlRegExp = re.compile('<()([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*(\'[^\']*\'|"[^"]*"|{[\s\S]+?}))*\s*(/?)\s*>') + + self.has_char_escapes = False + + if c == '`' or c == "'" or c == '"' or \ + ( \ + (c == '/') or \ + (self.opts.e4x and c == "<" and self.input.test(startXmlRegExp, -1)) \ + ) and ( \ + (last_token.type == 'TK_RESERVED' and last_token.text in ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield']) or \ + (last_token.type == 'TK_END_EXPR' and last_token.text == ')' and \ + last_token.parent and last_token.parent.type == 'TK_RESERVED' and last_token.parent.text in ['if', 'while', 'for']) or \ + (last_token.type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR', \ + 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'])): + sep = c + esc = False + esc1 = 0 + esc2 = 0 + resulting_string = c + in_char_class = False + + if sep == '/': + # handle regexp + in_char_class = False + while self.input.hasNext() and \ + (esc or in_char_class or self.input.peek()!= sep) and \ + not self.input.testChar(self.acorn.newline): + resulting_string += self.input.peek() + if not esc: + esc = self.input.peek() == '\\' + if self.input.peek() == '[': + in_char_class = True + elif self.input.peek() == ']': + in_char_class = False + else: + esc = False + self.input.next() + + elif self.opts.e4x and sep == '<': + # handle e4x xml literals + xmlRegExp = re.compile('[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*(\'[^\']*\'|"[^"]*"|{[\s\S]+?}))*\s*(/?)\s*>') + self.input.back() + xmlStr = "" + match = self.input.match(xmlRegExp) + if match: + rootTag = match.group(2) + rootTag = re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', rootTag)) + isCurlyRoot = rootTag.startswith('{') + depth = 0 + while (match): + isEndTag = match.group(1) + tagName = match.group(2) + isSingletonTag = (match.groups()[-1] != "") or (match.group(2)[0:8] == "![CDATA[") + if not isSingletonTag and ( + tagName == rootTag or (isCurlyRoot and re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', tagName)))): + if isEndTag: + depth -= 1 + else: + depth += 1 + + xmlStr += match.group(0) + if depth <= 0: + break + + match = self.input.match(xmlRegExp) + + + # if we didn't close correctly, keep unformatted. + if not match: + xmlStr += self.input.match(re.compile('[\s\S]*')).group(0) + + xmlStr = re.sub(self.acorn.allLineBreaks, '\n', xmlStr) + return xmlStr, 'TK_STRING' + + else: + + # handle string + def parse_string(self, resulting_string, delimiter, allow_unescaped_newlines = False, start_sub = None): + esc = False + while self.input.hasNext(): + current_char = self.input.peek() + if not (esc or (current_char != delimiter and + (allow_unescaped_newlines or not self.acorn.newline.match(current_char)))): + break + + # Handle \r\n linebreaks after escapes or in template strings + if (esc or allow_unescaped_newlines) and self.acorn.newline.match(current_char): + if current_char == '\r' and self.input.peek(1) == '\n': + self.input.next() + current_char = self.input.peek() + + resulting_string += '\n' + else: + resulting_string += current_char + + if esc: + if current_char == 'x' or current_char == 'u': + self.has_char_escapes = True + + esc = False + else: + esc = current_char == '\\' + + self.input.next() + + if start_sub and resulting_string.endswith(start_sub): + if delimiter == '`': + resulting_string = parse_string(self, resulting_string, '}', allow_unescaped_newlines, '`') + else: + resulting_string = parse_string(self, resulting_string, '`', allow_unescaped_newlines, '${') + + if self.input.hasNext(): + resulting_string += self.input.next() + + return resulting_string + + if sep == '`': + resulting_string = parse_string(self, resulting_string, '`', True, '${') + else: + resulting_string = parse_string(self, resulting_string, sep) + + + if self.has_char_escapes and self.opts.unescape_strings: + resulting_string = self.unescape_string(resulting_string) + + if self.input.peek() == sep: + resulting_string += self.input.next() + + if sep == '/': + # regexps may have modifiers /regexp/MOD, so fetch those too + # Only [gim] are valid, but if the user puts in garbage, do what we can to take it. + while self.input.hasNext() and self.acorn.isIdentifierStart(self.input.peekCharCode()): + resulting_string += self.input.next() + + resulting_string = re.sub(self.acorn.allLineBreaks, '\n', resulting_string) + + return resulting_string, 'TK_STRING' + + if c == '#': + + # she-bang + if len(self.tokens) == 0 and self.input.peek() == '!': + resulting_string = c + while self.input.hasNext() and c != '\n': + c = self.input.next() + resulting_string += c + return resulting_string.strip() + '\n', 'TK_UNKNOWN' + + + # Spidermonkey-specific sharp variables for circular references + # https://developer.mozilla.org/En/Sharp_variables_in_JavaScript + # http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 + sharp = '#' + if self.input.hasNext() and self.input.testChar(self.digit): + while True: + c = self.input.next() + sharp += c + if (not self.input.hasNext()) or c == '#' or c == '=': + break + if c == '#': + pass + elif self.input.peek() == '[' and self.input.peek(1) == ']': + sharp += '[]' + self.input.next() + self.input.next() + elif self.input.peek() == '{' and self.input.peek(1) == '}': + sharp += '{}' + self.input.next() + self.input.next() + return sharp, 'TK_WORD' + + if c == '<' and self.input.peek() in ['?', '%']: + self.input.back() + template_match = self.input.match(self.template_pattern) + if template_match: + c = template_match.group(0) + c = re.sub(self.acorn.allLineBreaks, '\n', c) + return c, 'TK_STRING' + + + if c == '<' and self.input.match(re.compile('\!--')): + c = '<!--' + while self.input.hasNext() and not self.input.testChar(self.acorn.newline): + c += self.input.next() + + self.in_html_comment = True + return c, 'TK_COMMENT' + + if c == '-' and self.in_html_comment and self.input.match(re.compile('->')): + self.in_html_comment = False + return '-->', 'TK_COMMENT' + + if c == '.': + if self.input.peek() == '.' and self.input.peek(1) == '.': + c += self.input.next() + self.input.next() + return c, 'TK_OPERATOR' + + return c, 'TK_DOT' + + if c in self.punct: + while self.input.hasNext() and c + self.input.peek() in self.punct: + c += self.input.next() + if not self.input.hasNext(): + break + + if c == ',': + return c, 'TK_COMMA' + if c == '=': + return c, 'TK_EQUALS' + + return c, 'TK_OPERATOR' + + return c, 'TK_UNKNOWN' + + def unescape_string(self, s): + # You think that a regex would work for this + # return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) { + # return String.fromCharCode(parseInt(val, 16)); + # }) + # However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun. + out = self.acorn.six.u('') + escaped = 0 + + input_scan = InputScanner(s) + matched = None + + while input_scan.hasNext(): + # Keep any whitespace, non-slash characters + # also keep slash pairs. + matched = input_scan.match(re.compile(r'([\s]|[^\\]|\\\\)+')) + + if matched: + out += matched.group(0) + + if input_scan.peek() != '\\': + continue + + input_scan.next() + if input_scan.peek() == 'x': + matched = input_scan.match(re.compile('x([0-9A-Fa-f]{2})')) + elif input_scan.peek() == 'u': + matched = input_scan.match(re.compile('u([0-9A-Fa-f]{4})')); + else: + out += '\\' + if input_scan.hasNext(): + out += input_scan.next() + continue + + # If there's some error decoding, return the original string + if not matched: + return s + + escaped = int(matched.group(1), 16) + + if escaped > 0x7e and escaped <= 0xff and matched.group(0).startswith('x'): + # we bail out on \x7f..\xff, + # leaving whole string escaped, + # as it's probably completely binary + return s + elif escaped >= 0x00 and escaped < 0x20: + # leave 0x00...0x1f escaped + out += '\\' + matched.group(0) + continue + elif escaped == 0x22 or escaped == 0x27 or escaped == 0x5c: + # single-quote, apostrophe, backslash - escape these + out += ('\\' + chr(escaped)) + else: + out += self.acorn.six.unichr(escaped) + + return out + +def isFileDifferent(filepath, expected): + try: + return (''.join(io.open(filepath, 'rt', newline='').readlines()) != expected) + except: + return True + + +def main(): + + argv = sys.argv[1:] + + try: + opts, args = getopt.getopt(argv, "s:c:e:o:rdEPjabkil:xhtfvXnCO:w:", + ['indent-size=','indent-char=','eol=''outfile=', 'replace', 'disable-preserve-newlines', + 'space-in-paren', 'space-in-empty-paren', 'jslint-happy', 'space-after-anon-function', + 'brace-style=', 'keep-array-indentation', 'indent-level=', 'unescape-strings', + 'help', 'usage', 'stdin', 'eval-code', 'indent-with-tabs', 'keep-function-indentation', 'version', + 'e4x', 'end-with-newline','comma-first','operator-position=','wrap-line-length','editorconfig']) + except getopt.GetoptError as ex: + print(ex, file=sys.stderr) + return usage(sys.stderr) + + js_options = default_options() + + file = None + outfile = 'stdout' + replace = False + if len(args) == 1: + file = args[0] + + for opt, arg in opts: + if opt in ('--keep-array-indentation', '-k'): + js_options.keep_array_indentation = True + if opt in ('--keep-function-indentation','-f'): + js_options.keep_function_indentation = True + elif opt in ('--outfile', '-o'): + outfile = arg + elif opt in ('--replace', '-r'): + replace = True + elif opt in ('--indent-size', '-s'): + js_options.indent_size = int(arg) + elif opt in ('--indent-char', '-c'): + js_options.indent_char = arg + elif opt in ('--eol', '-e'): + js_options.eol = arg + elif opt in ('--indent-with-tabs', '-t'): + js_options.indent_with_tabs = True + elif opt in ('--disable-preserve-newlines', '-d'): + js_options.preserve_newlines = False + elif opt in ('--space-in-paren', '-P'): + js_options.space_in_paren = True + elif opt in ('--space-in-empty-paren', '-E'): + js_options.space_in_empty_paren = True + elif opt in ('--jslint-happy', '-j'): + js_options.jslint_happy = True + elif opt in ('--space_after_anon_function', '-a'): + js_options.space_after_anon_function = True + elif opt in ('--eval-code'): + js_options.eval_code = True + elif opt in ('--brace-style', '-b'): + js_options.brace_style = arg + elif opt in ('--unescape-strings', '-x'): + js_options.unescape_strings = True + elif opt in ('--e4x', '-X'): + js_options.e4x = True + elif opt in ('--end-with-newline', '-n'): + js_options.end_with_newline = True + elif opt in ('--comma-first', '-C'): + js_options.comma_first = True + elif opt in ('--operator-position', '-O'): + js_options.operator_position = sanitizeOperatorPosition(arg) + elif opt in ('--wrap-line-length ', '-w'): + js_options.wrap_line_length = int(arg) + elif opt in ('--stdin', '-i'): + file = '-' + elif opt in ('--editorconfig'): + js_options.editorconfig = True + elif opt in ('--version', '-v'): + return print(__version__) + elif opt in ('--help', '--usage', '-h'): + return usage() + + + if not file: + file = '-' + + try: + if outfile == 'stdout' and replace and not file == '-': + outfile = file + + # Editorconfig used only on files, not stdin + if getattr(js_options, 'editorconfig'): + editorconfig_filepath = file + + if editorconfig_filepath == '-': + if outfile != 'stdout': + editorconfig_filepath = outfile + else: + fileType = 'js' + editorconfig_filepath = 'stdin.' + fileType + + # debug("EditorConfig is enabled for ", editorconfig_filepath); + js_options = copy.copy(js_options) + set_file_editorconfig_opts(editorconfig_filepath, js_options) + + pretty = beautify_file(file, js_options) + + if outfile == 'stdout': + # python automatically converts newlines in text to "\r\n" when on windows + # switch to binary to prevent this + if sys.platform == "win32": + import msvcrt + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) + + if sys.stdout.encoding is None: + # sys.stdout.encoding will be None if stdout is redirected (at + # least on Python 2), leading to encoding errors when stdout is + # redirected and the beautified content contains unicode + try: + sys.stdout.buffer.write(pretty.encode(locale.getpreferredencoding())) + except: + sys.stdout.write(pretty.encode(locale.getpreferredencoding())) + else: + sys.stdout.write(pretty) + + else: + if isFileDifferent(outfile, pretty): + mkdir_p(os.path.dirname(outfile)) + + # python automatically converts newlines in text to "\r\n" when on windows + # set newline to empty to prevent this + with io.open(outfile, 'wt', newline='') as f: + print('writing ' + outfile, file=sys.stderr) + try: + f.write(pretty) + except TypeError: + # This is not pretty, but given how we did the version import + # it is the only way to do this without having setup.py fail on a missing six dependency. + six = __import__("six") + f.write(six.u(pretty)) + + + except Exception as ex: + print(ex, file=sys.stderr) + return 1 + + # Success + return 0 diff --git a/clarin-dspace-docker/commul-customization/aai.js b/clarin-dspace-docker/commul-customization/aai.js new file mode 100644 index 0000000000000000000000000000000000000000..03edd649911917c8fb7ecfc352a0152ea6141669 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/aai.js @@ -0,0 +1,103 @@ +'use strict'; +(function(window){ + function AAI() { + var host = 'https://' + window.location.hostname, + ourEntityID = host.match("clarin-dev.eurac.edu") ? "https://clarin-dev.eurac.edu" : host; + this.defaults = { + //host : 'https://ufal-point.mff.cuni.cz', + host : host, //better default (useful when testing on ufal-point-dev) + // do not add protocol because an error will appear in the DJ dialog + // if you see the error, your SP is not listed among djc trusted (edugain is enough to be trusted) + responseUrl: window.location.protocol + '//clarin-dev.eurac.edu/aai/discojuiceDiscoveryResponse.html', + ourEntityID: ourEntityID + '/Shibboleth.sso/Metadata', + serviceName: '', + metadataFeed: host + '/xmlui/discojuice/feeds', + selector: 'a.signon', // selector for login button + autoInitialize: true, // auto attach DiscoJuice to DOM + textHelpMore: "First check you are searching under the right country.\nIf your provider is not listed, please read <a href='https://clarin-dev.eurac.edu/how-do-i-sign-up' style='text-decoration: underline; font-weight: bold;'>these instructions</a> to obtain an account." + }; + this.setup = function(options) { + var opts = jQuery.extend({}, this.defaults, options), + defaultCallback = function(e) { + window.location = opts.host + '/Shibboleth.sso/Login?SAMLDS=1&target=' + opts.target + '&entityID=' + window.encodeURIComponent(e.entityID); + }; + //console.log(opts); + if(!opts.target){ + throw 'You need to set the \'target\' parameter.'; + } + // call disco juice setup + if (!opts.autoInitialize || $(opts.selector).length > 0) { + if(! window.DiscoJuice ){ + throw 'Failed to find DiscoJuice. Did you include all that is necessary?'; + } + var djc = DiscoJuice.Hosted.getConfig( + opts.serviceName, + opts.ourEntityID, + opts.responseUrl, + [ ], + opts.host + '/Shibboleth.sso/Login?SAMLDS=1&target='+opts.target+'&entityID='); + djc.metadata = [opts.metadataFeed]; + djc.subtitle = "Login via Your home institution (e.g. university)"; + djc.textHelp = opts.textHelp; + djc.textHelpMore = opts.textHelpMore; + + djc.inlinemetadata = typeof opts.inlinemetadata === 'object' ? opts.inlinemetadata : []; + djc.inlinemetadata.push({ + 'country': '_all_', + 'entityID': 'https://idm.clarin.eu', + 'geo': {'lat': '51.833298', 'lon': '5.866699'}, + 'title': 'Clarin.eu website account', + 'weight': -801 + }); + djc.inlinemetadata.push({ + 'country': 'IT', + 'entityID': 'https://idp.eurac.edu/idp/shibboleth', + 'geo': {'lat': '46.494281', 'lon': '11.346842'}, + 'title': 'Eurac Research', + 'weight': -1000 + }); + + if(opts.localauth) { + djc.inlinemetadata.push( + { + 'entityID': 'local://', + 'auth': 'local', + 'title': 'Local authentication', + 'country': '_all_', + 'geo': null, + 'weight': 1000 + }); + djc.callback = function(e){ + var auth = e.auth || null; + switch(auth) { + case 'local': + DiscoJuice.UI.setScreen(opts.localauth); + jQuery('input#login').focus(); + break; + //case 'saml': + default: + defaultCallback(e); + break; + } + }; + } + + if (opts.callback && typeof opts.callback === 'function') { + djc.callback = function(e) { + opts.callback(e, opts, defaultCallback); + }; + } + + if (opts.autoInitialize) { + jQuery(opts.selector).DiscoJuice( djc ); + } + + return djc; + } //if jQuery(selector) + }; + } + + if (!window.aai) { + window.aai = new AAI(); + } +})(window); diff --git a/clarin-dspace-docker/commul-customization/aai_config.js b/clarin-dspace-docker/commul-customization/aai_config.js new file mode 100644 index 0000000000000000000000000000000000000000..289e5b4c728912d291a86dd997263293a1c83097 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/aai_config.js @@ -0,0 +1 @@ +"use strict";jQuery(document).ready(function(){var a=(function(){var b={};b.port=(window.location.port===""?"":":"+window.location.port);b.host=window.location.protocol+"//"+window.location.hostname;b.repoPath=jQuery("a#repository_path").attr("href");if(b.repoPath.charAt(b.repoPath.length-1)!=="/"){b.repoPath=b.repoPath+"/"}b.target=b.host+b.port+b.repoPath;b.responseUrl=(window.location.hostname.search("clarin-dev.eurac.edu")>=0)?"":b.host+b.port+b.repoPath+"themes/UFAL/lib/html/disco-juice.html?";b.metadataFeed=b.target+"discojuice/feeds";b.serviceName="Eurac Research CLARIN Centre";b.localauth='<form method="post" action="'+b.target+'password-login"> <p>Sign in using your local account obtained from the Eurac Research CLARIN Centre administrator.</p><p style="margin: 5px; color: #888" ><input type="text" name="login_email" style="font-size: 160%; width: 100%" id="login" /> <label for="login">E-Mail Address</label></p><p style="margin: 5px; color: #888" ><input type="password" name="login_password" style="font-size: 160%; width: 100%" id="pass" /> <label for="pass">Password</label></p><p style="margin: 5px; color: #607890; text-decoration: underline;"><a href="'+b.target+'forgot">Forgot your password?</a></p><p style="" ><input type="submit" style="margin: 20px 2px" name="submit" value="Sign in" /></p></form>';b.target=b.target+"shibboleth-login";return b})();if(!("aai" in window)){throw"Failed to find UFAL AAI object. See https://redmine.ms.mff.cuni.cz/projects/lindat-aai for more details!"}window.aai.setup(a)}); diff --git a/clarin-dspace-docker/commul-customization/catalina.sh b/clarin-dspace-docker/commul-customization/catalina.sh new file mode 100755 index 0000000000000000000000000000000000000000..42965df6bf5808c1a50d87ce9812cd09e9a47bee --- /dev/null +++ b/clarin-dspace-docker/commul-customization/catalina.sh @@ -0,0 +1,593 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Control Script for the CATALINA Server +# +# Environment Variable Prerequisites +# +# Do not set the variables in this script. Instead put them into a script +# setenv.sh in CATALINA_BASE/bin to keep your customizations separate. +# +# CATALINA_HOME May point at your Catalina "build" directory. +# +# CATALINA_BASE (Optional) Base directory for resolving dynamic portions +# of a Catalina installation. If not present, resolves to +# the same directory that CATALINA_HOME points to. +# +# CATALINA_OUT (Optional) Full path to a file where stdout and stderr +# will be redirected. +# Default is $CATALINA_BASE/logs/catalina.out +# +# CATALINA_OPTS (Optional) Java runtime options used when the "start", +# "run" or "debug" command is executed. +# Include here and not in JAVA_OPTS all options, that should +# only be used by Tomcat itself, not by the stop process, +# the version command etc. +# Examples are heap size, GC logging, JMX ports etc. +# +# CATALINA_TMPDIR (Optional) Directory path location of temporary directory +# the JVM should use (java.io.tmpdir). Defaults to +# $CATALINA_BASE/temp. +# +# JAVA_HOME Must point at your Java Development Kit installation. +# Required to run the with the "debug" argument. +# +# JRE_HOME Must point at your Java Runtime installation. +# Defaults to JAVA_HOME if empty. If JRE_HOME and JAVA_HOME +# are both set, JRE_HOME is used. +# +# JAVA_OPTS (Optional) Java runtime options used when any command +# is executed. +# Include here and not in CATALINA_OPTS all options, that +# should be used by Tomcat and also by the stop process, +# the version command etc. +# Most options should go into CATALINA_OPTS. +# +# JAVA_ENDORSED_DIRS (Optional) Lists of of colon separated directories +# containing some jars in order to allow replacement of APIs +# created outside of the JCP (i.e. DOM and SAX from W3C). +# It can also be used to update the XML parser implementation. +# Defaults to $CATALINA_HOME/endorsed. +# +# JPDA_TRANSPORT (Optional) JPDA transport used when the "jpda start" +# command is executed. The default is "dt_socket". +# +# JPDA_ADDRESS (Optional) Java runtime options used when the "jpda start" +# command is executed. The default is localhost:8000. +# +# JPDA_SUSPEND (Optional) Java runtime options used when the "jpda start" +# command is executed. Specifies whether JVM should suspend +# execution immediately after startup. Default is "n". +# +# JPDA_OPTS (Optional) Java runtime options used when the "jpda start" +# command is executed. If used, JPDA_TRANSPORT, JPDA_ADDRESS, +# and JPDA_SUSPEND are ignored. Thus, all required jpda +# options MUST be specified. The default is: +# +# -agentlib:jdwp=transport=$JPDA_TRANSPORT, +# address=$JPDA_ADDRESS,server=y,suspend=$JPDA_SUSPEND +# +# JSSE_OPTS (Optional) Java runtime options used to control the TLS +# implementation when JSSE is used. Default is: +# "-Djdk.tls.ephemeralDHKeySize=2048" +# +# CATALINA_PID (Optional) Path of the file which should contains the pid +# of the catalina startup java process, when start (fork) is +# used +# +# LOGGING_CONFIG (Optional) Override Tomcat's logging config file +# Example (all one line) +# LOGGING_CONFIG="-Djava.util.logging.config.file=$CATALINA_BASE/conf/logging.properties" +# +# LOGGING_MANAGER (Optional) Override Tomcat's logging manager +# Example (all one line) +# LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager" +# ----------------------------------------------------------------------------- + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false +darwin=false +os400=false +case "`uname`" in +CYGWIN*) cygwin=true;; +Darwin*) darwin=true;; +OS400*) os400=true;; +esac + +# resolve links - $0 may be a softlink +PRG="$0" + +while [ -h "$PRG" ]; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`/"$link" + fi +done + +# Get standard environment variables +PRGDIR=`dirname "$PRG"` + +# Only set CATALINA_HOME if not already set +[ -z "$CATALINA_HOME" ] && CATALINA_HOME=`cd "$PRGDIR/.." >/dev/null; pwd` + +# Copy CATALINA_BASE from CATALINA_HOME if not already set +[ -z "$CATALINA_BASE" ] && CATALINA_BASE="$CATALINA_HOME" + +# Ensure that any user defined CLASSPATH variables are not used on startup, +# but allow them to be specified in setenv.sh, in rare case when it is needed. +CLASSPATH= + +if [ -r "$CATALINA_BASE/bin/setenv.sh" ]; then + . "$CATALINA_BASE/bin/setenv.sh" +elif [ -r "$CATALINA_HOME/bin/setenv.sh" ]; then + . "$CATALINA_HOME/bin/setenv.sh" +fi + +export CATALINA_PID="$CATALINA_HOME/catalina_pid.txt" + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$JRE_HOME" ] && JRE_HOME=`cygpath --unix "$JRE_HOME"` + [ -n "$CATALINA_HOME" ] && CATALINA_HOME=`cygpath --unix "$CATALINA_HOME"` + [ -n "$CATALINA_BASE" ] && CATALINA_BASE=`cygpath --unix "$CATALINA_BASE"` + [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# Ensure that neither CATALINA_HOME nor CATALINA_BASE contains a colon +# as this is used as the separator in the classpath and Java provides no +# mechanism for escaping if the same character appears in the path. +case $CATALINA_HOME in + *:*) echo "Using CATALINA_HOME: $CATALINA_HOME"; + echo "Unable to start as CATALINA_HOME contains a colon (:) character"; + exit 1; +esac +case $CATALINA_BASE in + *:*) echo "Using CATALINA_BASE: $CATALINA_BASE"; + echo "Unable to start as CATALINA_BASE contains a colon (:) character"; + exit 1; +esac + +# For OS400 +if $os400; then + # Set job priority to standard for interactive (interactive - 6) by using + # the interactive priority - 6, the helper threads that respond to requests + # will be running at the same priority as interactive jobs. + COMMAND='chgjob job('$JOBNAME') runpty(6)' + system $COMMAND + + # Enable multi threading + export QIBM_MULTI_THREADED=Y +fi + +# Get standard Java environment variables +if $os400; then + # -r will Only work on the os400 if the files are: + # 1. owned by the user + # 2. owned by the PRIMARY group of the user + # this will not work if the user belongs in secondary groups + . "$CATALINA_HOME"/bin/setclasspath.sh +else + if [ -r "$CATALINA_HOME"/bin/setclasspath.sh ]; then + . "$CATALINA_HOME"/bin/setclasspath.sh + else + echo "Cannot find $CATALINA_HOME/bin/setclasspath.sh" + echo "This file is needed to run this program" + exit 1 + fi +fi + +# Add on extra jar files to CLASSPATH +if [ ! -z "$CLASSPATH" ] ; then + CLASSPATH="$CLASSPATH": +fi +CLASSPATH="$CLASSPATH""$CATALINA_HOME"/bin/bootstrap.jar + +if [ -z "$CATALINA_OUT" ] ; then + CATALINA_OUT="$CATALINA_BASE"/logs/catalina.out +fi + +if [ -z "$CATALINA_TMPDIR" ] ; then + # Define the java.io.tmpdir to use for Catalina + CATALINA_TMPDIR="$CATALINA_BASE"/temp +fi + +# Add tomcat-juli.jar to classpath +# tomcat-juli.jar can be over-ridden per instance +if [ -r "$CATALINA_BASE/bin/tomcat-juli.jar" ] ; then + CLASSPATH=$CLASSPATH:$CATALINA_BASE/bin/tomcat-juli.jar +else + CLASSPATH=$CLASSPATH:$CATALINA_HOME/bin/tomcat-juli.jar +fi + +# Bugzilla 37848: When no TTY is available, don't output to console +have_tty=0 +if [ "`tty`" != "not a tty" ]; then + have_tty=1 +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + JAVA_HOME=`cygpath --absolute --windows "$JAVA_HOME"` + JRE_HOME=`cygpath --absolute --windows "$JRE_HOME"` + CATALINA_HOME=`cygpath --absolute --windows "$CATALINA_HOME"` + CATALINA_BASE=`cygpath --absolute --windows "$CATALINA_BASE"` + CATALINA_TMPDIR=`cygpath --absolute --windows "$CATALINA_TMPDIR"` + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"` +fi + +if [ -z "$JSSE_OPTS" ] ; then + JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048" +fi +JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS" + +# Set juli LogManager config file if it is present and an override has not been issued +if [ -z "$LOGGING_CONFIG" ]; then + if [ -r "$CATALINA_BASE"/conf/logging.properties ]; then + LOGGING_CONFIG="-Djava.util.logging.config.file=$CATALINA_BASE/conf/logging.properties" + else + # Bugzilla 45585 + LOGGING_CONFIG="-Dnop" + fi +fi + +if [ -z "$LOGGING_MANAGER" ]; then + LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager" +fi + +# Uncomment the following line to make the umask available when using the +# org.apache.catalina.security.SecurityListener +#JAVA_OPTS="$JAVA_OPTS -Dorg.apache.catalina.security.SecurityListener.UMASK=`umask`" + +# ----- Execute The Requested Command ----------------------------------------- + +# Bugzilla 37848: only output this if we have a TTY +if [ $have_tty -eq 1 ]; then + echo "Using CATALINA_BASE: $CATALINA_BASE" + echo "Using CATALINA_HOME: $CATALINA_HOME" + echo "Using CATALINA_TMPDIR: $CATALINA_TMPDIR" + if [ "$1" = "debug" ] ; then + echo "Using JAVA_HOME: $JAVA_HOME" + else + echo "Using JRE_HOME: $JRE_HOME" + fi + echo "Using CLASSPATH: $CLASSPATH" + if [ ! -z "$CATALINA_PID" ]; then + echo "Using CATALINA_PID: $CATALINA_PID" + fi +fi + +if [ "$1" = "jpda" ] ; then + if [ -z "$JPDA_TRANSPORT" ]; then + JPDA_TRANSPORT="dt_socket" + fi + if [ -z "$JPDA_ADDRESS" ]; then + JPDA_ADDRESS="localhost:8000" + fi + if [ -z "$JPDA_SUSPEND" ]; then + JPDA_SUSPEND="n" + fi + if [ -z "$JPDA_OPTS" ]; then + JPDA_OPTS="-agentlib:jdwp=transport=$JPDA_TRANSPORT,address=$JPDA_ADDRESS,server=y,suspend=$JPDA_SUSPEND" + fi + CATALINA_OPTS="$JPDA_OPTS $CATALINA_OPTS" + shift +fi + +if [ "$1" = "debug" ] ; then + if $os400; then + echo "Debug command not available on OS400" + exit 1 + else + shift + if [ "$1" = "-security" ] ; then + if [ $have_tty -eq 1 ]; then + echo "Using Security Manager" + fi + shift + exec "$_RUNJDB" "$LOGGING_CONFIG" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ + -sourcepath "$CATALINA_HOME"/../../java \ + -Djava.security.manager \ + -Djava.security.policy=="$CATALINA_BASE"/conf/catalina.policy \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap "$@" start + else + exec "$_RUNJDB" "$LOGGING_CONFIG" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ + -sourcepath "$CATALINA_HOME"/../../java \ + -Dcatalina.base="$CATALINA_BASE" \ + -Dcatalina.home="$CATALINA_HOME" \ + -Djava.io.tmpdir="$CATALINA_TMPDIR" \ + org.apache.catalina.startup.Bootstrap "$@" start + fi + fi + +elif [ "$1" = "run" ]; then + + shift + if [ "$1" = "-security" ] ; then + if [ $have_tty -eq 1 ]; then + echo "Using Security Manager" + fi + shift + eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \ + -Djava.security.manager \ + -Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \ + -Dcatalina.base="\"$CATALINA_BASE\"" \ + -Dcatalina.home="\"$CATALINA_HOME\"" \ + -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ + org.apache.catalina.startup.Bootstrap "$@" start + else + eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \ + -Dcatalina.base="\"$CATALINA_BASE\"" \ + -Dcatalina.home="\"$CATALINA_HOME\"" \ + -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ + org.apache.catalina.startup.Bootstrap "$@" start + fi + +elif [ "$1" = "start" ] ; then + + if [ ! -z "$CATALINA_PID" ]; then + if [ -f "$CATALINA_PID" ]; then + if [ -s "$CATALINA_PID" ]; then + echo "Existing PID file found during start." + if [ -r "$CATALINA_PID" ]; then + PID=`cat "$CATALINA_PID"` + ps -p $PID >/dev/null 2>&1 + if [ $? -eq 0 ] ; then + echo "Tomcat appears to still be running with PID $PID. Start aborted." + echo "If the following process is not a Tomcat process, remove the PID file and try again:" + ps -f -p $PID + exit 1 + else + echo "Removing/clearing stale PID file." + rm -f "$CATALINA_PID" >/dev/null 2>&1 + if [ $? != 0 ]; then + if [ -w "$CATALINA_PID" ]; then + cat /dev/null > "$CATALINA_PID" + else + echo "Unable to remove or clear stale PID file. Start aborted." + exit 1 + fi + fi + fi + else + echo "Unable to read PID file. Start aborted." + exit 1 + fi + else + rm -f "$CATALINA_PID" >/dev/null 2>&1 + if [ $? != 0 ]; then + if [ ! -w "$CATALINA_PID" ]; then + echo "Unable to remove or write to empty PID file. Start aborted." + exit 1 + fi + fi + fi + fi + fi + + shift + touch "$CATALINA_OUT" + if [ "$1" = "-security" ] ; then + if [ $have_tty -eq 1 ]; then + echo "Using Security Manager" + fi + shift + eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \ + -Djava.security.manager \ + -Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \ + -Dcatalina.base="\"$CATALINA_BASE\"" \ + -Dcatalina.home="\"$CATALINA_HOME\"" \ + -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ + org.apache.catalina.startup.Bootstrap "$@" start \ + >> "$CATALINA_OUT" 2>&1 "&" + + else + eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \ + -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \ + -Dcatalina.base="\"$CATALINA_BASE\"" \ + -Dcatalina.home="\"$CATALINA_HOME\"" \ + -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ + org.apache.catalina.startup.Bootstrap "$@" start \ + >> "$CATALINA_OUT" 2>&1 "&" + + fi + + if [ ! -z "$CATALINA_PID" ]; then + echo $! > "$CATALINA_PID" + fi + + echo "Tomcat started." + +elif [ "$1" = "stop" ] ; then + + shift + + SLEEP=5 + if [ ! -z "$1" ]; then + echo $1 | grep "[^0-9]" >/dev/null 2>&1 + if [ $? -gt 0 ]; then + SLEEP=$1 + shift + fi + fi + + FORCE=0 + if [ "$1" = "-force" ]; then + shift + FORCE=1 + fi + + if [ ! -z "$CATALINA_PID" ]; then + if [ -f "$CATALINA_PID" ]; then + if [ -s "$CATALINA_PID" ]; then + kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1 + if [ $? -gt 0 ]; then + echo "PID file found but no matching process was found. Stop aborted." + exit 1 + fi + else + echo "PID file is empty and has been ignored." + fi + else + echo "\$CATALINA_PID was set but the specified file does not exist. Is Tomcat running? Stop aborted." + exit 1 + fi + fi + + eval "\"$_RUNJAVA\"" $LOGGING_MANAGER $JAVA_OPTS \ + -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \ + -Dcatalina.base="\"$CATALINA_BASE\"" \ + -Dcatalina.home="\"$CATALINA_HOME\"" \ + -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ + org.apache.catalina.startup.Bootstrap "$@" stop + + # stop failed. Shutdown port disabled? Try a normal kill. + if [ $? != 0 ]; then + if [ ! -z "$CATALINA_PID" ]; then + echo "The stop command failed. Attempting to signal the process to stop through OS signal." + kill -15 `cat "$CATALINA_PID"` >/dev/null 2>&1 + fi + fi + + if [ ! -z "$CATALINA_PID" ]; then + if [ -f "$CATALINA_PID" ]; then + while [ $SLEEP -ge 0 ]; do + kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1 + if [ $? -gt 0 ]; then + rm -f "$CATALINA_PID" >/dev/null 2>&1 + if [ $? != 0 ]; then + if [ -w "$CATALINA_PID" ]; then + cat /dev/null > "$CATALINA_PID" + # If Tomcat has stopped don't try and force a stop with an empty PID file + FORCE=0 + else + echo "The PID file could not be removed or cleared." + fi + fi + echo "Tomcat stopped." + break + fi + if [ $SLEEP -gt 0 ]; then + sleep 1 + fi + if [ $SLEEP -eq 0 ]; then + echo "Tomcat did not stop in time." + if [ $FORCE -eq 0 ]; then + echo "PID file was not removed." + fi + echo "To aid diagnostics a thread dump has been written to standard out." + kill -3 `cat "$CATALINA_PID"` + fi + SLEEP=`expr $SLEEP - 1 ` + done + fi + fi + + KILL_SLEEP_INTERVAL=5 + if [ $FORCE -eq 1 ]; then + if [ -z "$CATALINA_PID" ]; then + echo "Kill failed: \$CATALINA_PID not set" + else + if [ -f "$CATALINA_PID" ]; then + PID=`cat "$CATALINA_PID"` + echo "Killing Tomcat with the PID: $PID" + kill -9 $PID + while [ $KILL_SLEEP_INTERVAL -ge 0 ]; do + kill -0 `cat "$CATALINA_PID"` >/dev/null 2>&1 + if [ $? -gt 0 ]; then + rm -f "$CATALINA_PID" >/dev/null 2>&1 + if [ $? != 0 ]; then + if [ -w "$CATALINA_PID" ]; then + cat /dev/null > "$CATALINA_PID" + else + echo "The PID file could not be removed." + fi + fi + echo "The Tomcat process has been killed." + break + fi + if [ $KILL_SLEEP_INTERVAL -gt 0 ]; then + sleep 1 + fi + KILL_SLEEP_INTERVAL=`expr $KILL_SLEEP_INTERVAL - 1 ` + done + if [ $KILL_SLEEP_INTERVAL -lt 0 ]; then + echo "Tomcat has not been killed completely yet. The process might be waiting on some system call or might be UNINTERRUPTIBLE." + fi + fi + fi + fi + +elif [ "$1" = "configtest" ] ; then + + eval "\"$_RUNJAVA\"" $LOGGING_MANAGER $JAVA_OPTS \ + -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \ + -Dcatalina.base="\"$CATALINA_BASE\"" \ + -Dcatalina.home="\"$CATALINA_HOME\"" \ + -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ + org.apache.catalina.startup.Bootstrap configtest + result=$? + if [ $result -ne 0 ]; then + echo "Configuration error detected!" + fi + exit $result + +elif [ "$1" = "version" ] ; then + + "$_RUNJAVA" \ + -classpath "$CATALINA_HOME/lib/catalina.jar" \ + org.apache.catalina.util.ServerInfo + +else + + echo "Usage: catalina.sh ( commands ... )" + echo "commands:" + if $os400; then + echo " debug Start Catalina in a debugger (not available on OS400)" + echo " debug -security Debug Catalina with a security manager (not available on OS400)" + else + echo " debug Start Catalina in a debugger" + echo " debug -security Debug Catalina with a security manager" + fi + echo " jpda start Start Catalina under JPDA debugger" + echo " run Start Catalina in the current window" + echo " run -security Start in the current window with security manager" + echo " start Start Catalina in a separate window" + echo " start -security Start in a separate window with security manager" + echo " stop Stop Catalina, waiting up to 5 seconds for the process to end" + echo " stop n Stop Catalina, waiting up to n seconds for the process to end" + echo " stop -force Stop Catalina, wait up to 5 seconds and then use kill -KILL if still running" + echo " stop n -force Stop Catalina, wait up to n seconds and then use kill -KILL if still running" + echo " configtest Run a basic syntax check on server.xml - check exit code for result" + echo " version What version of tomcat are you running?" + echo "Note: Waiting for the process to end and use of the -force option require that \$CATALINA_PID is defined" + exit 1 + +fi diff --git a/clarin-dspace-docker/commul-customization/clarin.eurac.edu.template.metadata.xml b/clarin-dspace-docker/commul-customization/clarin.eurac.edu.template.metadata.xml new file mode 100644 index 0000000000000000000000000000000000000000..74dcff81bb7f30b1df76f7047911c74d530927ff --- /dev/null +++ b/clarin-dspace-docker/commul-customization/clarin.eurac.edu.template.metadata.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="_5d15d5a81bc952da1d34986ebc7713c0d5c9ea60" entityID="https://dspace-clarin-it.ilc.cnr.it/Shibboleth.sso/Metadata"> + <md:Extensions> + <mdattr:EntityAttributes xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"> + <saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Name="http://macedir.org/entity-category" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"> + <saml:AttributeValue>http://www.geant.net/uri/dataprotection-code-of-conduct/v1</saml:AttributeValue> + </saml:Attribute> + <saml:Attribute + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + Name="http://macedir.org/entity-category" + NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"> + <saml:AttributeValue>http://refeds.org/category/research-and-scholarship</saml:AttributeValue> + </saml:Attribute> + </mdattr:EntityAttributes> + </md:Extensions> + <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:1.0:protocol"> + <md:Extensions xmlns="urn:oasis:names:tc:SAML:2.0:metadata"> + <mdui:UIInfo xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui"> + <mdui:DisplayName xml:lang="en">Eurac Research CLARIN Centre</mdui:DisplayName> + <mdui:DisplayName xml:lang="de">Eurac Research CLARIN Centre</mdui:DisplayName> + <mdui:DisplayName xml:lang="it">Eurac Research CLARIN Centre</mdui:DisplayName> + <mdui:Description xml:lang="en">Eurac Research CLARIN Centre: Digital repository for language data</mdui:Description> + <mdui:Description xml:lang="de">Eurac Research CLARIN Centre: Digitales Repository für Sprachdaten</mdui:Description> + <mdui:Description xml:lang="it">Eurac Research CLARIN Centre: repository digitale di risorse linguistiche</mdui:Description> + <mdui:InformationURL xml:lang="en">https://clarin-dev.eurac.edu/repository/xmlui/page/about</mdui:InformationURL> + <mdui:InformationURL xml:lang="de">https://clarin-dev.eurac.edu/repository/xmlui/page/about</mdui:InformationURL> + <mdui:InformationURL xml:lang="it">https://clarin-dev.eurac.edu/repository/xmlui/page/about</mdui:InformationURL> + <mdui:Logo height="85" width="180">https://clarin-dev.eurac.edu/img/eurac.png</mdui:Logo> + <mdui:PrivacyStatementURL xml:lang="en">https://clarin-dev.eurac.edu/repository/xmlui/page/privacypolicy</mdui:PrivacyStatementURL> + <mdui:PrivacyStatementURL xml:lang="de">https://clarin-dev.eurac.edu/repository/xmlui/page/privacypolicy</mdui:PrivacyStatementURL> + <mdui:PrivacyStatementURL xml:lang="it">https://clarin-dev.eurac.edu/repository/xmlui/page/privacypolicy</mdui:PrivacyStatementURL> + </mdui:UIInfo> + </md:Extensions> + <md:AttributeConsumingService index="1"> + <md:ServiceName xml:lang="en">Digital Repository for the CLARIN Research Infrastructure provided by Eurac Research</md:ServiceName> + <md:ServiceName xml:lang="de">Digitales Repository für die CLARIN-Forschungs-Infrastruktur bereitgestellt durch Eurac Research</md:ServiceName> + <md:ServiceName xml:lang="it">Repository Digitale per la Infrastruttura di Ricerca CLARIN erogato da Eurac Research</md:ServiceName> + <md:ServiceDescription xml:lang="en">Digital Repository and services related to the CLARIN-IT consortium under the CLARIN Research Infrastructure; focused in the fields of terminology and multilingualism. The repository is based at Eurac Research in South Tyrol and managed by the Institute for Applied Linguistics.</md:ServiceDescription> + <md:ServiceDescription xml:lang="it">Digitales Repository und Services mit Bezug zum CLARIN-IT-Consortium im Rahmen der CLARIN Forschungsinfrastruktur; mit Fokus auf den Gebieten der Terminologie und Mehrsprachigkeit. Das Repository wird von Eurac Research gehostet und dort vom Institut für Angewandte Sprachforschung betreut.</md:ServiceDescription> + <md:ServiceDescription xml:lang="it">Repository Digitale e servizi relativi al consorzio CLARIN-IT sotto l'Infrastruttura di Ricerca CLARIN; focalizzato nel campo della teminologia e multilinguismo. Il repository è basato a Eurac Research in Alto Adige e gestito del Istituto di Linguistica Applicata.</md:ServiceDescription> + <md:RequestedAttribute FriendlyName="eduPersonPrincipalName" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.6" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true"/> + <md:RequestedAttribute FriendlyName="email" Name="urn:oid:0.9.2342.19200300.100.1.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true"/> + <md:RequestedAttribute FriendlyName="cn" Name="urn:oid:2.5.4.3" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false"/> + <md:RequestedAttribute FriendlyName="schacHomeOrganization" Name="urn:oid:1.3.6.1.4.1.25178.1.2.9" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false"/> + <md:RequestedAttribute FriendlyName="organizationName" Name="urn:oid:2.5.4.10" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false"/> + <md:RequestedAttribute FriendlyName="displayName" Name="urn:oid:2.16.840.1.113730.3.1.241" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true"/> + <md:RequestedAttribute FriendlyName="eduPersonEntitlement" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.7" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="false"/> + <md:RequestedAttribute FriendlyName="eduPersonTargetedID" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true"/> + <md:RequestedAttribute FriendlyName="eduPersonScopedAffiliation" Name="urn:oid:1.3.6.1.4.1.5923.1.1.1.9" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" isRequired="true"/> + </md:AttributeConsumingService> + </md:SPSSODescriptor> + <md:Organization> + <md:OrganizationName xml:lang="en">Eurac Research</md:OrganizationName> + <md:OrganizationName xml:lang="de">Eurac Research</md:OrganizationName> + <md:OrganizationName xml:lang="it">Eurac Research</md:OrganizationName> + <md:OrganizationDisplayName xml:lang="en">Eurac Research</md:OrganizationDisplayName> + <md:OrganizationDisplayName xml:lang="de">Eurac Research</md:OrganizationDisplayName> + <md:OrganizationDisplayName xml:lang="it">Eurac Research</md:OrganizationDisplayName> + <md:OrganizationURL xml:lang="en">http://www.eurac.edu/en</md:OrganizationURL> + <md:OrganizationURL xml:lang="de">http://www.eurac.edu/de</md:OrganizationURL> + <md:OrganizationURL xml:lang="it">http://www.eurac.edu/it</md:OrganizationURL> + </md:Organization> + <md:ContactPerson contactType="technical"> + <md:GivenName>Alexander</md:GivenName> + <md:SurName>König</md:SurName> + <md:EmailAddress>mailto:Alexander.Koenig@eurac.edu</md:EmailAddress> + </md:ContactPerson> + <md:ContactPerson contactType="support"> + <md:GivenName>Alexander</md:GivenName> + <md:SurName>König</md:SurName> + <md:EmailAddress>mailto:Alexander.Koenig@eurac.edu</md:EmailAddress> + </md:ContactPerson> + <md:ContactPerson contactType="administrative"> + <md:GivenName>Andrea</md:GivenName> + <md:SurName>Abel</md:SurName> + <md:EmailAddress>mailto:Andrea.Abel@eurac.edu</md:EmailAddress> + </md:ContactPerson> +</md:EntityDescriptor> diff --git a/clarin-dspace-docker/commul-customization/default-ssl b/clarin-dspace-docker/commul-customization/default-ssl new file mode 100644 index 0000000000000000000000000000000000000000..2164a1ffeb14441928e535fc790643a9cb9541e9 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/default-ssl @@ -0,0 +1,161 @@ +upstream tomcats { + server dspace:8009; + keepalive 10; + } + +server { + listen 80; + listen [::]:80; + server_name clarin-dev.eurac.edu; + return 301 https://$server_name$request_uri; +} + +server { + listen 443; + server_name clarin-dev.eurac.edu localhost; + + root /opt/nginx/html; + index index.html index.htm; + + ssl on; + ssl_certificate /etc/ssl/certs/lindat-dev_eurac_edu.pem; + ssl_certificate_key /etc/ssl/private/lindat-dev.key; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers RC4:HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + keepalive_timeout 70; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + add_header 'Access-Control-Allow-Origin' "*"; + add_header 'Access-Control-Allow-Credentials' 'true'; + + # redirect different legacy forms to repository/xmlui + rewrite ^/xmlui(.*)$ /repository/xmlui$1 redirect; + rewrite ^/repository.?$ /repository/xmlui/ redirect; + rewrite ^/solr(.*)$ /repository/solr$1 redirect; + rewrite ^/handle/(.*)$ /repository/xmlui/handle/$1 redirect; + + set $oai_index /repository/oai/request?verb=Identify; + rewrite ^/oai$ $oai_index redirect; + location /oai { + return 301 https://$http_host/repository$request_uri; + } + rewrite ^/repository/oai$ $oai_index redirect; + + if ($http_accept ~ "(.*xml.cmdi.*)"){ + rewrite ^/(.*)/xmlui/handle/(.*)$ https://$http_host/repository/oai/cite?metadataPrefix=cmdi&handle=$2? redirect; + } + + if ($query_string ~* "format=cmdi"){ + rewrite ^/(.*)/xmlui/handle/(.*)$ https://$http_host/repository/oai/cite?metadataPrefix=cmdi&handle=$2? redirect; + } + + if ($query_string ~* "format=cite_(cmdi|bibtex|html)"){ + rewrite ^/(.*)/xmlui/handle/(.*)$ https://$http_host/repository/oai/cite?metadataPrefix=%1&handle=$2? redirect; + } + +# location = / { +# return 301 /repository; +# } + + location / { + index index.php index.html index.htm; + +# try_files $uri $uri/ /index.php?$args; + +# location ~ \.php$ { +# include process_php; +# } + } + + location /repository { + + client_body_buffer_size 32K; + + client_max_body_size 4G; + + sendfile on; + send_timeout 300s; + + ajp_keep_conn on; + ajp_pass tomcats; + + location /repository/solr { + satisfy any; + + auth_basic "Restricted App"; + auth_basic_user_file /etc/apache2/passwd/passwords; + + allow 127.0.0.1; + deny all; + + ajp_keep_conn on; + ajp_pass tomcats; + } + + location /repository/JSON/solr/ { + rewrite /repository/JSON/solr/(.*) /repository/solr/search/select/?q=*:*&rows=0&facet=on&wt=json&indent=true&facet.field=$1 break; + + proxy_pass http://localhost:8080; + } + + } + + # define aai location + location /aai { + alias /opt/repository/sources/lindat-aai-discovery; } + + # add path your repository path that will be protected by shibboleth + location /repository/xmlui/shibboleth-login { + include repository_auth; + ajp_keep_conn on; + ajp_pass tomcats; + } + + location /xmlui/shibboleth-login { + include repository_auth; + ajp_keep_conn on; + ajp_pass tomcats; + } + + # remove the robots and favicon from the logs + location ~ /robots.txt$ { access_log off; log_not_found off; } + location ~ ^/favicon.ico$ { access_log off; log_not_found off; } + + # this prevents hidden files (beginning with a period) from being served + location ~ /\. { access_log off; log_not_found off; deny all; } + + # FastCGI authorizer for Auth Request module + location = /shibauthorizer { + internal; + include fastcgi_params; + fastcgi_pass unix:/opt/shibboleth-sp-fastcgi/shibauthorizer.sock; + } + + # FastCGI responder + location /Shibboleth.sso { + include fastcgi_params; + fastcgi_pass unix:/opt/shibboleth-sp-fastcgi/shibresponder.sock; + } + + # Resources for the Shibboleth error pages + location /shibboleth-sp { + alias /opt/shibboleth-sp-fastcgi/share/shibboleth/; + } + + location /secure { + alias /opt/repository/sources/secure; + include shib_clear_headers; + #Add your attributes here. They get introduced as headers + #by the FastCGI authorizer so we must prevent spoofing. + more_clear_input_headers 'displayName' 'mail' 'persistent-id'; + shib_request /shibauthorizer; + shib_request_use_headers on; + proxy_pass http://localhost:8080; + } + + include /opt/nginx/conf/proxies-enabled/*; +} + diff --git a/clarin-dspace-docker/commul-customization/default_locale b/clarin-dspace-docker/commul-customization/default_locale new file mode 100644 index 0000000000000000000000000000000000000000..0e74fc3ba0fbd4d77da905e387bc67956333c722 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/default_locale @@ -0,0 +1,11 @@ +# File generated by update-locale +LANG="en_US.UTF-8" +LC_NUMERIC="en_US.UTF-8" +LC_TIME="en_US.UTF-8" +LC_MONETARY="en_US.UTF-8" +LC_PAPER="en_US.UTF-8" +LC_NAME="en_US.UTF-8" +LC_ADDRESS="en_US.UTF-8" +LC_TELEPHONE="en_US.UTF-8" +LC_MEASUREMENT="en_US.UTF-8" +LC_IDENTIFICATION="en_US.UTF-8" diff --git a/clarin-dspace-docker/commul-customization/discojuiceDiscoveryResponse.html b/clarin-dspace-docker/commul-customization/discojuiceDiscoveryResponse.html new file mode 100644 index 0000000000000000000000000000000000000000..3d0946b1a9c50d7f7865a6ec09c3f82189d3d56b --- /dev/null +++ b/clarin-dspace-docker/commul-customization/discojuiceDiscoveryResponse.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> + <meta http-equiv="Cache-Control" content="no-cache"> + <meta http-equiv="Pragma" content="no-cache"> + <title>IdP Discovery Response Receiver</title> + + <script type="text/javascript"> + +function parseURL(url) { + var a = document.createElement('a'); + a.href = url; + return a.hostname; +} + +function receive() { + var urlParams = {}, + cid = null, + idpentityid, + subid, + rsearch + ; + + (function () { + var e, + a = /\+/g, // Regex for replacing addition symbol with a space + r = /([^&;=]+)=?([^&;]*)/g, + d = function (s) { return decodeURIComponent(s.replace(a, " ")); }, + q = window.location.search.substring(1); + + while (e = r.exec(q)) + urlParams[d(e[1])] = d(e[2]); + })(); + + if (urlParams.cid) cid = urlParams.cid; + + var sender = parseURL(document.referrer); + + // Received a specific entity ID from the storage. + if (urlParams.entityID) { + + window.parent.DiscoJuice.Utils.log("Entity id parameter was " + urlParams.entityID); + + rsearch = urlParams.entityID.match(/^(.*)#(.*)$/); + if (rsearch) { + idpentityid = rsearch[1]; + subid = rsearch[2]; + } else { + idpentityid = urlParams.entityID; + subid = null; + } + window.parent.DiscoJuice.Utils.log('ResponseLocation: Response from discovery service [' + sender + ']: ' + idpentityid + ' subID: ' + subid); + window.parent.DiscoJuice.Control.discoResponse(sender, idpentityid, subid, cid); + + // Received a textual error from the storage, to show in the debug log. + } else if (urlParams['error']) { + window.parent.DiscoJuice.Control.discoResponseError(cid, + "Error from IdP Discovery Service [" + sender + "]: " + urlParams.error); + + // Did not receive a response parameter. This probably means that the Disco storage did not have a stored preference + // for the user. Consequently: no error. + } else { + window.parent.DiscoJuice.Utils.log('No valid response parameters. cid[' + cid + ']'); + window.parent.DiscoJuice.Control.discoResponseError(cid); + } + +} + + </script> +</head> + +<body onload="receive();"> + +</body> +</html> diff --git a/clarin-dspace-docker/commul-customization/eurac.png b/clarin-dspace-docker/commul-customization/eurac.png new file mode 100644 index 0000000000000000000000000000000000000000..496e90d6cb066e656d0644cf478cf9295181feff Binary files /dev/null and b/clarin-dspace-docker/commul-customization/eurac.png differ diff --git a/clarin-dspace-docker/commul-customization/fastcgi.conf b/clarin-dspace-docker/commul-customization/fastcgi.conf new file mode 100644 index 0000000000000000000000000000000000000000..091738c60381b378628c6a58f82462aa72be30a0 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/fastcgi.conf @@ -0,0 +1,26 @@ + +fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; +fastcgi_param QUERY_STRING $query_string; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; + +fastcgi_param SCRIPT_NAME $fastcgi_script_name; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param DOCUMENT_URI $document_uri; +fastcgi_param DOCUMENT_ROOT $document_root; +fastcgi_param SERVER_PROTOCOL $server_protocol; +fastcgi_param REQUEST_SCHEME $scheme; +fastcgi_param HTTPS $https if_not_empty; + +fastcgi_param GATEWAY_INTERFACE CGI/1.1; +fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; + +fastcgi_param REMOTE_ADDR $remote_addr; +fastcgi_param REMOTE_PORT $remote_port; +fastcgi_param SERVER_ADDR $server_addr; +fastcgi_param SERVER_PORT $server_port; +fastcgi_param SERVER_NAME $server_name; + +# PHP only, required if PHP was built with --enable-force-cgi-redirect +fastcgi_param REDIRECT_STATUS 200; diff --git a/clarin-dspace-docker/commul-customization/favicon_eurac-research.png b/clarin-dspace-docker/commul-customization/favicon_eurac-research.png new file mode 100644 index 0000000000000000000000000000000000000000..5ceed3076077b8aa071e60174558274723ecc71d Binary files /dev/null and b/clarin-dspace-docker/commul-customization/favicon_eurac-research.png differ diff --git a/clarin-dspace-docker/commul-customization/hdl-setup.tmp b/clarin-dspace-docker/commul-customization/hdl-setup.tmp new file mode 100644 index 0000000000000000000000000000000000000000..0b145374614328abbaf05bcf048cc7b7ec6a821d --- /dev/null +++ b/clarin-dspace-docker/commul-customization/hdl-setup.tmp @@ -0,0 +1,17 @@ +y +n +193.106.181.70 +193.106.181.70 +2641 +8000 +y +D +1 +ERCC Handle Server +Eurac Research + + +corpman@eurac.edu +y +n +n diff --git a/clarin-dspace-docker/commul-customization/index.html b/clarin-dspace-docker/commul-customization/index.html new file mode 100644 index 0000000000000000000000000000000000000000..403507a3d4e4158b5e961fc3c14b654e25de2865 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/index.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> +<title>Welcome to ERCC!</title> +<style> + body { + width: 35em; + margin: 0 auto; + font-family: Tahoma, Verdana, Arial, sans-serif; + } +</style> +</head> +<body> +<h1>Welcome to ERCC!</h1> +<p>We are the Eurac Research CLARIN Centre.</p> +<p><img src="img/eurac.png"</p> +<p>For the repository click <a href="repository">here</a>.<br/> +</body> +</html> diff --git a/clarin-dspace-docker/commul-customization/init-dspace-dbs.sh b/clarin-dspace-docker/commul-customization/init-dspace-dbs.sh new file mode 100644 index 0000000000000000000000000000000000000000..1dbd90c76aff425b126c128f8e1b1dba34debeb6 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/init-dspace-dbs.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -xe + +psql --username "$POSTGRES_USER" -c "CREATE USER $dspaceuser WITH PASSWORD '$dspacepassword'"; +psql --username "$POSTGRES_USER" -c "CREATE DATABASE "$dspacedbname" OWNER $dspaceuser ENCODING \"UTF-8\" TEMPLATE template0;" +psql --username "$POSTGRES_USER" -c "CREATE DATABASE "$dspaceutilsdbname" OWNER $dspaceuser ENCODING \"UTF-8\" TEMPLATE template0;" + +psql --username "$POSTGRES_USER" --set=utildir="/tmp/sql/" -p 5432 $secretdspaceutilsdbname < /tmp/sql/utilities.sql diff --git a/clarin-dspace-docker/commul-customization/init-dspace-dbs.sh.dist b/clarin-dspace-docker/commul-customization/init-dspace-dbs.sh.dist new file mode 100644 index 0000000000000000000000000000000000000000..88a1d730d87c282d3c8d143a40f2224d2290dda0 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/init-dspace-dbs.sh.dist @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE USER secretdspaceuser WITH PASSWORD 'secretdspacepassword'; + CREATE DATABASE "secretdspacedbname" OWNER secretdspaceuser ENCODING "UTF-8" TEMPLATE template0; + CREATE DATABASE "secretdspaceutilsdbname" OWNER secretdspaceuser ENCODING "UTF-8" TEMPLATE template0; +EOSQL + +psql --username "$POSTGRES_USER" --set=utildir="/tmp/sql/" -p 5432 secretdspaceutilsdbname < /tmp/sql/utilities.sql diff --git a/clarin-dspace-docker/commul-customization/local.properties b/clarin-dspace-docker/commul-customization/local.properties new file mode 100644 index 0000000000000000000000000000000000000000..44402d342847c97597ad35794259679adb8dc600 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/local.properties @@ -0,0 +1,397 @@ +############################# +# Local config modification # +############################# + +# PID service +# type o service; for now only epic and epic2 are supported +lr.pid.service.type = +lr.pid.service.url = https://handle.gwdg.de/pidservice/ +lr.pid.service.user = +lr.pid.service.pass = +# test pid which should have been created with the above credentials +lr.pid.service.testPid = +# per community pid configurations for pid prefixes of the format: +# community=<community ID>,prefix=<prefix>,alternative_prefixes=<pipeline separated list of alternative prefixes>,type=<local|epic>,canonical_prefix=<URL of handle>,subprefix=<subprefix> +# multiple configurations can be given separated by semicolon +# default configuration should have asterisk as the community ID +# subprefix is only used for local handles +lr.pid.community.configurations = community=*,prefix=99999,type=local,canonical_prefix=http://hdl.handle.net/ +# if true, PID metadata will be filled with object metadata like title +lr.pid.resolvemetadata = true + +handle.canonical.prefix = http://hdl.handle.net/ +handle.prefix = 123456789 +handle.dir=${dspace.dir}/handle-server + +lr.reserve.pid.on.start = false + + +# UFAL mounted assetstore for restricted item harvesting. Please mind the trailing slash +lr.harvestable.assetstore = + + +# DSpace host name - should match base URL. Do not include protocol i.e. http or https and do not include port, this is only the name of server. +# Also do not use localhost because using this property some external links will be generated. +dspace.hostname = lindat-dev.eurac.edu + +# DSpace base URL, the full url of the instance +# !!! URL must NOT contain trailing slash !!! +dspace.baseUrl = https://lindat-dev.eurac.edu/repository + +# OAI webapp url without context (eg. /request) use http +# in theory ${dspace.baseUrl}/oai except for the http requirement +dspace.oai.url = http://lindat-dev.eurac.edu/repository/oai + +# DSpace URL, usually ending with /xmlui +# DSpace base URL. Include port number etc., but NOT trailing slash +dspace.url = ${dspace.baseUrl}/xmlui + +# Google analytics key +xmlui.google.analytics.key = + +# Database settings ######################## + +# The name of the database that you will use for your dspace instance. +# If the database is not created yet, this name will be used to create the new database. +# Be careful with the trailing spaces in the name! +lr.database = ${env.dspacedbname} +lr.database.port = ${env.DSPACE_POSTGRESQL_SERVICE_PORT_POSTGRESQL} + +# Connection URL of the database. Make sure you are using the correct port and if you are connecting to remote server, +# verify that the port is accessible. Do not change the ${lr.database}, the database name will be substituted from above property. +db.url = jdbc:postgresql://${env.DSPACE_POSTGRESQL_SERVICE_HOST}:${lr.database.port}/${lr.database} + +# The database driver you are using. Make sure that the required jars are available. +db.driver = org.postgresql.Driver + +# Credentials for the database user. The user should have read/write permissions on your database. +db.username = ${env.dspaceuser} +db.password = ${env.dspacepassword} + +db.maxidle = -1 +db.schema = +db.maxwait = 5000 +db.maxconnections = 30 +db.statementpool = true +db.poolname = dspacepool + +# UFAL Utilities database name. +lr.utilities.database = ${dspaceutilsdbname} + +# Connection URL for the Utilities database. +lr.utilities.db.url = jdbc:postgresql://${env.DSPACE_POSTGRESQL_SERVICE_HOST}:${lr.database.port}/${lr.utilities.database} + +# Functionality Manager facade for Utilities API +lr.utilities.functionalityManager.class = cz.cuni.mff.ufal.lindat.utilities.HibernateFunctionalityManager + +############################################ + +# DSpace general settings ################## + +# You can use this value to append version with other values. +lr.dspace.version = lindat-dspace5 + +# The installation directory of your instance. +# THIS IS USED IN THE VARIABLES/MAKEFILE so don't use variables(${*}) here +dspace.install.dir = /opt/lindat-dspace/installation + +# A description of your dspace instance. It will be used as a description of your dspace instance for other services +dspace.name = Eurac Research CLARIN Centre +# Used in email templates +lr.dspace.name.short = ERCC + +supported.locales = en + +default.language = en_US +http.proxy.host = +http.proxy.port = +loglevel.other=INFO +loglevel.dspace=INFO + + +############################################ + +# Mail settings ############################ + +# Mail server address. you can use 127.0.0.1, if configured on localhost. +mail.server = smtp.office365.com + +# Mail server disabled? +mail.server.disabled = false + +mail.server.username = secretmailuser +mail.server.password = secretmailpassword +mail.server.port = 587 +mail.extraproperties = mail.smtp.starttls.enable=true + + +# Eurac IAL: Contact information +lr.help.mail = corpman@eurac.edu +lr.help.phone = +39 0471 055 129 +mail.from.address = lindat.mail@unibz.it +mail.feedback.recipient = corpman@eurac.edu +info.recipient = corpman@eurac.edu +mail.alert.recipient = ${mail.admin} +mail.registration.notify = ${mail.admin} + +# General site administration (Webmaster) e-mail +mail.admin = ${mail.feedback.recipient} + +# Email where errors will be sent (from specific classes) +lr.errors.email= + +# solr server address that your instance is using. +solr.server = http://lindat-dev.eurac.edu:8080/repository/solr + +# Google Analytics +# NOTE: Copy appropriate Google Analytics API key file to the location specified +# below upon first installation +# Be sure to choose location that is not deleted/overwritten upon deployment +# (the default value is safe) +lr.ga.analytics.key.file = +lr.ga.analytics.profile.id = +lr.ga.analytics.account.email = + +# Path of the sources folder that is used for the compilation of this dspace instance. +lr.dspace.source.dir = /opt/lindat-dspace/sources/ + +# Backup Configurations #################### + +# which files/directories needs backup +lr.backup.source = (/opt/lindat-dspace/installation/ /opt/lindat-dspace/database/ /opt/lindat-dspace/sources/ /opt/nginx /etc/libapache2-mod-jk /opt/shibboleth-sp-fastcgi/etc/shibboleth /opt/tomcat8/ /etc/cron.d/dspace) + +# where the backup should be stored +lr.backup.target = "/home/backup/" + +# the following settings are good to keep one month of restorable backup +# with differential backup every day and full backup every week + +# max levels of differential backups +backup2l.max.level = 1 +# how many differential backups per level before new level +backup2l.max.per.level = 6 +# how many full backups to keep +backup2l.max.full = 4 +# number of generations to keep per level +backup2l.generations = 4 + +############################################ + +# iRods specific for EUDAT +lr.replication.eudat.on=false +lr.replication.eudat.protocol=irods +lr.replication.eudat.host= +lr.replication.eudat.port= +lr.replication.eudat.username= +lr.replication.eudat.password= +lr.replication.eudat.homedirectory= +# directory in homedirectory where to put replicas for this instance +lr.replication.eudat.replicadirectory= +lr.replication.eudat.zone= +lr.replication.eudat.defaultstorage= +lr.replication.eudat.id= +lr.replication.eudat.notification_email=${info.recipient} +lr.replication.eudat.retrievetopath= +# default is not set +lr.replication.jargon.numThreads= +# default 32 * 1024 * 1024 +lr.replication.jargon.MAX_SZ_FOR_SINGLE_BUF= +# default 65535 +lr.replication.jargon.BUFFER_SIZE= +# log progress after count of iterations (depends on buffer_size) +lr.replication.jargon.PUT_LOG_AFTER= + + +# Selenium Testing ######################### + +# URLs that are required for test cases +selenium.test.home.url = /xmlui +selenium.test.browse.url = /xmlui/browse +selenium.test.search.url = /xmlui/discover +selenium.test.submission.url = /xmlui/submissions +selenium.test.submit.url = /xmlui/submit +selenium.test.local.login = /xmlui/password-login +selenium.test.oai.url = /oai + +# default User for testing ... UserName should be the name that is supposed to appear on the home page after login. +selenium.test.user.id = +selenium.test.user.password = +selenium.test.user.name = + +# admin User +selenium.test.admin.id = +selenium.test.admin.password = +selenium.test.admin.name = + +# more users can be added by replacing *.user.* in the above credential settings +# the user will be used in testng.xml file to refer that user. + +############################################ + +# Metadata quality ######################## + +# you should map the solr (if it is not public which it should not) +# url (or any other) into publicly accessible e.g., in apache conf +# using mod_rewrite: +# +# RewriteRule ^/jm/repository/JSON/solr/(.*)$ http://localhost:8080/jm/solr/search/select/?q=*:*&rows=0&facet=on&wt=json&indent=true&facet.field=$1 [P,L] +# +# There are more ways in DSpace how to restrict metadata values (authority manager, +# value pairs, specific js code and this) +# +lr.autocomplete.on=true +lr.autocomplete.solr.url=${dspace.url}/JSON/solr/ +lr.autocomplete.json_static.url=${dspace.url}/static/json/ + +lr.allow.edit.metadata = Eurac Research CLARIN Centre + +############################################ +# +# File download options +# +# minimum number of files for enabling zipped download: +lr.download.all.limit.min.file.count = 1 +# maximum total size of files for enabling zipped download: +lr.download.all.limit.max.file.size = 1073741824 +# minimum total size of files for enabling download alert: +lr.download.all.alert.min.file.size = 10485760 + +############################################ +# +# File upload options +# +# maximum size of file for enabling upload alert +lr.upload.file.alert.max.file.size = 2147483648 + +####### +# +# Shibboleth +# +lr.shibboleth.log.path = /opt/shibboleth-sp-fastcgi/var/log/shibboleth +lr.shibboleth.log.defaultName = shibd.log +#the place where you run shibboleth's discoFeed handler ("https://"+hostname+"/Shibboleth.sso/DiscoFeed") +lr.shibboleth.discofeed.url = https://lindat-dev.eurac.edu/Shibboleth.sso/DiscoFeed + +#### +# +# discojuice/aai url, without trailing slash +# don't change this on ufal-point-dev! +lr.aai.url = https://lindat-dev.eurac.edu/aai + +# For reports +harvesterInfo.url = http://catalog.clarin.eu/oai-harvester/ + +############################################ +# +# User profile options +# +# whether to show language field in user profile +lr.xmlui.user.showlanguage = false + +############################################### +# oai +#mind the documentation for oai/cite and the respective apache rewrite +lr.repository.identifier = lindat-dev.eurac.edu +lr.repository.sampleIdentifier = oai:lindat-dev.eurac.edu:11858/00-097C-0000-0001-487A-4 + +#### +# +# description-olac.xml +#### +lr.description.archiveURL = http://lindat-dev.eurac.edu/ +lr.description.participant.name = Alexander König +lr.description.institution = Eurac Research, Institute for Applied Linguistics +lr.description.institutionURL = http://www.eurac.edu/linguistics +lr.description.shortLocation = Bolzano, Italy +lr.description.location = Viale Druso, 1 / Drususallee 1, 39100 Bolzano / Bozen - Italy +lr.description.synopsis = http://www.eurac.edu +lr.desription.access = https://lindat-dev.eurac.edu/repository/xmlui/page/about +lr.description.archivalSubmissionPolicy = https://lindat-dev.eurac.edu/repository/xmlui/page/about + + +############################################ +# +# Tracker configuration +# +# Enable or disable tracker +#lr.tracker.enabled = true +# +## Type of the tracker. Currently only the Piwik tracker is supported +#lr.tracker.type = piwik +# +# Tracker API URL to which tracking information is being sent +#lr.tracker.api.url = https://host/piwik/piwik.php +# +# Tracker API authentication token +#lr.tracker.api.auth.token = +# +# Site ID under which OAI requests are filed +#lr.tracker.oai.site_id = 5 +# +# Site ID under which Bitstream downloads are filed +#lr.tracker.bitstream.site_id = 5 +# +############################################ +lr.tracker.enabled = true +lr.tracker.type = piwik +lr.tracker.api.url = https://commul.eurac.edu/piwik/piwik.php +lr.tracker.api.auth.token = +lr.tracker.oai.site_id = 2 +lr.tracker.bitstream.site_id = 2 + +lr.statistics.api.url = https://commul.eurac.edu/piwik/ +lr.statistics.api.auth.token = +lr.statistics.api.site_id = 2 +lr.statistics.report.path = ${dspace.dir}/piwik_reports +lr.statistics.show_last_n = 7 +lr.lindat.logo.mono = ${dspace.dir}/webapps/xmlui/themes/UFAL/images/lindat/lindat-logo-mono.png + +############################################ +# +# Curation +# +############################################ + +# Space separated metadata patterns (regular expression) +# that should be ignored while checking for required metadata. +# Leave it empty if you want to check everything. +lr.curation.metadata.checkrequired.ignore = metashare.* + +############################################ +# +# Link checker +# +# User Agent string for link checker +#lr.link.checker.user.agent = "DSpace Link Validator" +# +# Connection timeout for link checker (in milliseconds) +#lr.link.checker.connect.timeout = 2000 +# +# Read timeout for link checker (in milliseconds) +#lr.link.checker.read.timeout = 3000 +# +############################################ +lr.link.checker.user.agent = "DSpace Link Validator" +lr.link.checker.connect.timeout = 2000 +lr.link.checker.read.timeout = 3000 + +### Entity ID of your shibboleth sp +lr.spEntityId = http://lindat-dev.eurac.edu/sp/shibboleth + +##### +# +# shortener +# +#### +lr.shortener.enabled = false +lr.shortener.handle.prefix = 1234 + +##### +# +# XSendFile header +# +#### +lr.XSendFileHeader = +lr.XSendFilePathPrefix = + +lr.compile.time=Tue Oct 3 14:46:46 UTC 2017 \ No newline at end of file diff --git a/clarin-dspace-docker/commul-customization/nginx b/clarin-dspace-docker/commul-customization/nginx new file mode 100755 index 0000000000000000000000000000000000000000..4aaad1fb48c03b136e3626889c0fa0cd6f3c1dec --- /dev/null +++ b/clarin-dspace-docker/commul-customization/nginx @@ -0,0 +1,100 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: nginx +# Required-Start: $local_fs $remote_fs $network $syslog $named +# Required-Stop: $local_fs $remote_fs $network $syslog $named +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: starts the nginx web server +# Description: starts nginx using start-stop-daemon +### END INIT INFO + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/opt/nginx/sbin/nginx +NAME=nginx +DESC=nginx + +test -x $DAEMON || exit 0 + +set -e + +. /lib/lsb/init-functions + +PID=$(awk -F'[ ;]' '/[^#]pid/ {print $2}' /opt/nginx/conf/nginx.conf) +if [ -z "$PID" ] +then + PID=/var/run/nginx.pid +fi + +test_nginx_config() { + $DAEMON -t $DAEMON_OPTS >/dev/null 2>&1 + retvar=$? + if [ $retvar -ne 0 ] + then + exit $retvar + fi +} + +start() { + start-stop-daemon --start --quiet \ + --retry 5 --exec $DAEMON --oknodo -- $DAEMON_OPTS +} + +stop() { + start-stop-daemon --stop --quiet \ + --retry 5 --oknodo --exec $DAEMON +} + +case "$1" in + start) + test_nginx_config + log_daemon_msg "Starting $DESC" "$NAME" + start + log_end_msg $? + ;; + + stop) + log_daemon_msg "Stopping $DESC" "$NAME" + stop + log_end_msg $? + ;; + + restart|force-reload) + test_nginx_config + log_daemon_msg "Restarting $DESC" "$NAME" + stop + sleep 1 + start + log_end_msg $? + ;; + + reload) + test_nginx_config + log_daemon_msg "Reloading $DESC configuration" "$NAME" + start-stop-daemon --stop --signal HUP --quiet \ + --oknodo --exec $DAEMON + log_end_msg $? + ;; + + configtest|testconfig) + log_daemon_msg "Testing $DESC configuration" + if test_nginx_config; then + log_daemon_msg "$NAME" + else + exit $? + fi + log_end_msg $? + ;; + + status) + status_of_proc -p $PID "$DAEMON" nginx + ;; + + *) + echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/clarin-dspace-docker/commul-customization/nginx.conf b/clarin-dspace-docker/commul-customization/nginx.conf new file mode 100644 index 0000000000000000000000000000000000000000..bb281f6cfb9e12c26b6f2bfdf0bf01542c2406a0 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/nginx.conf @@ -0,0 +1,55 @@ + + +user www-data; +worker_processes 1; + +#error_log logs/error.log; +#error_log logs/error.log notice; +#error_log logs/error.log info; + +#pid logs/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include mime.types; + default_type application/octet-stream; + + #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + # '$status $body_bytes_sent "$http_referer" ' + # '"$http_user_agent" "$http_x_forwarded_for"'; + + #access_log logs/access.log main; + + sendfile on; + #tcp_nopush on; + + #keepalive_timeout 0; + keepalive_timeout 65; + + gzip on; + gzip_disable "msie6"; + + gzip_comp_level 6; + gzip_min_length 1100; + gzip_buffers 16 8k; + gzip_proxied any; + gzip_types + text/plain + text/css + text/js + text/xml + text/javascript + application/javascript + application/x-javascript + application/json + application/xml + application/rss+xml + image/svg+xml; + + include default-ssl; +} diff --git a/clarin-dspace-docker/commul-customization/nginx_build.sh b/clarin-dspace-docker/commul-customization/nginx_build.sh new file mode 100644 index 0000000000000000000000000000000000000000..1f0e7f4b079fe7ef936cfcf1a69110adc1e14871 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/nginx_build.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +NGINX_VERSION=1.12.0 +HEADERS_MORE_VERSION=0.31 +OPENSSL=1.1.0f +AJP_VERSION=bf6cd93f2098b59260de8d494f0f4b1f11a84627 + +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +pushd $DIR + +function download_archive { + local dirname=$1 + local url=$2 + local archive="$dirname.tar.gz" + + if [ ! -d "$dirname" ]; then + wget -O "$dirname.tar.gz" "$url" + #sha1sum "$archive" + tar -xzvf "$archive" + mv `tar -ztf "$archive" | head -n 1` "$dirname" + rm "$archive" + fi +} + +download_archive nginx_src http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz + +#Headers More module +download_archive nginx-headers-more-module https://github.com/openresty/headers-more-nginx-module/archive/v$HEADERS_MORE_VERSION.tar.gz +download_archive nginx-ajp-module https://github.com/yaoweibin/nginx_ajp_module/archive/$AJP_VERSION.tar.gz +download_archive nginx-shib-module https://github.com/nginx-shib/nginx-http-shibboleth/archive/v2.0.0.tar.gz +download_archive openssl-$OPENSSL http://www.openssl.org/source/openssl-$OPENSSL.tar.gz +ln -s openssl-$OPENSSL openssl + +cd nginx_src +./configure --prefix=/opt/nginx \ + --add-module="$DIR/nginx-headers-more-module" \ + --add-module="$DIR/nginx-ajp-module" \ + --add-module="$DIR/nginx-shib-module" \ + --with-http_ssl_module \ + --with-http_stub_status_module \ + --with-openssl="$DIR/openssl" \ + --with-debug \ + --with-threads +make -j2 +make install + +popd diff --git a/clarin-dspace-docker/commul-customization/repository_auth b/clarin-dspace-docker/commul-customization/repository_auth new file mode 100644 index 0000000000000000000000000000000000000000..5c4246b2c0cc9c3f768fc6cde348b38a6999bb70 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/repository_auth @@ -0,0 +1,27 @@ +#wildcards (Shib-*) not working +more_clear_input_headers + Auth-Type + Shib-Application-Id + Shib-Assertion-01 + Shib-Assertion-Count + Shib-Authentication-Instant + Shib-Authentication-Method + Shib-Authncontext-Class + Shib-Identity-Provider + Shib-Session-Id + Shib-Session-Index + Remote-User; + +# Add your attributes here. They get introduced as headers +# by the FastCGI authorizer so we must prevent spoofing. +# get these from shibboleth attributes-map.xml +# xmllint --xpath '//@id' /opt/shibboleth-sp/etc/shibboleth/attribute-map.xml | sed -e 's/ /\n/g' -e's/id=//g' -e "s/\"/'/g" | sort -u | paste -d" " -s +more_clear_input_headers 'affiliation' 'assurance' 'cn' 'eduPersonOrgUnitDN' 'eduPersonStudyiProgramme' 'eduPersonStudySubject' 'entitlement' 'eppn' 'givenName' 'mail' 'o' 'ou' 'persistent-id' 'sn' 'telephoneNumber' 'unscoped-affiliation'; + +# Require https and will redirect +if ($https != "on") { + return 301 https://$http_host$request_uri; +} + +shib_request /shibauthorizer; +shib_request_use_headers on; diff --git a/clarin-dspace-docker/commul-customization/setenv.sh b/clarin-dspace-docker/commul-customization/setenv.sh new file mode 100644 index 0000000000000000000000000000000000000000..bd9505c6a1e8c257940c559f16e3eaa1bded9f63 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/setenv.sh @@ -0,0 +1,4 @@ +export CATALINA_OPTS="$CATALINA_OPTS -Xms2048m" +export CATALINA_OPTS="$CATALINA_OPTS -Xmx4096m" +export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxPermSize=256m" + diff --git a/clarin-dspace-docker/commul-customization/shib_clear_headers b/clarin-dspace-docker/commul-customization/shib_clear_headers new file mode 100644 index 0000000000000000000000000000000000000000..a913850abbf4b8b35012bb2f060119c27a71b7b1 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/shib_clear_headers @@ -0,0 +1,33 @@ +# Ensure that you add directives to clear input headers for *all* attributes +# that your backend application uses. This may also include variations on these +# headers, such as differing capitalisations and replacing hyphens with +# underscores etc -- it all depends on what your application is reading. +# +# Note that Nginx silently drops headers with underscores +# unless the non-default `underscores_in_headers` is enabled. + +# Shib-* doesn't currently work because * isn't (yet) supported +more_clear_input_headers + Auth-Type + Shib-Application-Id + Shib-Authentication-Instant + Shib-Authentication-Method + Shib-Authncontext-Class + Shib-Identity-Provider + Shib-Session-Id + Shib-Session-Index + Remote-User; + +# more_clear_input_headers +# EPPN +# Affiliation +# Unscoped-Affiliation +# Entitlement +# Targeted-Id +# Persistent-Id +# Transient-Name +# Commonname +# DisplayName +# Email +# OrganizationName; + diff --git a/clarin-dspace-docker/commul-customization/shib_test.pl b/clarin-dspace-docker/commul-customization/shib_test.pl new file mode 100644 index 0000000000000000000000000000000000000000..4412bdb652504bb0726edd2884fd2bea41847f07 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/shib_test.pl @@ -0,0 +1,397 @@ +#!/usr/bin/env perl +# +# Version: $Id$ +# +use strict; +use warnings; +use CGI; +use CGI::Carp qw( fatalsToBrowser); +use URI; + +# +# constants +# +my @ATTRIBUTES_REQUIRED = qw( + eduPersonPrincipalName:eppn + eduPersonTargetedID:persistent_id +); +my @ATTRIBUTES_OPTIONAL = qw( + eduPersonScopedAffiliation:affiliation:eduPersonAffiliation + cn + displayName +); + + +# allow override from environment ... +if (exists $ENV{'SHIBTEST_ATTRIBUTES_REQUIRED'}) { + @ATTRIBUTES_REQUIRED = split('\s+', $ENV{'SHIBTEST_ATTRIBUTES_REQUIRED'}); +} +if (exists $ENV{'SHIBTEST_ATTRIBUTES_OPTIONAL'}) { + @ATTRIBUTES_OPTIONAL = split('\s+', $ENV{'SHIBTEST_ATTRIBUTES_OPTIONAL'}); +} + + +# +# code below ... nothing to change there ... +# +sub xml_escape { + my $s = shift; + + $s =~ s!&!&!gs; + $s =~ s!<!<!gs; + $s =~ s!>!>!gs; + return $s; +} + + +sub render_table_rows { + my $caption = shift; + my $keys = shift; + my $i = 0; + print '<tr class="header">', '<th colspan="2">', + $caption, '</th>', '</tr>'; + if (scalar(@{$keys}) > 0) { + foreach my $key (@{$keys}) { + my $value = $ENV{$key}; + $value =~ s!\n*!!gs; + $value =~ s!\s*(;|\$)\s*!\n!gs; + $value = xml_escape($value); + $value =~ s!\n!<br />!gs; + print '<tr class="', ($i++ % 2 == 0 ? 'even' : 'odd'), '">'; + print '<td>', $key, '</td>', '<td>', $value, '</td>', '</tr>'; + } + } + else { + print '<tr class="even"><td colspan="2">', + '<span class="error center">[NONE]</span></tr>'; + } +} + + +sub dump_shibboleth_attributes { + my $debug_env = shift; + + my @keys = sort(keys(%ENV)); + my @attrs = grep(!m/^(HTTPS|SERVER_|SCRIPT_|PATH|QUERY_STRING|GATEWAY|DOCUMENT_ROOT|REMOTE|REQUEST|HTTP_|AUTH_TYPE|Shib_)/i, @keys); + my @shib = grep(m/Shib_/i, @keys); + + render_table_rows('Shibboleth Attributes:', \@attrs); + render_table_rows('Shibboleth Enviroment Variables:', \@shib); + if (defined ($debug_env)) { + render_table_rows('All Environment Variables:', \@keys); + } +} + + +sub dump_shibboleth_assertions { + my $count = shift; + + return unless defined($count) && $count > 0; + + # try to load LWP and XML::Twig and bail if not available ... + eval { + require LWP; + require XML::Twig; + }; + if ($@) { + return; + } + print '<tr class="header">', '<th colspan="2">', + 'Raw SAML Assertion(s)', '</th>', '</tr>'; + my $j = 0; + my $browser = LWP::UserAgent->new; + ASSERTION: + for (my $i = 1; $i <= $count; $i++) { + my $url = $ENV{sprintf('Shib_Assertion_%02d', $i)}; + next ASSERTION unless defined ($url); + + print '<tr class="', ($j++ % 2 == 0 ? 'even' : 'odd'), '">'; + print '<td>Assertion ', $i, '</td>'; + my $response = $browser->get($url); + if ($response->is_success) { + my $twig = XML::Twig->new(pretty_print => 'indented', + output_encoding => 'utf-8', + no_prolog => '1', + keep_original_prefix => '1'); + $twig->parse($response->content); + my $s = $twig->sprint(); + $s = xml_escape($s); + $s =~ s! ! !gs; + $s =~ s!\n!<br />!gs; + print '<td>', $s, '</td>'; + } + else { + print '<td>', '<span class="error">Cannot retieve assertion: ', + xml_escape($response->status_line), '</span>', '</td>'; + } + print '</tr>'; + } +} + + +sub make_self_uri { + my $scheme = exists $ENV{'HTTPS'} ? 'https' : 'http'; + my $uri = URI->new($scheme . '://' . $ENV{'SERVER_NAME'}); + $uri->path($ENV{'REQUEST_URI'}); + return $uri->as_string(); +} + + +sub make_shibboleth_uri { + my $path = shift; + + # XXX: always assume https for Shibboleth URIs ... + my $uri = URI->new('https://' . $ENV{'SERVER_NAME'}); + $uri->path($path); + return $uri; +} + + +sub make_login_uri { + my $uri = $ENV{'SHIBTEST_LOGIN_URI'}; + if (defined($uri)) { + $uri = URI->new($uri); + } + else { + $uri = make_shibboleth_uri('/Shibboleth.sso/Login'); + } + $uri->query_form({ + target => make_self_uri(), + }); + return $uri->as_string(); +} + + +sub make_logout_uri { + my $uri = $ENV{'SHIBTEST_LOGOUT_URI'}; + if (defined($uri)) { + $uri = URI->new($uri); + } + else { + $uri = make_shibboleth_uri('/Shibboleth.sso/Logout'); + } + $uri->query_form({ + return => make_self_uri(), + }); + return $uri->as_string(); +} + + +sub scan_attributes { + my $scan_ref = shift; + my $optional = shift; + my $missing = 0; + + foreach my $aliases (@{$scan_ref}) { + my $found = undef; + my @attrs = split(':', $aliases); + + KEY: + foreach my $b (keys(%ENV)) { + foreach my $a (@attrs) { + if (lc($a) eq lc($b)) { + $found = $b; + last KEY; + } + } + } + + if (defined($found)) { + print '<p class="attr ok">', + ($optional ? 'Optional' + : 'Required'), + ' attribute <code>', $attrs[0], '</code> is available', + ($found ne $attrs[0] ? " (exported as <code>$found</code>)" + : ''), + '.</p>'; + } + else { + print '', ($optional ? '<p class="attr warn">Optional' + : '<p class="attr error">Required'), + ' attribute <code>', $attrs[0], + '</code> is not available.</p>'; + $missing++; + } + } + return $missing; +} + + +sub main { + my $q = shift; + + if (defined($ENV{'Shib_Session_ID'})) { + # logout link + my $idp = $ENV{'Shib_Identity_Provider'}; + if (!defined($idp)) { + $idp = '<span class="error">[UNKNOWN]</span>'; + } + print '<p>'; + print 'A Shibboleth session was established with <em>', $idp, + '</em>.'; + if (defined($ENV{'SHIBTEST_LAZY'})) { + print ' [<a href="', make_logout_uri(), '">Logout</a>]<br />'; + print 'NB: if this webserver is configured to always requires ', + 'authentication for this page, you will be immediately ', + 'redirected to the WAYF/Discovery service after logging out!'; + } + print '</p>'; + + my $errors = 0; + my $warnings = 0; + # CLARIN required attributes + if (scalar(@ATTRIBUTES_REQUIRED) > 0) { + $errors += scan_attributes(\@ATTRIBUTES_REQUIRED, 0); + } + + # CLARIN optional attributes + if (scalar(@ATTRIBUTES_OPTIONAL) > 0) { + $warnings += scan_attributes(\@ATTRIBUTES_OPTIONAL, 1); + } + + # remote user + my $user = $ENV{'REMOTE_USER'}; + $warnings++ unless defined($user); + print '<p class="attr ', (defined($user) ? 'ok' : 'warn'), '">'; + print 'REMOTE_USER: ', + (defined($user) ? $user : 'N/A (not exported by mod_shib?!)'); + print '</p>'; + + if ($errors == 0) { + print '<p class="ok result">Interoperability between your SP ', + 'and the IDP is ', + ($warnings > 0 ? 'sufficent' : 'optimal'), '. ', + $errors, ' error(s), ', + $warnings, ' warning(s)</p>'; + } + else { + print '<p class="error result">Interoperability between your SP ', + 'and the IDP is problematic! ', $errors, ' error(s), ', + $warnings, ' warning(s) <br/>', + 'Please check SP config and IDP release policy.</p>'; + } + # attribute / environment variable / assertion + print '<table class="attr">'; + my $debug_env = (defined($q) && $q->param('debug_env')); + dump_shibboleth_attributes($debug_env); + dump_shibboleth_assertions($ENV{'Shib_Assertion_Count'}); + print '</table>'; + } + else { + # login link + print '<p>No Shibboleth session exists, please <a href="', + make_login_uri(), '">Login</a>.</p>'; + } +} + + +my $style = <<STYLE; +body { + font-family: Arial, Verdana, sans-serif; + font-size: 12pt; + margin: 0; + padding: 2px; +} + +h1 { + font-size: 150%; + margin: 0 0 5px 0; + padding: 0; +} + +h2 { + font-size: 100%; + margin: 1px 0; + padding: 0; +} + +p { + margin: 10px 0; + padding: 4px; +} + +p.ok { + color: #FFFFFF; + background-color: #009900; +} + + +p.warn { + color: #000000; + background-color: #FFFF00; +} + +p.error { + color: #FFFFFF; + background-color: #CC0000; + font-weight: bold; +} + +p.attr { + margin: 1px 0; +} + +p.result { + margin: 20px 0; + font-size: 120%; + font-weight: bold; + border: 2px solid #000000; +} + +code { + font-family: "Courier New", monospace; + font-weight: bold; +} + +span.error { + color: #CC0000; + background-color: inherit; + font-weight: bold; +} + +table { + border: 1px solid #000000; + border-collapse: collapse; + margin: 0; + padding: 0; +} + +td, th { + border: 1px solid #000000; + vertical-align: top; + text-align: left; + margin: 0; + padding: 4px; +} + +th { + font-weight: bold; + font-size: 110%; + color: #FFFFFF; + background-color: transparent; +} + +.header { + color: inherit; + background-color: #707677; +} + +.even { + color: inherit; + background-color: #E7E7E7; +} + +.odd { + color: inherit; + background-color: #CFCFCF; +} +STYLE + +my $q = CGI->new(); +print $q->header(-type => 'text/html', -charset => 'utf-8'); +print $q->start_html(-title => 'CLARIN SPF Interoperability Test Page', + -style => { -code => $style }); +print $q->h1('CLARIN SPF Interoperability Test Page'); +main($q); +print $q->end_html; +exit 0; diff --git a/clarin-dspace-docker/commul-customization/shibboleth2.xml b/clarin-dspace-docker/commul-customization/shibboleth2.xml new file mode 100644 index 0000000000000000000000000000000000000000..d5721b97812f945156c78f7c20335fc29e5fd6ca --- /dev/null +++ b/clarin-dspace-docker/commul-customization/shibboleth2.xml @@ -0,0 +1,173 @@ +<SPConfig xmlns="urn:mace:shibboleth:2.0:native:sp:config" + xmlns:conf="urn:mace:shibboleth:2.0:native:sp:config" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" + logger="syslog.logger" clockSkew="180"> + + <!-- + By default, in-memory StorageService, ReplayCache, ArtifactMap, and SessionCache + are used. See example-shibboleth2.xml for samples of explicitly configuring them. + --> + + <!-- configure some loggers --> + <OutOfProcess logger="shibd.logger" /> + <InProcess logger="native.logger"/> + <!-- + To customize behavior for specific resources on Apache, and to link vhosts or + resources to ApplicationOverride settings below, use web server options/commands. + See https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPConfigurationElements for help. + + For examples with the RequestMap XML syntax instead, see the example-shibboleth2.xml + file, and the https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPRequestMapHowTo topic. + --> + <RequestMapper type="XML"> + <RequestMap> + <Host name="clarin-dev.eurac.edu" + authType="shibboleth" + requireSession="true" + redirectToSSL="443"> + <Path name="/secure" /> + </Host> + + </RequestMap> +</RequestMapper> + + <!-- The ApplicationDefaults element is where most of Shibboleth's SAML bits are defined. --> + <ApplicationDefaults entityID="https://clarin-dev.eurac.edu/Shibboleth.sso/Metadata" + REMOTE_USER="eppn persistent-id targeted-id" + cipherSuites="ECDHE+AESGCM:ECDHE:!aNULL:!eNULL:!LOW:!EXPORT:!RC4:!SHA:!SSLv2"> + + <!-- + Controls session lifetimes, address checks, cookie handling, and the protocol handlers. + You MUST supply an effectively unique handlerURL value for each of your applications. + The value defaults to /Shibboleth.sso, and should be a relative path, with the SP computing + a relative value based on the virtual host. Using handlerSSL="true", the default, will force + the protocol to be https. You should also set cookieProps to "https" for SSL-only sites. + Note that while we default checkAddress to "false", this has a negative impact on the + security of your site. Stealing sessions via cookie theft is much easier with this disabled. + --> + <Sessions lifetime="28800" timeout="3600" relayState="ss:mem" + checkAddress="false" handlerSSL="false" cookieProps="http"> + + <!-- + Configures SSO for a default IdP. To allow for >1 IdP, remove + entityID property and adjust discoveryURL to point to discovery service. + (Set discoveryProtocol to "WAYF" for legacy Shibboleth WAYF support.) + You can also override entityID on /Login query string, or in RequestMap/htaccess. + --> + <SSO + discoveryProtocol="SAMLDS" discoveryURL="https://discovery.clarin.eu/discojuice"> + SAML2 SAML1 + </SSO> + +<!-- <SSO entityID="https://idp.eurac.edu/idp/shibboleth"> + discoveryProtocol="SAMLDS" discoveryURL="https://discovery.clarin.eu/discojuice"> + SAML2 SAML1 + </SSO>--> + + <!-- SAML and local-only logout. --> + <Logout>SAML2 Local</Logout> + + <!-- Extension service that generates "approximate" metadata based on SP configuration. --> + <Handler type="MetadataGenerator" Location="/Metadata" signing="false" template="lindat.eurac.edu.template.metadata.xml"/> + + <!-- Status reporting service. --> + <Handler type="Status" Location="/Status" acl="127.0.0.1 ::1"/> + + <!-- Session diagnostic service. --> + <Handler type="Session" Location="/Session" showAttributeValues="false"/> + + <!-- JSON feed of discovery information. --> + <Handler type="DiscoveryFeed" Location="/DiscoFeed"/> + </Sessions> + + <!-- + Allows overriding of error template information/filenames. You can + also add attributes with values that can be plugged into the templates. + --> + <Errors supportContact="corpman@eurac.edu" + helpLocation="/repository/xmlui/page/about" + styleSheet="/shibboleth-sp/main.css"/> + + <!-- Example of remotely supplied batch of signed metadata. --> + <!-- + <MetadataProvider type="XML" validate="true" + uri="http://example.org/federation-metadata.xml" + backingFilePath="federation-metadata.xml" reloadInterval="7200"> + <MetadataFilter type="RequireValidUntil" maxValidityInterval="2419200"/> + <MetadataFilter type="Signature" certificate="fedsigner.pem"/> + <DiscoveryFilter type="Blacklist" matcher="EntityAttributes" trimTags="true" + attributeName="http://macedir.org/entity-category" + attributeNameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" + attributeValue="http://refeds.org/category/hide-from-discovery" /> + </MetadataProvider> + --> + <MetadataProvider + xmlns:xi="http://www.w3.org/2001/XInclude" + type="Chaining"> + <!-- SAML metadata about all contracted identity federations' production IdPs. --> + <MetadataProvider + type="XML" + uri="https://infra.clarin.eu/aai/prod_md_about_spf_idps.xml" + backingFilePath="prod_md_about_spf_idps.xml" + reloadInterval="7200"> + <!-- # tag::MetadataFilter[] --> + <MetadataFilter + xmlns="urn:mace:shibboleth:2.0:native:sp:config" + type="Blacklist"> + <Exclude>https://openidp.aco.net/saml</Exclude> + </MetadataFilter> + <!-- # tag::MetadataFilter[] --> + </MetadataProvider> + <!-- SAML metadata about the CLARIN IdP. --> + <MetadataProvider + type="XML" + uri="https://infra.clarin.eu/aai/prod_md_about_clarin_erics_idp.xml" + backingFilePath="prod_md_about_clarin_erics_idp.xml" + reloadInterval="7200"/> + </MetadataProvider> + <!--<MetadataProvider type="XML" + uri="https://idp.eurac.edu/idp/shibboleth" + backingFilePath="/opt/shibboleth-sp-fastcgi/var/cache/shibboleth/eurac-idp.xml" + reloadInterval="7200" legacyOrgNames="true"> + </MetadataProvider>--> + + <!-- Example of locally maintained metadata. --> + <!-- + <MetadataProvider type="XML" validate="true" file="partner-metadata.xml"/> + --> + + <!-- Map to extract attributes from SAML assertions. --> + <AttributeExtractor type="XML" validate="true" reloadChanges="false" path="attribute-map.xml"/> + + <!-- Use a SAML query if no attributes are supplied during SSO. --> + <AttributeResolver type="Query" subjectMatch="true"/> + + <!-- Default filtering policy for recognized attributes, lets other data pass. --> + <AttributeFilter type="XML" validate="true" path="attribute-policy.xml"/> + + <!-- Simple file-based resolver for using a single keypair. --> + <CredentialResolver type="File" key="sp-key.pem" certificate="sp-cert.pem"/> + + <!-- + The default settings can be overridden by creating ApplicationOverride elements (see + the https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPApplicationOverride topic). + Resource requests are mapped by web server commands, or the RequestMapper, to an + applicationId setting. + + Example of a second application (for a second vhost) that has a different entityID. + Resources on the vhost would map to an applicationId of "admin": + --> + <!-- + <ApplicationOverride id="admin" entityID="https://admin.example.org/shibboleth"/> + --> + </ApplicationDefaults> + + <!-- Policies that determine how to process and authenticate runtime messages. --> + <SecurityPolicyProvider type="XML" validate="true" path="security-policy.xml"/> + + <!-- Low-level configuration about protocols and bindings available for use. --> + <ProtocolProvider type="XML" validate="true" reloadChanges="false" path="protocols.xml"/> + +</SPConfig> diff --git a/clarin-dspace-docker/commul-customization/shibboleth_sp_with_fastcgi.sh b/clarin-dspace-docker/commul-customization/shibboleth_sp_with_fastcgi.sh new file mode 100644 index 0000000000000000000000000000000000000000..5486b399dfc559290a0141e33c77652f7947cc57 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/shibboleth_sp_with_fastcgi.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# https://github.com/ufal/lindat-dspace/wiki/Building-Shibboleth-with-FastCGI-support +# ensure the versions are still latest + +apt-get install -y libfcgi-dev libboost-all-dev openssl libssl-dev pkg-config libcurl4-openssl-dev + +INSTALLDIR=/opt/shibboleth-sp-fastcgi + +function get { + local dirname=$1 + local version=$2 + local url=$3 + local archive="$dirname-$version.tar.gz" + + if [ ! -d "$dirname" ]; then + wget -O "$archive" "$url$archive" + tar -xzvf "$archive" + mv `tar -ztf "$archive" | head -n 1` "$dirname.$version" + ln -s $dirname.$version $dirname + rm "$archive" + fi +} + +get log4shib 1.0.9 http://shibboleth.net/downloads/log4shib/latest/ +get xerces-c 3.2.0 http://mirror.hosting90.cz/apache/xerces/c/3/sources/ +get xml-security-c 1.7.3 http://mirror.hosting90.cz/apache/santuario/c-library/ +get xmltooling 1.6.0 http://shibboleth.net/downloads/c++-opensaml/latest/ +get opensaml 2.6.0 http://shibboleth.net/downloads/c++-opensaml/latest/ +get shibboleth-sp 2.6.0 http://shibboleth.net/downloads/service-provider/latest/ + +sed -i '676,679 s/^/\/\//' shibboleth-sp.2.6.0/shibsp/handler/impl/MetadataGenerator.cpp +sed -i '687,688 s/^/\/\//' shibboleth-sp.2.6.0/shibsp/handler/impl/MetadataGenerator.cpp + +function compile { + local dirname=$1 + local config="--enable-option-checking=fatal $2" + + cd $dirname && \ + ./configure $config && \ + make && \ + make install && \ + cd .. +} + +compile log4shib "--disable-static --disable-doxygen --prefix=$INSTALLDIR" && \ +compile xerces-c "--disable-netaccessor-curl --prefix=$INSTALLDIR" && \ +compile xml-security-c "--without-xalan --disable-static \ + --with-xerces=$INSTALLDIR \ + --prefix=$INSTALLDIR" && \ +compile xmltooling "--with-log4shib=$INSTALLDIR --prefix=$INSTALLDIR -C" && \ +compile opensaml "--with-log4shib=$INSTALLDIR --prefix=$INSTALLDIR -C" && \ +compile shibboleth-sp "--with-log4shib=$INSTALLDIR \ + --prefix=$INSTALLDIR \ + --with-fastcgi" diff --git a/clarin-dspace-docker/commul-customization/start_stack.sh b/clarin-dspace-docker/commul-customization/start_stack.sh new file mode 100644 index 0000000000000000000000000000000000000000..a0140c329391a01555a29364138e29f2faea4e11 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/start_stack.sh @@ -0,0 +1,33 @@ +#!/bin/bash +#Put start commands here +### Postgres ### +# echo "Starting postgres" +# /etc/init.d/postgresql start +### Tomcat ### +echo "Starting tomcat" +/etc/init.d/tomcat8 start +### Handle server ### +HANDLE_SERVER=/etc/init.d/handle-server +if [[ -r $HANDLE_SERVER ]]; then + echo "Starting handle server"; + $HANDLE_SERVER start; +else + echo "Handle server not present - ignoring start command"; +fi + +### nginx ### +echo "Starting nginx" +/etc/init.d/nginx start + +### supervisor (shibboleth + fastcgi) ### +# make sure supervisord is running +/sbin/start-stop-daemon -p /tmp/supervisord.pid --start --exec /usr/bin/python -- /usr/local/bin/supervisord -c /etc/supervisord.conf +echo "Starting all supervised programs" +supervisorctl start all + +### apache ### +#echo "Starting apache" +#apache2ctl start +### shibboleth ### +#echo "Starting shibboleth" +#/etc/init.d/shibd start diff --git a/clarin-dspace-docker/commul-customization/stop_stack.sh b/clarin-dspace-docker/commul-customization/stop_stack.sh new file mode 100644 index 0000000000000000000000000000000000000000..43223d6132134ec889fdf4bc1f7b7d19c85c1131 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/stop_stack.sh @@ -0,0 +1,33 @@ +#!/bin/bash +#Put stop commands and cleanup here +### Tomcat ### +echo "Stopping tomcat" +/etc/init.d/tomcat8 stop + +### Postgres ### +# echo "Stopping postgres" +# /etc/init.d/postgresql stop + +### Handle server ### +HANDLE_SERVER=/etc/init.d/handle-server +if [[ -r $HANDLE_SERVER ]]; then + echo "Stopping handle server"; + $HANDLE_SERVER stop; +else + echo "Handle server not present - ignoring stop command"; +fi + +### nginx ### +echo "Stopping nginx" +/etc/init.d/nginx stop + +### supervisor (shibboleth + fastcgi) ### +echo "Stopping all supervised programs" +supervisorctl stop all +### apache #### +#echo "Stop apache" +#apache2ctl stop +### Shibboleth ### +#echo "Stopping shibboleth" +#/etc/init.d/shibd stop + diff --git a/clarin-dspace-docker/commul-customization/supervisord.conf b/clarin-dspace-docker/commul-customization/supervisord.conf new file mode 100644 index 0000000000000000000000000000000000000000..9ad90c6ce2bfa44329b27dd3f40b5450989b8982 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/supervisord.conf @@ -0,0 +1,92 @@ +; Sample supervisor config file. +; +; For more information on the config file, please see: +; http://supervisord.org/configuration.html +; +; Notes: +; - Shell expansion ("~" or "$HOME") is not supported. Environment +; variables can be expanded using this syntax: "%(ENV_HOME)s". +; - Comments must have a leading space: "a=b ;comment" not "a=b;comment". + +[unix_http_server] +file=/tmp/supervisor.sock ; (the path to the socket file) +;chmod=0700 ; socket file mode (default 0700) +;chown=nobody:nogroup ; socket file uid:gid owner +;username=user ; (default is no username (open server)) +;password=123 ; (default is no password (open server)) + +;[inet_http_server] ; inet (TCP) server disabled by default +;port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface) +;username=user ; (default is no username (open server)) +;password=123 ; (default is no password (open server)) + +[supervisord] +logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log) +logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB) +logfile_backups=10 ; (num of main logfile rotation backups;default 10) +loglevel=info ; (log level;default info; others: debug,warn,trace) +pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid) +nodaemon=true ; (start in foreground if true;default false) +minfds=1024 ; (min. avail startup file descriptors;default 1024) +minprocs=200 ; (min. avail process descriptors;default 200) +;umask=022 ; (process file creation umask;default 022) +;user=chrism ; (default is current user, required if root) +;identifier=supervisor ; (supervisord identifier, default is 'supervisor') +;directory=/tmp ; (default is not to cd during start) +;nocleanup=true ; (don't clean up tempfiles at start;default false) +;childlogdir=/tmp ; ('AUTO' child log dir, default $TEMP) +;environment=KEY="value" ; (key value pairs to add to environment) +;strip_ansi=false ; (strip ansi escape codes in logs; def. false) + +; the below section must remain in the config file for RPC +; (supervisorctl/web interface) to work, additional interfaces may be +; added by defining them in separate rpcinterface: sections +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket +;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket +;username=chris ; should be same as http_username if set +;password=123 ; should be same as http_password if set +;prompt=mysupervisor ; cmd line prompt (default "supervisor") +;history_file=~/.sc_history ; use readline history if available + +[program:shibboleth] +command=/opt/shibboleth-sp-fastcgi/sbin/shibd -F -f +stdout_logfile=/var/log/supervisor/shibd.log +stderr_logfile=/var/log/supervisor/shibd.error.log + +[fcgi-program:shibauthorizer] +command=/opt/shibboleth-sp-fastcgi/lib/shibboleth/shibauthorizer +socket=unix:///opt/shibboleth-sp-fastcgi/shibauthorizer.sock +socket_owner=www-data:www-data +socket_mode=0660 +user=www-data +stdout_logfile=/var/log/supervisor/shibauthorizer.log +stderr_logfile=/var/log/supervisor/shibauthorizer.error.log + +[fcgi-program:shibresponder] +command=/opt/shibboleth-sp-fastcgi/lib/shibboleth/shibresponder +socket=unix:///opt/shibboleth-sp-fastcgi/shibresponder.sock +socket_owner=www-data:www-data +socket_mode=0660 +user=www-data +stdout_logfile=/var/log/supervisor/shibresponder.log +stderr_logfile=/var/log/supervisor/shibresponder.error.log + + + +[program:nginx] +command=/opt/nginx/sbin/nginx -g "daemon off;" +user=root +autostart=true +autorestart=true +stopwaitsecs=30 + + +; The below sample program section shows all possible program subsection values, +; create one or more 'real' program: sections to be able to control them under +; supervisor. + + diff --git a/clarin-dspace-docker/commul-customization/tomcat-server.xml b/clarin-dspace-docker/commul-customization/tomcat-server.xml new file mode 100644 index 0000000000000000000000000000000000000000..d2003f72b254291ea37a23dfae19b7f53f58b97e --- /dev/null +++ b/clarin-dspace-docker/commul-customization/tomcat-server.xml @@ -0,0 +1,152 @@ +<?xml version='1.0' encoding='utf-8'?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- Note: A "Server" is not itself a "Container", so you may not + define subcomponents such as "Valves" at this level. + Documentation at /docs/config/server.html + --> +<Server port="8005" shutdown="SHUTDOWN"> + <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> + <!-- Security listener. Documentation at /docs/config/listeners.html + <Listener className="org.apache.catalina.security.SecurityListener" /> + --> + <!--APR library loader. Documentation at /docs/apr.html --> + <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> + <!-- Prevent memory leaks due to use of particular java/javax APIs--> + <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> + <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> + <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> + + <!-- Global JNDI resources + Documentation at /docs/jndi-resources-howto.html + --> + <GlobalNamingResources> + <!-- Editable user database that can also be used by + UserDatabaseRealm to authenticate users + --> + <Resource name="UserDatabase" auth="Container" + type="org.apache.catalina.UserDatabase" + description="User database that can be updated and saved" + factory="org.apache.catalina.users.MemoryUserDatabaseFactory" + pathname="conf/tomcat-users.xml" /> + </GlobalNamingResources> + + <!-- A "Service" is a collection of one or more "Connectors" that share + a single "Container" Note: A "Service" is not itself a "Container", + so you may not define subcomponents such as "Valves" at this level. + Documentation at /docs/config/service.html + --> + <Service name="Catalina"> + + <!--The connectors can use a shared executor, you can define one or more named thread pools--> + <!-- + <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" + maxThreads="150" minSpareThreads="4"/> + --> + + + <!-- A "Connector" represents an endpoint by which requests are received + and responses are returned. Documentation at : + Java HTTP Connector: /docs/config/http.html (blocking & non-blocking) + Java AJP Connector: /docs/config/ajp.html + APR (HTTP/AJP) Connector: /docs/apr.html + Define a non-SSL/TLS HTTP/1.1 Connector on port 8080 + --> + <Connector port="8080" protocol="HTTP/1.1" + connectionTimeout="20000" + URIEncoding="UTF-8" + redirectPort="8443" /> + <!-- A "Connector" using the shared thread pool--> + <!-- + <Connector executor="tomcatThreadPool" + port="8080" protocol="HTTP/1.1" + connectionTimeout="20000" + redirectPort="8443" /> + --> + <!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443 + This connector uses the NIO implementation that requires the JSSE + style configuration. When using the APR/native implementation, the + OpenSSL style configuration is required as described in the APR/native + documentation --> + <!-- + <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol" + maxThreads="150" SSLEnabled="true" scheme="https" secure="true" + clientAuth="false" sslProtocol="TLS" /> + --> + + <!-- Define an AJP 1.3 Connector on port 8009 --> + <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> + + + <!-- An Engine represents the entry point (within Catalina) that processes + every request. The Engine implementation for Tomcat stand alone + analyzes the HTTP headers included with the request, and passes them + on to the appropriate Host (virtual host). + Documentation at /docs/config/engine.html --> + + <!-- You should set jvmRoute to support load-balancing via AJP ie : + <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1"> + --> + <Engine name="Catalina" defaultHost="localhost"> + + <!--For clustering, please take a look at documentation at: + /docs/cluster-howto.html (simple how to) + /docs/config/cluster.html (reference documentation) --> + <!-- + <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> + --> + + <!-- Use the LockOutRealm to prevent attempts to guess user passwords + via a brute-force attack --> + <Realm className="org.apache.catalina.realm.LockOutRealm"> + <!-- This Realm uses the UserDatabase configured in the global JNDI + resources under the key "UserDatabase". Any edits + that are performed against this UserDatabase are immediately + available for use by the Realm. --> + <Realm className="org.apache.catalina.realm.UserDatabaseRealm" + resourceName="UserDatabase"/> + </Realm> + + <Host name="localhost" appBase="webapps" + unpackWARs="true" autoDeploy="true"> + + <Context path="/repository/xmlui" docBase="/opt/lindat-dspace/installation/webapps/xmlui" + reloadable="true" cachingAllowed="false" allowLinking="true" /> + <Context path="/repository/solr" docBase="/opt/lindat-dspace/installation/webapps/solr" + reloadable="true" cachingAllowed="false" allowLinking="true" /> + <Context path="/repository/oai" docBase="/opt/lindat-dspace/installation/webapps/oai" + reloadable="true" cachingAllowed="false" allowLinking="true" /> + <Context path="/repository/rest" docBase="/opt/lindat-dspace/installation/webapps/rest" + reloadable="true" cachingAllowed="false" allowLinking="true" /> + + <!-- SingleSignOn valve, share authentication between web applications + Documentation at: /docs/config/valve.html --> + <!-- + <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> + --> + + <!-- Access log processes all example. + Documentation at: /docs/config/valve.html + Note: The pattern used is equivalent to using pattern="common" --> + <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" + prefix="localhost_access_log" suffix=".txt" + pattern="%h %l %u %t "%r" %s %b" /> + + </Host> + </Engine> + </Service> +</Server> diff --git a/clarin-dspace-docker/commul-customization/tomcat8 b/clarin-dspace-docker/commul-customization/tomcat8 new file mode 100644 index 0000000000000000000000000000000000000000..58470968ecb9d930aaecd526438fc1cf68a45375 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/tomcat8 @@ -0,0 +1,74 @@ +#!/bin/bash +# +#https://wiki.debian.org/LSBInitScripts +### BEGIN INIT INFO +# Provides: tomcat8 +# Required-Start: $local_fs $remote_fs $network +# Required-Stop: $local_fs $remote_fs $network +# Should-Start: $named +# Should-Stop: $named +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start Tomcat. +# Description: Start the Tomcat servlet engine. +### END INIT INFO + +# Run Tomcat 8 as this user ID and group ID +TOMCAT8_USER=tomcat8 +TOMCAT8_GROUP=tomcat8 + +# this is a work-around until there is a suitable runtime replacement +# for dpkg-architecture for arch:all packages +# this function sets the variable JDK_DIRS +find_jdks() +{ + for java_version in 9 8 7 + do + for jvmdir in /usr/lib/jvm/java-${java_version}-openjdk-* \ + /usr/lib/jvm/jdk-${java_version}-oracle-* \ + /usr/lib/jvm/jre-${java_version}-oracle-* + do + if [ -d "${jvmdir}" ] + then + JDK_DIRS="${JDK_DIRS} ${jvmdir}" + fi + done + done + + # Add older non multi arch installations + JDK_DIRS="${JDK_DIRS} /usr/lib/jvm/java-7-oracle" +} + +# The first existing directory is used for JAVA_HOME (if JAVA_HOME is not +# defined in $DEFAULT) +JDK_DIRS="/usr/lib/jvm/default-java" +find_jdks + +# Look for the right JVM to use +for jdir in $JDK_DIRS; do + if [ -r "$jdir/bin/java" -a -z "${JAVA_HOME}" ]; then + JAVA_HOME="$jdir" + fi +done +export JAVA_HOME + +export CATALINA_HOME=/opt/tomcat8 +export PATH=$JAVA_HOME/bin:$PATH + +start() { + echo "Starting Tomcat 8..." + /bin/su -s /bin/bash $TOMCAT8_USER -c $CATALINA_HOME/bin/startup.sh +} +stop() { + echo "Stopping Tomcat 8..." + /bin/su -s /bin/bash $TOMCAT8_USER -c '$CATALINA_HOME/bin/shutdown.sh -force' +} +case $1 in + start|stop) $1;; + restart) stop; start;; + *) echo "Usage : $0 <start|stop|restart>"; exit 1;; +esac + +exit 0 + + diff --git a/clarin-dspace-docker/commul-customization/variable.makefile b/clarin-dspace-docker/commul-customization/variable.makefile new file mode 100644 index 0000000000000000000000000000000000000000..b4eb87804d540bee93d17b82b370b64be51e7b43 --- /dev/null +++ b/clarin-dspace-docker/commul-customization/variable.makefile @@ -0,0 +1,31 @@ +# Makefile for this project +# by LINDAT/CLARIN dev team +# +# Note: If you want to change this file, copy it to project/config +# + +# lindat-common settings +DIR_LINDAT_COMMON_THEME :=/opt/lindat-common +URL_LINDAT_COMMON_GIT :=https://github.com/commul/lindat-common.git +LINDAT_COMMON_THEME_FETCH=git fetch && git checkout -f releases && git pull + +# tomcat +TOMCAT_VERSION=8 +TOMCAT_USER:=tomcat$(TOMCAT_VERSION) +TOMCAT_GROUP:=tomcat$(TOMCAT_VERSION) +TOMCAT_CONF:=/etc/$(TOMCAT_USER) + +# dspace +DSPACE_USER:=dspace + +# tool directories +DIRECTORY_POSTGRESQL:=/var/lib/postgresql +BACKUP2l:=/usr/sbin/backup2l + +# database settings - mostly for recovering +#RESTORE_FROM_DATABASE=prod-dspace-1.8 +RESTORE_FROM_DATABASE=lrt-dspace-1.8 + + +# you can use different versions e.g., export pg_dump=pg_dump --cluster 9.4/main +export pg_dump=pg_dump diff --git a/clarin-dspace-docker/difference-between-clarin-dev-and-clarin.txt b/clarin-dspace-docker/difference-between-clarin-dev-and-clarin.txt new file mode 100644 index 0000000000000000000000000000000000000000..4c0f8d386b25ae3552be40e9c8f94a198f07b0c1 --- /dev/null +++ b/clarin-dspace-docker/difference-between-clarin-dev-and-clarin.txt @@ -0,0 +1,12 @@ + +Lindat Production workflow + +nach Schritt './password_mod.sh' + +./clarin-dev2clarin.sh + +nach Schritt 'make new_deploy' +grep -rl clarin-dev /opt/lindat-common/public/ | xargs -i perl -pi -e 's/clarin-dev/clarin/g' {} + + + diff --git a/clarin-dspace-docker/docker-compose.yml b/clarin-dspace-docker/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..44d464234fe9b554dc038e55c19a7ddb9af12208 --- /dev/null +++ b/clarin-dspace-docker/docker-compose.yml @@ -0,0 +1,40 @@ +version: "3" +services: + + postgres: + build: + context: . + dockerfile: Dockerfile.psql + image: eurac_psql + restart: always + environment: + POSTGRES_PASSWORD: mysecretpassword + nginx: + build: + context: . + dockerfile: Dockerfile.nginx + image: eurac_nginx + hostname: clarin-dev.eurac.edu + restart: always + ports: + - "80:80" + - "443:443" + handle: + build: + context: . + dockerfile: Dockerfile.handle + image: eurac_handle + restart: always + ports: + - "8000:8000" + - "2641:2641" + dspace: + build: + context: . + dockerfile: Dockerfile.dspace + image: eurac_dspace + depends_on: + - postgres + restart: always + ports: + - "8080:8080" diff --git a/clarin-dspace-docker/dspace-entrypoint.sh b/clarin-dspace-docker/dspace-entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..4d6624c9b62669d28c3308bc2aa3e581c88c0368 --- /dev/null +++ b/clarin-dspace-docker/dspace-entrypoint.sh @@ -0,0 +1,214 @@ +#!/bin/bash +# +# Wiki https://github.com/ufal/clarin-dspace/wiki/Installation +# +# + +MAJOR=0 +BUILD=2 +VERSION="${MAJOR}.${BUILD}" + + +#set -ex + +# let's define some variables +export DSPACE_INSTANCE_NAME=repository +export DSPACE_SOURCE_DIRECTORY=/opt/repository/sources/dspace +export DSPACE_WORKSPACE=/opt/repository/workspace +export DSPACE_INSTALLATION_DIRECTORY=/opt/repository/installations + +AAI_CONFIGFILE="/opt/lindat-dspace/installation/webapps/xmlui/themes/UFAL/lib/js/aai_config.js" +DSPACE_CONFIGFILE="$DSPACE_WORKSPACE/sources/local.properties" + +# +# functions +# +_logger () { + MSG=$1 + if [ ${DEBUG} ]; then + echo -ne "${MSG}" +# else +# echo -ne "${MSG}" >> ${LogFile} + fi +} + + +_loggerDate() { + _logger "[$(date)] $*" +} + + +_exit() { + if [ -z $1 ]; then + E_CODE=0 + else + E_CODE=$1 + fi + rm -rf ${TemporaryPath} + _loggerDate "Script completed\n" + exit ${E_CODE} +} + + +_printVersion() { + echo "${BASENAME} version ${VERSION}" +} + +_printHelp() { + echo "${BASENAME}" + echo "Assembly some stuff, creating things..." + echo + echo "Supported options:" + echo " -d|--debug enable debug" + echo " -V|--version report program version" + echo " -h|--help print this help" + echo +} + +_printComment () { + MSG=$1 + echo "<"'!'"-- ${MSG} -->" +} + +_checkenv () { +set -x + _loggerDate "Checking env variables " + echo $* + tot_args=$# + echo $tot_args + count=0 + while [ "$1" ] + do + echo "." + shift + ((count++)) + done + echo $tot_args $count + if [ "$tot_args" -ne "$count" ];then + exit 1 + fi +set +x +} + +# +# main script +# + +# argument parsing +while [ $1 ]; do + case $1 in + -d|--debug) DEBUG=1 + shift + ;; + -V|--version) _printVersion + exit 0 + ;; + -h|--help) _printHelp + exit 0 + ;; + -t|--test) _notifyByIcingaPassiveCheck + exit 0 + ;; + *) _printHelp + exit 0 + ;; + esac +done + +_loggerDate "Starting ${BASENAME}\n" + +_loggerDate "Cloning lindat-dspace repository..." + +if [ ! -d $DSPACE_SOURCE_DIRECTORY ] +then + _logger "in $DSPACE_SOURCE_DIRECTORY \n" + git clone https://github.com/ufal/lindat-dspace.git -b lindat $DSPACE_SOURCE_DIRECTORY +else + _logger "skip! $DSPACE_SOURCE_DIRECTORY already exists \n" +fi + +cd $DSPACE_SOURCE_DIRECTORY/utilities/project_helpers + +_loggerDate "Creating workspace..." + +if [ ! -d $DSPACE_WORKSPACE ] +then + _logger "$DSPACE_WORKSPACE \n" + ./setup.sh /opt/repository/workspace +else + _logger "skip! $DSPACE_WORKSPACE already exists \n" +fi + + +_loggerDate "Copy makefile..." +if [ ! -f $DSPACE_WORKSPACE/config/variable.makefile ] +then + cp /tmp/commul-customization/variable.makefile $DSPACE_WORKSPACE/config/ + _logger "$DSPACE_WORKSPACE/config/variable.makefile\n" +else + _logger "skip! $DSPACE_WORKSPACE/config/variable.makefile already exists\n" +fi +_loggerDate "Copy local.properties..." +if [ ! -f $DSPACE_CONFIGFILE ] +then + set -x + cp /tmp/commul-customization/local.properties $DSPACE_CONFIGFILE + set +x +else + _logger "skip! $DSPACE_CONFIGFILE already exists\n" +fi + + +_loggerDate "Compilation and Deployment of DSpace..." +cd $DSPACE_WORKSPACE/scripts/ + +if [ ! -f $DSPACE_WORKSPACE/scripts/install_libs.done ] +then + make install_libs + touch $DSPACE_WORKSPACE/scripts/install_libs.done +else + _logger "already done \n" +fi + + +if [ ! -f $DSPACE_WORKSPACE/scripts/fresh_install.done ] +then + make compile + make fresh_install + touch $DSPACE_WORKSPACE/scripts/fresh_install.done +else + _logger "already done \n" +fi + +if [ ! -f $DSPACE_WORKSPACE/scripts/postinstall.done ] +then + make postinstall + touch $DSPACE_WORKSPACE/scripts/postinstall.done + _logger "OK\n" +else + _logger "already done \n" +fi + +_loggerDate "Copy aai_config.js..." +if [ ! -f $AAI_CONFIGFILE ] +then + set -x + cp /tmp/commul-customization/aai_config.js $AAI_CONFIGFILE + set +x +else + _logger "skip! $AAI_CONFIGFILE already exists\n" +fi + +_loggerDate "Create dspace admin..." +if [ ! -f $DSPACE_WORKSPACE/scripts/tomcatadmin.done ] +then + su tomcat8 -c "/opt/lindat-dspace/installation/bin/dspace create-administrator" + touch $DSPACE_WORKSPACE/scripts/tomcatadmin.done + _logger "OK\n" +else + _logger "already done \n" +fi + +_loggerDate "Starting Tomcat..." +/etc/init.d/tomcat8 start +sleep infinity diff --git a/clarin-dspace-docker/password_mod.sh.dist b/clarin-dspace-docker/password_mod.sh.dist new file mode 100644 index 0000000000000000000000000000000000000000..ebb0bdb5e7ac2ffe83c81e77283c06a4adc7b25a --- /dev/null +++ b/clarin-dspace-docker/password_mod.sh.dist @@ -0,0 +1,6 @@ +#!/bin/bash + +perl -pi -e 's/secretdspaceuser/yourdspaceusername/; s/secretdspacepassword/yourdspacepassword/;' Dockerfile +perl -pi -e 's/secretdspaceuser/yourdspaceusername/; s/secretdspacepassword/yourdspacepassword/; s/secretdspacedbname/yourdspacedbname/; s/secretdspaceutilsdbname/yourdspaceutilsdbname/;' commul-customization/init-dspace-dbs.sh +perl -pi -e 's/secretdspaceuser/yourdspaceusername/; s/secretdspacepassword/yourdspacepassword/; s/secretmailuser/yourmailuser/; s/secretmailpassword/yourmailpassword/;' commul-customization/local.properties + diff --git a/kubernetes/helm/dspace/.gitignore b/kubernetes/helm/dspace/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..8f2ea6bc075b7d23c5083df53a739bc4549ef49e --- /dev/null +++ b/kubernetes/helm/dspace/.gitignore @@ -0,0 +1,2 @@ +config.yaml +.DS_Store diff --git a/kubernetes/helm/dspace/.helmignore b/kubernetes/helm/dspace/.helmignore new file mode 100644 index 0000000000000000000000000000000000000000..f0c13194444163d1cba5c67d9e79231a62bc8f44 --- /dev/null +++ b/kubernetes/helm/dspace/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/kubernetes/helm/dspace/Chart.yaml b/kubernetes/helm/dspace/Chart.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f4bc22a98525343cb4393a4523b23bf8bcfdb3cb --- /dev/null +++ b/kubernetes/helm/dspace/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +description: A Helm chart for Kubernetes +name: dspace +version: 0.1.0 diff --git a/kubernetes/helm/dspace/README.md b/kubernetes/helm/dspace/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2ba38942722d36bc0db33a95a160c32968abe866 --- /dev/null +++ b/kubernetes/helm/dspace/README.md @@ -0,0 +1,79 @@ +# Design Document + + +## PostgreSQL +PostgreSQL is deployed using official [helm](https://github.com/kubernetes/charts/tree/master/stable/postgresql "helm"). +Additional env variables are added : +- dspaceuser +- dspacepassword +- dspacedbname +- dspaceutilsdbname + +Other changes: +- added adapt_utilities_sql.sh to Dockerfile. +- modified commul-customization/init-dspace-dbs.sh to add env params + +### Kubertes cli +How to show **pv** (persistent volume), **pvc** (persistsne volume claim), **po** (pod) +```bash +$ kubectl -n dspace get pv,pvc,po -o wide +NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE +pv/pvc-e38405df-97b4-11e7-ba35-b60483de6a40 8Gi RWO Delete Bound dspace/dspace-postgresql auto 7d + +NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE +pvc/dspace-postgresql Bound pvc-e38405df-97b4-11e7-ba35-b60483de6a40 8Gi RWO auto 7d + +NAME READY STATUS RESTARTS AGE IP NODE +po/dspace-postgresql-4284405808-xfx5w 1/1 Running 0 6d 10.12.36.39 10.7.2.234 + + +``` +## Tomcat +configured two cephfs mount points, so container can scale +- /opt/lindat-dspace +- /opt/repository + +Other notes: +- installation is done manually [1] +- tomcat is started manually [2] + +```bash +$ kubectl -n dspace get po +NAME READY STATUS RESTARTS AGE +dspace-977584178-m9wsv 1/1 Running 16 16h +dspace-postgresql-4284405808-xfx5w 1/1 Running 0 1d +``` +### Kubertes cli +#### How to enable port forward +```bash +$ kubectl -n dspace port-forward dspace-977584178-m9wsv 8080:8080 +Forwarding from 127.0.0.1:8080 -> 8080 +Forwarding from [::1]:8080 -> 8080 +Handling connection for 8080 +Handling connection for 8080 +``` +now you can connect to http://localhost:8080/repository/xmlui/ + +#### How to connect into pod +```bash +$ kubectl -n dspace exec -it dspace-977584178-m9wsv bash +``` + +## Web Server +- Substitute Apache for Ngnix. +- added env variable SHIB_HOSTNAME (pod's hostname is a pretty different concept) +- Web server's certificates are injected via secrets +- Shibboleth config pushed into container [4] + +```bash +docker run -it -p 80:80 -p 443:443 -e SHIB_HOSTNAME="lindat-dev.eurac.edu" gitlab.scientificnet.org:4567/kube/kubernetes/dspace/apache:0.1 +``` + +# To Do +- [1] [PostgreSQL] we need/should automatize with a entrypoint..sh script + psudo-code: + if exist /path/file/installed, do nothing + else, install! +- [2] [Tomcat] write a entrypoint.sh to start tomcat +- [3] [Tomcat] use env variables in /opt/repository/sources/dspace/dspace/target/dspace-installer/config/dspace.cfg file +- [4] [Web Server] - use config map to supply Shibboleth config diff --git a/kubernetes/helm/dspace/charts/postgresql/Chart.yaml b/kubernetes/helm/dspace/charts/postgresql/Chart.yaml new file mode 100755 index 0000000000000000000000000000000000000000..f7993476d0af02e7272617529f24049a8c98434b --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/Chart.yaml @@ -0,0 +1,18 @@ +description: Object-relational database management system (ORDBMS) with an emphasis + on extensibility and on standards-compliance. +engine: gotpl +home: https://www.postgresql.org/ +icon: https://www.postgresql.org/media/img/about/press/elephant.png +keywords: +- postgresql +- postgres +- database +- sql +maintainers: +- name: swordbeta +- name: databus23 +name: postgresql +sources: +- https://github.com/kubernetes/charts +- https://github.com/docker-library/postgres +version: 0.8.1 diff --git a/kubernetes/helm/dspace/charts/postgresql/README.md b/kubernetes/helm/dspace/charts/postgresql/README.md new file mode 100755 index 0000000000000000000000000000000000000000..e213b2e85ae8b7ba2dc86ae4bd99e0dfa50d50e6 --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/README.md @@ -0,0 +1,132 @@ +# PostgreSQL + +[PostgreSQL](https://postgresql.org) is a powerful, open source object-relational database system. It has more than 15 years of active development and a proven architecture that has earned it a strong reputation for reliability, data integrity, and correctness. + +## TL;DR; + +```bash +$ helm install stable/postgresql +``` + +## Introduction + +This chart bootstraps a [PostgreSQL](https://github.com/docker-library/postgres) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +## Prerequisites + +- Kubernetes 1.4+ with Beta APIs enabled +- PV provisioner support in the underlying infrastructure (Only when persisting data) + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```bash +$ helm install --name my-release stable/postgresql +``` + +The command deploys PostgreSQL on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```bash +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Configuration + +The following tables lists the configurable parameters of the PostgresSQL chart and their default values. + +| Parameter | Description | Default | +| ----------------------- | --------------------------------------------- | ---------------------------------------------------------- | +| `image` | `postgres` image repository | `postgres` | +| `imageTag` | `postgres` image tag | `9.6.2` | +| `imagePullPolicy` | Image pull policy | `Always` if `imageTag` is `latest`, else `IfNotPresent` | +| `postgresUser` | Username of new user to create. | `postgres` | +| `postgresPassword` | Password for the new user. | random 10 characters | +| `postgresDatabase` | Name for new database to create. | `postgres` | +| `postgresInitdbArgs` | Initdb Arguments | `nil` | +| `persistence.enabled` | Use a PVC to persist data | `true` | +| `persistence.existingClaim`| Provide an existing PersistentVolumeClaim | `nil` | +| `persistence.storageClass` | Storage class of backing PVC | `nil` (uses alpha storage class annotation) | +| `persistence.accessMode` | Use volume as ReadOnly or ReadWrite | `ReadWriteOnce` | +| `persistence.size` | Size of data volume | `8Gi` | +| `persistence.subPath` | Subdirectory of the volume to mount at | `postgresql-db` | +| `resources` | CPU/Memory resource requests/limits | Memory: `256Mi`, CPU: `100m` | +| `metrics.enabled` | Start a side-car prometheus exporter | `false` | +| `metrics.image` | Exporter image | `wrouesnel/postgres_exporter` | +| `metrics.imageTag` | Exporter image | `v0.1.1` | +| `metrics.imagePullPolicy` | Exporter image pull policy | `IfNotPresent` | +| `metrics.resources` | Exporter resource requests/limit | Memory: `256Mi`, CPU: `100m` | +| `metrics.customMetrics` | Additional custom metrics | `nil` | +| `service.externalIPs` | External IPs to listen on | `[]` | +| `service.port` | TCP port | `5432` | +| `service.type` | k8s service type exposing ports, e.g. `NodePort`| `ClusterIP` | +| `networkPolicy.enabled` | Enable NetworkPolicy | `false` | +| `networkPolicy.allowExternal` | Don't require client label for connections | `true` | + +The above parameters map to the env variables defined in [postgres](http://github.com/docker-library/postgres). For more information please refer to the [postgres](http://github.com/docker-library/postgres) image documentation. + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```bash +$ helm install --name my-release \ + --set postgresUser=my-user,postgresPassword=secretpassword,postgresDatabase=my-database \ + stable/postgresql +``` + +The above command creates a PostgresSQL user named `root` with password `secretpassword`. Additionally it creates a database named `my-database`. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```bash +$ helm install --name my-release -f values.yaml stable/postgresql +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Persistence + +The [postgres](https://github.com/docker-library/postgres) image stores the PostgreSQL data and configurations at the `/var/lib/postgresql/data/pgdata` path of the container. + +The chart mounts a [Persistent Volume](http://kubernetes.io/docs/user-guide/persistent-volumes/) volume at this location. The volume is created using dynamic volume provisioning. If the PersistentVolumeClaim should not be managed by the chart, define `persistence.existingClaim`. + +### Existing PersistentVolumeClaims + +1. Create the PersistentVolume +1. Create the PersistentVolumeClaim +1. Install the chart +```bash +$ helm install --set persistence.existingClaim=PVC_NAME postgresql +``` + +The volume defaults to mount at a subdirectory of the volume instead of the volume root to avoid the volume's hidden directories from interfering with `initdb`. If you are upgrading this chart from before version `0.4.0`, set `persistence.subPath` to `""`. + +## Metrics +The chart optionally can start a metrics exporter for [prometheus](https://prometheus.io). The metrics endpoint (port 9187) is not exposed and it is expected that the metrics are collected from inside the k8s cluster using something similar as the described in the [example Prometheus scrape configuration](https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml). + +The exporter allows to create custom metrics from additional SQL queries. See the Chart's `values.yaml` for an example and consult the [exporters documentation](https://github.com/wrouesnel/postgres_exporter#adding-new-metrics-via-a-config-file) for more details. + +## NetworkPolicy + +To enable network policy for PostgreSQL, +install [a networking plugin that implements the Kubernetes +NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin), +and set `networkPolicy.enabled` to `true`. + +For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting +the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace: + + kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" + +With NetworkPolicy enabled, traffic will be limited to just port 5432. + +For more precise policy, set `networkPolicy.allowExternal=false`. This will +only allow pods with the generated client label to connect to PostgreSQL. +This label will be displayed in the output of a successful install. diff --git a/kubernetes/helm/dspace/charts/postgresql/templates/NOTES.txt b/kubernetes/helm/dspace/charts/postgresql/templates/NOTES.txt new file mode 100755 index 0000000000000000000000000000000000000000..cb511f38ab758e9dc61b651d07cd201da24ba0c1 --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/templates/NOTES.txt @@ -0,0 +1,35 @@ +PostgreSQL can be accessed via port 5432 on the following DNS name from within your cluster: +{{ template "postgresql.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local + +To get your user password run: + + PGPASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "postgresql.fullname" . }} -o jsonpath="{.data.postgres-password}" | base64 --decode; echo) + +To connect to your database run the following command (using the env variable from above): + + kubectl run {{ template "postgresql.fullname" . }}-client --rm --tty -i --image postgres \ + --env "PGPASSWORD=$PGPASSWORD" \{{- if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} + --labels="{{ template "postgresql.fullname" . }}-client=true" \{{- end }} + --command -- psql -U {{ default "postgres" .Values.postgresUser }} \ + -h {{ template "postgresql.fullname" . }} {{ default "postgres" .Values.postgresDatabase }} + +{{ if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} +Note: Since NetworkPolicy is enabled, only pods with label +{{ template "postgresql.fullname" . }}-client=true" +will be able to connect to this PostgreSQL cluster. +{{- end }} + +To connect to your database directly from outside the K8s cluster: + {{- if contains "NodePort" .Values.service.type }} + PGHOST=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath='{.items[0].status.addresses[0].address}') + PGPORT=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "postgresql.fullname" . }} -o jsonpath='{.spec.ports[0].nodePort}') + + {{- else if contains "ClusterIP" .Values.service.type }} + PGHOST=127.0.0.1 + PGPORT={{ default "5432" .Values.service.port }} + + # Execute the following commands to route the connection: + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "postgresql.fullname" . }}" -o jsonpath="{.items[0].metadata.name}") + kubectl port-forward $POD_NAME {{ default "5432" .Values.service.port }}:{{ default "5432" .Values.service.port }} + + {{- end }} diff --git a/kubernetes/helm/dspace/charts/postgresql/templates/_helpers.tpl b/kubernetes/helm/dspace/charts/postgresql/templates/_helpers.tpl new file mode 100755 index 0000000000000000000000000000000000000000..b6528392a2e3f564a4acf233707999eff8cc61dc --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/templates/_helpers.tpl @@ -0,0 +1,27 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "postgresql.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "postgresql.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "postgresql.networkPolicy.apiVersion" -}} +{{- if and (ge .Capabilities.KubeVersion.Minor "4") (le .Capabilities.KubeVersion.Minor "6") -}} +"extensions/v1beta1" +{{- else if ge .Capabilities.KubeVersion.Minor "7" -}} +"networking.k8s.io/v1" +{{- end -}} +{{- end -}} diff --git a/kubernetes/helm/dspace/charts/postgresql/templates/deployment.yaml b/kubernetes/helm/dspace/charts/postgresql/templates/deployment.yaml new file mode 100755 index 0000000000000000000000000000000000000000..743679ed1f1ba903d495f9de71d43c824b4cce88 --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/templates/deployment.yaml @@ -0,0 +1,114 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ template "postgresql.fullname" . }} + labels: + app: {{ template "postgresql.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +spec: + template: + metadata: + labels: + app: {{ template "postgresql.fullname" . }} + spec: + imagePullSecrets: + - name: gitlab-scientificnet-org-registry + containers: + - name: {{ template "postgresql.fullname" . }} + image: "{{ .Values.image }}:{{ .Values.imageTag }}" + imagePullPolicy: {{ default "" .Values.imagePullPolicy | quote }} + env: + - name: POSTGRES_USER + value: {{ default "postgres" .Values.postgresUser | quote }} + # Required for pg_isready in the health probes. + - name: PGUSER + value: {{ default "postgres" .Values.postgresUser | quote }} + - name: POSTGRES_DB + value: {{ default "" .Values.postgresDatabase | quote }} + - name: POSTGRES_INITDB_ARGS + value: {{ default "" .Values.postgresInitdbArgs | quote }} + - name: dspaceuser + value: {{ default "" .Values.global.dspace.user | quote }} + - name: dspacepassword + valueFrom: + secretKeyRef: + name: dspace-secrets + key: dspace.password + - name: dspacedbname + value: {{ default "" .Values.global.dspace.dbname | quote }} + - name: dspaceutilsdbname + value: {{ default "" .Values.global.dspaceutils.dbname | quote }} + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.fullname" . }} + key: postgres-password + - name: POD_IP + valueFrom: { fieldRef: { fieldPath: status.podIP } } + ports: + - name: postgresql + containerPort: 5432 + livenessProbe: + exec: + command: + - sh + - -c + - exec pg_isready --host $POD_IP + initialDelaySeconds: 60 + timeoutSeconds: 5 + failureThreshold: 6 + readinessProbe: + exec: + command: + - sh + - -c + - exec pg_isready --host $POD_IP + initialDelaySeconds: 5 + timeoutSeconds: 3 + periodSeconds: 5 + resources: +{{ toYaml .Values.resources | indent 10 }} + volumeMounts: + - name: data + mountPath: /var/lib/postgresql/data/pgdata + subPath: {{ .Values.persistence.subPath }} +{{- if .Values.metrics.enabled }} + - name: metrics + image: "{{ .Values.metrics.image }}:{{ .Values.metrics.imageTag }}" + imagePullPolicy: {{ default "" .Values.metrics.imagePullPolicy | quote }} + env: + - name: DATA_SOURCE_NAME + value: postgresql://postgres@127.0.0.1:5432?sslmode=disable + ports: + - name: metrics + containerPort: 9187 + {{- if .Values.metrics.customMetrics }} + args: ["-extend.query-path", "/conf/custom-metrics.yaml"] + volumeMounts: + - name: custom-metrics + mountPath: /conf + readOnly: true + {{- end }} + resources: +{{ toYaml .Values.metrics.resources | indent 10 }} +{{- end }} + volumes: + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim | default (include "postgresql.fullname" .) }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.customMetrics }} + - name: custom-metrics + secret: + secretName: {{ template "postgresql.fullname" . }} + items: + - key: custom-metrics.yaml + path: custom-metrics.yaml + {{- end }} diff --git a/kubernetes/helm/dspace/charts/postgresql/templates/networkpolicy.yaml b/kubernetes/helm/dspace/charts/postgresql/templates/networkpolicy.yaml new file mode 100755 index 0000000000000000000000000000000000000000..92f3b71b15c2e0b27c7bbe336288e2f82245b92f --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/templates/networkpolicy.yaml @@ -0,0 +1,28 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ template "postgresql.networkPolicy.apiVersion" . }} +metadata: + name: "{{ template "postgresql.fullname" . }}" + labels: + app: {{ template "postgresql.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +spec: + podSelector: + matchLabels: + app: "{{ template "postgresql.fullname" . }}" + ingress: + # Allow inbound connections + - ports: + - port: 5432 + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "postgresql.fullname" . }}-client: "true" + {{- end }} + # Allow prometheus scrapes + - ports: + - port: 9187 +{{- end }} diff --git a/kubernetes/helm/dspace/charts/postgresql/templates/pvc.yaml b/kubernetes/helm/dspace/charts/postgresql/templates/pvc.yaml new file mode 100755 index 0000000000000000000000000000000000000000..12d8dc22ed1e6e26eb0f5282ade2f11b4f7879a7 --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/templates/pvc.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) -}} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ template "postgresql.fullname" . }} + labels: + app: {{ template "postgresql.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + annotations: + {{- if .Values.persistence.storageClass }} + volume.beta.kubernetes.io/storage-class: {{ .Values.persistence.storageClass | quote }} + {{- else }} + volume.alpha.kubernetes.io/storage-class: default + {{- end }} +spec: + accessModes: + - {{ .Values.persistence.accessMode | quote }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- end -}} diff --git a/kubernetes/helm/dspace/charts/postgresql/templates/secrets.yaml b/kubernetes/helm/dspace/charts/postgresql/templates/secrets.yaml new file mode 100755 index 0000000000000000000000000000000000000000..6dd4c7da5517e1d491a924a75cf2f44de86bd427 --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/templates/secrets.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "postgresql.fullname" . }} + labels: + app: {{ template "postgresql.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +data: + {{ if .Values.postgresPassword }} + postgres-password: {{ .Values.postgresPassword | b64enc | quote }} + {{ else }} + postgres-password: {{ randAlphaNum 10 | b64enc | quote }} + {{ end }} + {{- if .Values.metrics.customMetrics }} + custom-metrics.yaml: {{ toYaml .Values.metrics.customMetrics | b64enc | quote }} + {{- end }} diff --git a/kubernetes/helm/dspace/charts/postgresql/templates/svc.yaml b/kubernetes/helm/dspace/charts/postgresql/templates/svc.yaml new file mode 100755 index 0000000000000000000000000000000000000000..6446db534049cbb453c2ffa4b8aee6f331f5ca9a --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/templates/svc.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "postgresql.fullname" . }} + labels: + app: {{ template "postgresql.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +{{- if .Values.metrics.enabled }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9187" +{{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - name: postgresql + port: {{ .Values.service.port }} + targetPort: postgresql +{{- if .Values.service.externalIPs }} + externalIPs: +{{ toYaml .Values.service.externalIPs | indent 4 }} +{{- end }} + selector: + app: {{ template "postgresql.fullname" . }} \ No newline at end of file diff --git a/kubernetes/helm/dspace/charts/postgresql/values.yaml b/kubernetes/helm/dspace/charts/postgresql/values.yaml new file mode 100755 index 0000000000000000000000000000000000000000..c092fe471b422fc8f7c1588d0deea853afaf7eee --- /dev/null +++ b/kubernetes/helm/dspace/charts/postgresql/values.yaml @@ -0,0 +1,91 @@ +## postgres image repository +image: "postgres" +## postgres image version +## ref: https://hub.docker.com/r/library/postgres/tags/ +## +imageTag: "9.6.2" + +## Specify a imagePullPolicy +## 'Always' if imageTag is 'latest', else set to 'IfNotPresent' +## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images +## +# imagePullPolicy: + +## Create a database user +## Default: postgres +# postgresUser: +## Default: random 10 character string +# postgresPassword: + +## Create a database +## Default: the postgres user +# postgresDatabase: + +## Specify initdb arguments, e.g. --data-checksums +## ref: https://github.com/docker-library/docs/blob/master/postgres/content.md#postgres_initdb_args +## ref: https://www.postgresql.org/docs/current/static/app-initdb.html +# postgresInitdbArgs: + +## Persist data to a persitent volume +persistence: + enabled: true + + ## A manually managed Persistent Volume and Claim + ## Requires persistence.enabled: true + ## If defined, PVC must be created manually before volume will be bound + # existingClaim: + + ## If defined, volume.beta.kubernetes.io/storage-class: <storageClass> + ## Default: volume.alpha.kubernetes.io/storage-class: default + ## + # storageClass: + accessMode: ReadWriteOnce + size: 8Gi + subPath: "postgresql-db" + +metrics: + enabled: false + image: wrouesnel/postgres_exporter + imageTag: v0.1.1 + imagePullPolicy: IfNotPresent + resources: + requests: + memory: 256Mi + cpu: 100m + ## Define additional custom metrics + ## ref: https://github.com/wrouesnel/postgres_exporter#adding-new-metrics-via-a-config-file + #customMetrics: + # pg_database: + # query: "SELECT d.datname AS name, CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT') THEN pg_catalog.pg_database_size(d.datname) ELSE 0 END AS size FROM pg_catalog.pg_database d where datname not in ('template0', 'template1', 'postgres')" + # metrics: + # - name: + # usage: "LABEL" + # description: "Name of the database" + # - size_bytes: + # usage: "GAUGE" + # description: "Size of the database in bytes" + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + requests: + memory: 256Mi + cpu: 100m + +service: + type: ClusterIP + port: 5432 + externalIPs: [] + +networkPolicy: + ## Enable creation of NetworkPolicy resources. + ## + enabled: false + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port PostgreSQL is listening + ## on. When true, PostgreSQL will accept connections from any source + ## (with the correct destination port). + ## + allowExternal: true diff --git a/kubernetes/helm/dspace/dspace.png b/kubernetes/helm/dspace/dspace.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f02a4527bc702db500509184db473791bbbfb6 Binary files /dev/null and b/kubernetes/helm/dspace/dspace.png differ diff --git a/kubernetes/helm/dspace/requirements.yaml b/kubernetes/helm/dspace/requirements.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4f6af4a01bbb182bf6d3f6e11d43bb9312f8bf5a --- /dev/null +++ b/kubernetes/helm/dspace/requirements.yaml @@ -0,0 +1,4 @@ +dependencies: +- name: postgresql + version: 0.8.1 + repository: https://kubernetes-charts.storage.googleapis.com/ diff --git a/kubernetes/helm/dspace/templates/apache-certs.yaml b/kubernetes/helm/dspace/templates/apache-certs.yaml new file mode 100644 index 0000000000000000000000000000000000000000..24bf3324074c9f67b415545369bb649cc6480388 --- /dev/null +++ b/kubernetes/helm/dspace/templates/apache-certs.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +metadata: + creationTimestamp: null + labels: + io.kompose.service: dspace + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" + name: apache-lindat-keys +data: + lindat-dev.eurac.edu.cert: {{ .Values.webserver.cert }} + lindat-dev.eurac.edu.key: {{ .Values.webserver.key }} +kind: Secret +type: Opaque \ No newline at end of file diff --git a/kubernetes/helm/dspace/templates/client.fs.services.kubernetes.eurac.commul.dspace.yaml b/kubernetes/helm/dspace/templates/client.fs.services.kubernetes.eurac.commul.dspace.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b8b134cfa1bbde716ada4a66929a7863f4b267ed --- /dev/null +++ b/kubernetes/helm/dspace/templates/client.fs.services.kubernetes.eurac.commul.dspace.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: client.fs.services.kubernetes.eurac.commul.dspace +type: Opaque +data: + key: {{ .Values.cephfs.key }} diff --git a/kubernetes/helm/dspace/templates/dockerconfigjson.yaml b/kubernetes/helm/dspace/templates/dockerconfigjson.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f8f91038190ef57fd4502dcc5c53397eb57cc739 --- /dev/null +++ b/kubernetes/helm/dspace/templates/dockerconfigjson.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +data: + .dockerconfigjson: {{ .Values.dockerconfig.json | quote }} +kind: Secret +metadata: + creationTimestamp: 2017-06-23T13:05:27Z + name: gitlab-scientificnet-org-registry +type: kubernetes.io/dockerconfigjson + \ No newline at end of file diff --git a/kubernetes/helm/dspace/templates/dspace-deployment.yaml b/kubernetes/helm/dspace/templates/dspace-deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..47ab197c6a0ca80a3ab8c1b3a9d16117c1f08daf --- /dev/null +++ b/kubernetes/helm/dspace/templates/dspace-deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.1.0 () + creationTimestamp: null + labels: + io.kompose.service: dspace + name: dspace +spec: + replicas: 1 + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + io.kompose.service: dspace + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: ["sleep","360000"] + # command: ["/etc/init.d/tomcat8","start"] + env: + - name: dspaceuser + value: {{ default "" .Values.global.dspace.user | quote }} + - name: dspacepassword + valueFrom: + secretKeyRef: + name: dspace-secrets + key: dspace.password + - name: dspacedbname + value: {{ default "" .Values.global.dspace.dbname | quote }} + - name: dspaceutilsdbname + value: {{ default "" .Values.global.dspaceutils.dbname | quote }} + ports: + - containerPort: 8080 + resources: {} + volumeMounts: + - name: cephfs-repository + mountPath: /opt/repository + - name: cephfs-lindat + mountPath: /opt/lindat-dspace + imagePullSecrets: + - name: gitlab-scientificnet-org-registry + volumes: + - name: cephfs-repository + cephfs: + monitors: + - 10.8.55.201:6789 + - 10.8.55.202:6789 + - 10.8.55.203:6789 + - 10.7.55.201:6789 + - 10.7.55.202:6789 + path: /services/kubernetes/eurac/commul/dspace/dspace/repository + user: fs.services.kubernetes.eurac.commul.dspace + secretRef: + name: client.fs.services.kubernetes.eurac.commul.dspace + readOnly: false + - name: cephfs-lindat + cephfs: + monitors: + - 10.8.55.201:6789 + - 10.8.55.202:6789 + - 10.8.55.203:6789 + - 10.7.55.201:6789 + - 10.7.55.202:6789 + path: /services/kubernetes/eurac/commul/dspace/dspace/lindat-common + user: fs.services.kubernetes.eurac.commul.dspace + secretRef: + name: client.fs.services.kubernetes.eurac.commul.dspace + readOnly: false + + restartPolicy: Always +status: {} \ No newline at end of file diff --git a/kubernetes/helm/dspace/templates/dspace-service.yaml b/kubernetes/helm/dspace/templates/dspace-service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c919cf3a0600acd3aa8f068ae2c5afcf32ab9bd3 --- /dev/null +++ b/kubernetes/helm/dspace/templates/dspace-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.1.0 () + creationTimestamp: null + labels: + io.kompose.service: dspace + name: dspace +spec: + ports: + - name: "8080" + port: 8080 + targetPort: 8080 + selector: + io.kompose.service: dspace +status: + loadBalancer: {} diff --git a/kubernetes/helm/dspace/templates/postgres-service.yaml b/kubernetes/helm/dspace/templates/postgres-service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1aa7bed3aeb0ce9781cf979e98a1ad940459b94b --- /dev/null +++ b/kubernetes/helm/dspace/templates/postgres-service.yaml @@ -0,0 +1,20 @@ +papiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.1.0 () + creationTimestamp: null + labels: + io.kompose.service: postgres + name: postgres +spec: + clusterIP: None + ports: + - name: headless + port: 55555 + targetPort: 0 + selector: + io.kompose.service: postgres +status: + loadBalancer: {} diff --git a/kubernetes/helm/dspace/templates/rbd.yaml b/kubernetes/helm/dspace/templates/rbd.yaml new file mode 100644 index 0000000000000000000000000000000000000000..217ed301a9d9ab8d348e28aba314386a0729a8a2 --- /dev/null +++ b/kubernetes/helm/dspace/templates/rbd.yaml @@ -0,0 +1,8 @@ +kind: Secret +apiVersion: v1 +metadata: + name: ceph-rbd-secret +data: + key: {{ .Values.rbd.secretToken | b64enc | quote }} +type: kubernetes.io/rbd + diff --git a/kubernetes/helm/dspace/templates/secrets.yaml b/kubernetes/helm/dspace/templates/secrets.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f5a62ccbc5ab22629934df018ffc674c3231443e --- /dev/null +++ b/kubernetes/helm/dspace/templates/secrets.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: dspace-secrets +type: Opaque +data: + dspace.password: {{ .Values.global.dspace.password | b64enc | quote }} diff --git a/kubernetes/helm/dspace/templates/webserver-deployment.yaml b/kubernetes/helm/dspace/templates/webserver-deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c812f6602dc20b73f8db46899ff5d86639a29f6d --- /dev/null +++ b/kubernetes/helm/dspace/templates/webserver-deployment.yaml @@ -0,0 +1,44 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert -f docker-compose.yml + kompose.version: 1.1.0 () + creationTimestamp: null + labels: + io.kompose.service: dspace + name: apache +spec: + replicas: 1 + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + io.kompose.service: apache + spec: + containers: + - image: "{{ .Values.webserver.repository }}:{{ .Values.webserver.tag }}" + name: apache + command: ["sleep","3600"] + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: SHIB_HOSTNAME + value: "{{ .Values.webserver.shib_hostname }}" + ports: + - containerPort: 80 + - containerPort: 443 + resources: {} + volumeMounts: + - name: apache-certs + mountPath: "/etc/httpd/certs/" + readOnly: true + imagePullSecrets: + - name: gitlab-scientificnet-org-registry + volumes: + - name: apache-certs + secret: + secretName: "apache-lindat-keys" + restartPolicy: Always +status: {} + \ No newline at end of file diff --git a/kubernetes/helm/dspace/values.yaml b/kubernetes/helm/dspace/values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f1b62b8adc11bda71e76a6f1c967de7fbf562c05 --- /dev/null +++ b/kubernetes/helm/dspace/values.yaml @@ -0,0 +1,51 @@ +# Default values for dspace. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +postgres: + password: "xxx" + repository: nginx + tag: stable + pullPolicy: IfNotPresent + +replicaCount: 1 +image: + repository: gitlab.scientificnet.org:4567/kube/kubernetes/dspace/dspace + tag: 0.2 + pullPolicy: IfNotPresent +service: + name: nginx + type: ClusterIP + externalPort: 80 + internalPort: 80 +ingress: + enabled: false + # Used to create Ingress record (should used with service.type: ClusterIP). + hosts: + - chart-example.local + annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + tls: + # Secrets must be manually created in the namespace. + # - secretName: chart-example-tls + # hosts: + # - chart-example.local +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + #requests: + # cpu: 100m + # memory: 128Mi +dspace: + dbname : + password : + user : +dspaceutils: + dbname : +cephfs: + key: "" \ No newline at end of file diff --git a/kubernetes/yaml/.gitignore b/kubernetes/yaml/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e3012077f8cc86fa454eeacd6627c3c146c87431 --- /dev/null +++ b/kubernetes/yaml/.gitignore @@ -0,0 +1,2 @@ +gitlab-scientificnet-org-registry.yaml +postgresql-secret.yaml diff --git a/kubernetes/yaml/postgresql-pvc.yaml b/kubernetes/yaml/postgresql-pvc.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5c25b8470d9514df4860d724a2e9a6a6a285a730 --- /dev/null +++ b/kubernetes/yaml/postgresql-pvc.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + annotations: + volume.alpha.kubernetes.io/storage-class: default + volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/rbd + labels: + app: dspace-postgresql + chart: postgresql-0.8.1 + heritage: Tiller + release: dspace + name: dspace-postgresql +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + storageClassName: auto diff --git a/kubernetes/yaml/postgresql-service.yaml b/kubernetes/yaml/postgresql-service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..be097b4de6c1ba72a6360fb721bf9d76d3543520 --- /dev/null +++ b/kubernetes/yaml/postgresql-service.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: dspace-postgresql + chart: postgresql-0.8.1 + heritage: Tiller + release: dspace + name: dspace-postgresql + namespace: dspace +spec: + ports: + - name: postgresql + port: 5432 + protocol: TCP + targetPort: postgresql + selector: + app: dspace-postgresql + sessionAffinity: None + type: ClusterIP +status: + loadBalancer: {} diff --git a/kubernetes/yaml/postgresql.yaml b/kubernetes/yaml/postgresql.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ea50a1048d90e5454147e85963e0a31141ad7eb8 --- /dev/null +++ b/kubernetes/yaml/postgresql.yaml @@ -0,0 +1,106 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + generation: 1 + labels: + app: dspace-postgresql + chart: postgresql-0.8.1 + heritage: Tiller + release: dspace + name: dspace-postgresql +spec: + replicas: 1 + selector: + matchLabels: + app: dspace-postgresql + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + type: RollingUpdate + template: + metadata: + labels: + app: dspace-postgresql + spec: + containers: + - env: + - name: POSTGRES_USER + value: dspace + - name: PGUSER + value: dspace + - name: POSTGRES_DB + - name: POSTGRES_INITDB_ARGS + - name: dspaceuser + value: dbuser + - name: dspacepassword + valueFrom: + secretKeyRef: + key: dspace.password + name: dspace-secrets + - name: dspacedbname + value: dbname + - name: dspaceutilsdbname + value: dbnamespace + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + key: postgres-password + name: dspace-postgresql + - name: POD_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP + image: gitlab.scientificnet.org:4567/commul/dspace/postgres:0.3 + imagePullPolicy: IfNotPresent + livenessProbe: + exec: + command: + - sh + - -c + - exec pg_isready --host $POD_IP + failureThreshold: 6 + initialDelaySeconds: 60 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + name: dspace-postgresql + ports: + - containerPort: 5432 + name: postgresql + protocol: TCP + readinessProbe: + exec: + command: + - sh + - -c + - exec pg_isready --host $POD_IP + failureThreshold: 3 + initialDelaySeconds: 5 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 3 + resources: + requests: + cpu: 100m + memory: 256Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /var/lib/postgresql/data/pgdata + name: data + subPath: postgresql-db + dnsPolicy: ClusterFirst + imagePullSecrets: + - name: gitlab-scientificnet-org-registry + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + volumes: + - name: data + persistentVolumeClaim: + claimName: dspace-postgresql diff --git a/kubernetes/yaml/servlet.yaml b/kubernetes/yaml/servlet.yaml new file mode 100644 index 0000000000000000000000000000000000000000..33d0cd6cc3af54d2a8ed9739bc34ab33887583e6 --- /dev/null +++ b/kubernetes/yaml/servlet.yaml @@ -0,0 +1,67 @@ +apiVersion: v1 +kind: Pod +metadata: + name: servlet +spec: + containers: + - args: + - --debug + env: + - name: dspaceuser + value: dbuser + - name: dspacepassword + valueFrom: + secretKeyRef: + key: dspace.password + name: dspace-secrets + - name: dspacedbname + value: dbname + - name: dspaceutilsdbname + value: dbnamespace + image: gitlab.scientificnet.org:4567/commul/dspace/dspace:0.3 + imagePullPolicy: Always + name: servlet + ports: + - containerPort: 8080 + protocol: TCP + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /opt/lindat-dspace + name: cephfs-lindat + - mountPath: /opt/repository + name: cephfs-repository + dnsPolicy: ClusterFirst + imagePullSecrets: + - name: gitlab-scientificnet-org-registry + restartPolicy: Always + volumes: + - cephfs: + monitors: + - 10.8.55.201:6789 + - 10.8.55.202:6789 + - 10.8.55.203:6789 + - 10.7.55.201:6789 + - 10.7.55.202:6789 + path: /services/kubernetes/eurac/commul/dspace-dev/lindat-common + secretRef: + name: client.fs.services.kubernetes.eurac.commul.dspace-dev + user: fs.services.kubernetes.eurac.commul.dspace-dev + name: cephfs-lindat + - cephfs: + monitors: + - 10.8.55.201:6789 + - 10.8.55.202:6789 + - 10.8.55.203:6789 + - 10.7.55.201:6789 + - 10.7.55.202:6789 + path: /services/kubernetes/eurac/commul/dspace-dev/repository + secretRef: + name: client.fs.services.kubernetes.eurac.commul.dspace-dev + user: fs.services.kubernetes.eurac.commul.dspace-dev + name: cephfs-repository + - name: default-token-c1pd4 + secret: + defaultMode: 420 + secretName: default-token-c1pd4 diff --git a/pgadmin_1.png b/pgadmin_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f2831d73ebc90b4f1aa186af2087b6a7f4b7f173 Binary files /dev/null and b/pgadmin_1.png differ diff --git a/pgadmin_2.png b/pgadmin_2.png new file mode 100644 index 0000000000000000000000000000000000000000..68a56c1a10d341319e271ae69765adf36aa110be Binary files /dev/null and b/pgadmin_2.png differ