From 5c873ac7c5676645ea1fc60fb0572e9a7bf922f9 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Thu, 14 May 2020 09:48:39 +0100 Subject: [PATCH 01/10] First pass of Islandora 8 under Docker. --- .gitattributes | 20 + .gitignore | 5 + .vscode/settings.json | 14 + README.md | 11 + abuild/.dockerignore | 1 + abuild/Dockerfile | 12 + abuild/README.md | 76 + activemq/.dockerignore | 1 + activemq/Dockerfile | 20 + activemq/README.md | 94 + .../confd/conf.d/credentials.properties.toml | 7 + .../etc/confd/conf.d/groups.properties.toml | 7 + .../confd/conf.d/jetty-realm.properties.toml | 7 + .../etc/confd/conf.d/users.properties.toml | 7 + activemq/rootfs/etc/confd/confd.toml | 6 + .../templates/credentials.properties.tmpl | 3 + .../confd/templates/groups.properties.tmpl | 4 + .../templates/jetty-realm.properties.tmpl | 5 + .../etc/confd/templates/users.properties.tmpl | 2 + .../rootfs/etc/services.d/activemq/finish | 4 + activemq/rootfs/etc/services.d/activemq/run | 6 + alpaca/.dockerignore | 1 + alpaca/Dockerfile | 41 + alpaca/README.md | 79 + ...a.alpaca.connector.fits.blueprint.xml.toml | 7 + ...lpaca.connector.homarus.blueprint.xml.toml | 7 + ...lpaca.connector.houdini.blueprint.xml.toml | 7 + ...ra.alpaca.connector.ocr.blueprint.xml.toml | 7 + .../ca.islandora.alpaca.http.client.cfg.toml | 7 + ....islandora.alpaca.indexing.fcrepo.cfg.toml | 7 + ...ndora.alpaca.indexing.triplestore.cfg.toml | 7 + ...fcrepo.camel.indexing.triplestore.cfg.toml | 7 + ...org.fcrepo.camel.service.activemq.cfg.toml | 7 + .../conf.d/org.fcrepo.camel.service.cfg.toml | 7 + .../conf.d/org.ops4j.pax.logging.cfg.toml | 7 + alpaca/rootfs/etc/confd/confd.toml | 6 + ...a.alpaca.connector.fits.blueprint.xml.tmpl | 28 + ...lpaca.connector.homarus.blueprint.xml.tmpl | 28 + ...lpaca.connector.houdini.blueprint.xml.tmpl | 28 + ...ra.alpaca.connector.ocr.blueprint.xml.tmpl | 28 + .../ca.islandora.alpaca.http.client.cfg.tmpl | 1 + ....islandora.alpaca.indexing.fcrepo.cfg.tmpl | 8 + ...ndora.alpaca.indexing.triplestore.cfg.tmpl | 4 + ...fcrepo.camel.indexing.triplestore.cfg.tmpl | 3 + ...org.fcrepo.camel.service.activemq.cfg.tmpl | 6 + .../org.fcrepo.camel.service.cfg.tmpl | 7 + .../templates/org.ops4j.pax.logging.cfg.tmpl | 34 + base/.dockerignore | 1 + base/Dockerfile | 45 + base/README.md | 31 + .../rootfs/etc/cleanup.d/empty-ash-history.sh | 2 + .../etc/cleanup.d/empty-bash-history.sh | 2 + base/rootfs/etc/cleanup.d/empty-tmp.sh | 2 + base/rootfs/etc/confd/conf.d/.gitignore | 2 + base/rootfs/etc/confd/confd.toml | 5 + base/rootfs/etc/confd/templates/.gitignore | 2 + base/rootfs/etc/cont-finish.d/.gitignore | 2 + .../cont-init.d/01-confd-render-templates.sh | 17 + base/rootfs/etc/fix-attrs.d/.gitignore | 2 + base/rootfs/etc/services.d/.gitignore | 2 + base/rootfs/opt/.gitignore | 2 + base/rootfs/run/islandora/.gitignore | 2 + base/rootfs/sbin/apk-install.sh | 2 + base/rootfs/sbin/apk-uninstall.sh | 2 + base/rootfs/usr/local/bin/cleanup.sh | 65 + base/rootfs/usr/local/bin/git-clone-cached.sh | 104 + base/rootfs/usr/local/bin/wait-for-mysql.sh | 114 + .../usr/local/bin/wait-for-open-port.sh | 80 + blazegraph/.dockerignore | 1 + blazegraph/Dockerfile | 17 + blazegraph/README.md | 37 + .../etc/confd/conf.d/RWStore.properties.toml | 6 + .../etc/confd/conf.d/log4j.properties.toml | 6 + blazegraph/rootfs/etc/confd/confd.toml | 6 + .../confd/templates/RWStore.properties.tmpl | 49 + .../etc/confd/templates/log4j.properties.tmpl | 110 + build.gradle.kts | 117 + cantaloupe/.dockerignore | 1 + cantaloupe/Dockerfile | 31 + cantaloupe/README.md | 205 ++ .../confd/conf.d/cataloupe.properties.toml | 7 + .../rootfs/etc/confd/conf.d/setenv.sh.toml | 6 + cantaloupe/rootfs/etc/confd/confd.toml | 6 + .../templates/cantaloupe.properties.tmpl | 311 +++ .../rootfs/etc/confd/templates/setenv.sh.tmpl | 4 + commands/continuous.sh | 129 ++ commands/drush.sh | 4 + commands/etcdctl.sh | 4 + commands/mysql.sh | 4 + commands/open-in-browser.sh | 123 ++ commands/shell.sh | 4 + composer/.dockerignore | 1 + composer/Dockerfile | 24 + composer/README.md | 6 + crayfish/.dockerignore | 1 + crayfish/Dockerfile | 20 + crayfish/README.md | 31 + .../rootfs/etc/confd/conf.d/default.conf.toml | 7 + .../etc/confd/conf.d/syn-settings.xml.toml | 7 + .../etc/confd/templates/default.conf.tmpl | 29 + .../etc/confd/templates/syn-settings.xml.tmpl | 7 + crayfits/.dockerignore | 1 + crayfits/Dockerfile | 22 + crayfits/README.md | 31 + .../rootfs/etc/confd/conf.d/.env.local.toml | 7 + .../rootfs/etc/confd/conf.d/default.conf.toml | 7 + crayfits/rootfs/etc/confd/confd.toml | 6 + .../etc/confd/templates/.env.local.tmpl | 1 + .../etc/confd/templates/default.conf.tmpl | 29 + docker-compose.yml | 213 ++ drupal/.dockerignore | 1 + drupal/Dockerfile | 10 + drupal/README.md | 60 + .../conf.d/create-drupal-databases.sh.toml | 7 + .../conf.d/create-drupal-databases.sql.toml | 7 + .../confd/conf.d/drupal-install-sites.sh.toml | 7 + drupal/rootfs/etc/confd/confd.toml | 6 + .../templates/create-drupal-databases.sh.tmpl | 33 + .../create-drupal-databases.sql.tmpl | 13 + .../templates/drupal-install-sites.sh.tmpl | 101 + .../rootfs/etc/cont-init.d/03-drupal-setup.sh | 4 + drupal/rootfs/etc/nginx/conf.d/default.conf | 121 + fcrepo/.dockerignore | 1 + fcrepo/Dockerfile | 23 + fcrepo/README.md | 56 + .../rootfs/etc/confd/conf.d/activemq.xml.toml | 7 + .../conf.d/allowed-external-content.txt.toml | 7 + fcrepo/rootfs/etc/confd/conf.d/claw.cnd.toml | 6 + .../rootfs/etc/confd/conf.d/context.xml.toml | 6 + .../etc/confd/conf.d/fcrepo-config.xml.toml | 7 + .../etc/confd/conf.d/repository.json.toml | 7 + fcrepo/rootfs/etc/confd/conf.d/setenv.sh.toml | 7 + .../etc/confd/conf.d/syn-settings.xml.toml | 7 + fcrepo/rootfs/etc/confd/confd.toml | 6 + .../etc/confd/templates/activemq.xml.tmpl | 28 + .../allowed-external-content.txt.tmpl | 3 + .../rootfs/etc/confd/templates/claw.cnd.tmpl | 34 + .../etc/confd/templates/context.xml.tmpl | 8 + .../confd/templates/fcrepo-config.xml.tmpl | 258 +++ .../etc/confd/templates/repository.json.tmpl | 66 + .../rootfs/etc/confd/templates/setenv.sh.tmpl | 6 + .../etc/confd/templates/syn-settings.xml.tmpl | 6 + .../rootfs/etc/cont-init.d/03-fcrepo-setup.sh | 5 + fits/.dockerignore | 1 + fits/Dockerfile | 17 + fits/README.md | 36 + .../etc/confd/conf.d/catalina.properties.toml | 6 + .../confd/conf.d/fits-service.properties.toml | 7 + fits/rootfs/etc/confd/confd.toml | 6 + .../confd/templates/catalina.properties.tmpl | 147 ++ .../templates/fits-service.properties.tmpl | 8 + gemini/.dockerignore | 1 + gemini/Dockerfile | 11 + gemini/README.md | 32 + .../rootfs/etc/confd/conf.d/config.yaml.toml | 7 + .../conf.d/create-gemini-database.sh.toml | 7 + .../conf.d/create-gemini-database.sql.toml | 7 + gemini/rootfs/etc/confd/confd.toml | 6 + .../etc/confd/templates/config.yaml.tmpl | 24 + .../templates/create-gemini-database.sh.tmpl | 33 + .../templates/create-gemini-database.sql.tmpl | 13 + .../rootfs/etc/cont-init.d/03-gemini-setup.sh | 4 + gradle.properties | 9 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 58694 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 183 ++ gradlew.bat | 103 + homarus/.dockerignore | 1 + homarus/Dockerfile | 20 + homarus/README.md | 23 + .../rootfs/etc/confd/conf.d/config.yaml.toml | 7 + homarus/rootfs/etc/confd/confd.toml | 6 + .../etc/confd/templates/config.yaml.tmpl | 41 + houdini/.dockerignore | 1 + houdini/Dockerfile | 21 + houdini/README.md | 23 + .../rootfs/etc/confd/conf.d/monolog.yaml.toml | 7 + houdini/rootfs/etc/confd/confd.toml | 6 + .../etc/confd/templates/monolog.yaml.tmpl | 7 + .../config/packages/crayfish_commons.yaml | 3 + .../www/crayfish/Houdini/config/services.yaml | 33 + hypercube/.dockerignore | 1 + hypercube/Dockerfile | 25 + hypercube/README.md | 24 + .../rootfs/etc/confd/conf.d/config.yaml.toml | 7 + hypercube/rootfs/etc/confd/confd.toml | 6 + .../etc/confd/templates/config.yaml.tmpl | 20 + imagemagick/.dockerignore | 1 + imagemagick/Dockerfile | 45 + imagemagick/README.md | 25 + imagemagick/build/APKBUILD | 200 ++ .../build/disable-avaraging-tests.patch | 26 + java/.dockerignore | 1 + java/Dockerfile | 15 + java/README.md | 15 + .../usr/local/bin/install-apache-service.sh | 127 ++ karaf/.dockerignore | 1 + karaf/Dockerfile | 25 + karaf/README.md | 72 + .../etc/confd/conf.d/users.properties.toml | 7 + .../etc/confd/templates/users.properties.tmpl | 18 + .../etc/cont-init.d/04-karaf-startup.sh | 4 + karaf/rootfs/etc/services.d/karaf/finish | 4 + karaf/rootfs/etc/services.d/karaf/run | 6 + mariadb/.dockerignore | 1 + mariadb/Dockerfile | 19 + mariadb/README.md | 46 + .../etc/confd/conf.d/mariadb-server.cnf.toml | 5 + mariadb/rootfs/etc/confd/conf.d/my.cnf.toml | 5 + .../conf.d/set-root-user-password.sql.toml | 7 + mariadb/rootfs/etc/confd/confd.toml | 6 + .../confd/templates/mariadb-server.cnf.tmpl | 42 + .../rootfs/etc/confd/templates/my.cnf.tmpl | 12 + .../templates/set-root-user-password.sql.tmpl | 5 + .../rootfs/etc/cont-init.d/03-mysql-setup.sh | 32 + mariadb/rootfs/etc/services.d/mysqld/finish | 4 + mariadb/rootfs/etc/services.d/mysqld/run | 5 + matomo/.dockerignore | 1 + matomo/Dockerfile | 21 + matomo/README.md | 31 + .../conf.d/create-matomo-database.sh.toml | 7 + .../conf.d/create-matomo-database.sql.toml | 7 + .../rootfs/etc/confd/conf.d/default.conf.toml | 7 + matomo/rootfs/etc/confd/confd.toml | 6 + .../templates/create-matomo-database.sh.tmpl | 33 + .../templates/create-matomo-database.sql.tmpl | 7 + .../etc/confd/templates/default.conf.tmpl | 64 + .../rootfs/etc/cont-init.d/03-matomo-setup.sh | 3 + milliner/.dockerignore | 1 + milliner/Dockerfile | 13 + milliner/README.md | 26 + .../rootfs/etc/confd/conf.d/config.yaml.toml | 7 + milliner/rootfs/etc/confd/confd.toml | 6 + .../etc/confd/templates/config.yaml.tmpl | 28 + nginx/.dockerignore | 1 + nginx/Dockerfile | 26 + nginx/README.md | 57 + nginx/rootfs/etc/confd/conf.d/nginx.conf.toml | 7 + .../rootfs/etc/confd/conf.d/php-fpm.conf.toml | 7 + nginx/rootfs/etc/confd/conf.d/php.ini.toml | 7 + nginx/rootfs/etc/confd/conf.d/www.conf.toml | 7 + .../etc/confd/templates/nginx.conf.tmpl | 85 + .../etc/confd/templates/php-fpm.conf.tmpl | 136 ++ nginx/rootfs/etc/confd/templates/php.ini.tmpl | 1939 +++++++++++++++++ .../rootfs/etc/confd/templates/www.conf.tmpl | 438 ++++ .../rootfs/etc/cont-init.d/02-fpm-install.sh | 2 + .../etc/cont-init.d/02-ngnix-install.sh | 2 + nginx/rootfs/etc/nginx/modules/daemon.conf | 1 + nginx/rootfs/etc/services.d/fpm/finish | 4 + nginx/rootfs/etc/services.d/fpm/run | 4 + nginx/rootfs/etc/services.d/nginx/finish | 4 + nginx/rootfs/etc/services.d/nginx/run | 4 + recast/.dockerignore | 1 + recast/Dockerfile | 13 + recast/README.md | 26 + .../rootfs/etc/confd/conf.d/config.yaml.toml | 7 + recast/rootfs/etc/confd/confd.toml | 6 + .../etc/confd/templates/config.yaml.tmpl | 37 + sandbox/.dockerignore | 1 + sandbox/Dockerfile | 52 + sandbox/README.md | 11 + .../confd/conf.d/blazegraph.properties.toml | 7 + .../rootfs/etc/confd/conf.d/inference.nt.toml | 7 + .../etc/confd/conf.d/sandbox-setup.sh.toml | 7 + sandbox/rootfs/etc/confd/confd.toml | 6 + .../templates/blazegraph.properties.tmpl | 12 + .../etc/confd/templates/inference.nt.tmpl | 2 + .../etc/confd/templates/sandbox-setup.sh.tmpl | 178 ++ .../etc/cont-init.d/04-sandbox-setup.sh | 3 + .../opt/islandora/configs/jwt/jwt.config.yml | 2 + .../configs/jwt/key.key.islandora_rsa_key.yml | 17 + settings.gradle.kts | 11 + solr/.dockerignore | 1 + solr/Dockerfile | 20 + solr/README.md | 40 + solr/rootfs/etc/services.d/solr/finish | 4 + solr/rootfs/etc/services.d/solr/run | 5 + tomcat/.dockerignore | 1 + tomcat/Dockerfile | 24 + tomcat/README.md | 75 + .../etc/confd/conf.d/manager.context.toml | 6 + tomcat/rootfs/etc/confd/conf.d/setenv.sh.toml | 6 + .../rootfs/etc/confd/conf.d/tomcat-users.toml | 6 + .../confd/templates/manager.context.xml.tmpl | 23 + .../rootfs/etc/confd/templates/setenv.sh.tmpl | 3 + .../etc/confd/templates/tomcat-users.xml.tmpl | 9 + .../etc/cont-init.d/02-ngnix-install.sh | 2 + tomcat/rootfs/etc/nginx/conf.d/default.conf | 8 + tomcat/rootfs/etc/nginx/modules/daemon.conf | 1 + tomcat/rootfs/etc/services.d/nginx/finish | 4 + tomcat/rootfs/etc/services.d/nginx/run | 4 + tomcat/rootfs/etc/services.d/tomcat/finish | 4 + tomcat/rootfs/etc/services.d/tomcat/run | 6 + .../usr/local/bin/install-war-into-tomcat.sh | 122 ++ 294 files changed, 9254 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 abuild/.dockerignore create mode 100644 abuild/Dockerfile create mode 100644 abuild/README.md create mode 100644 activemq/.dockerignore create mode 100644 activemq/Dockerfile create mode 100644 activemq/README.md create mode 100644 activemq/rootfs/etc/confd/conf.d/credentials.properties.toml create mode 100644 activemq/rootfs/etc/confd/conf.d/groups.properties.toml create mode 100644 activemq/rootfs/etc/confd/conf.d/jetty-realm.properties.toml create mode 100644 activemq/rootfs/etc/confd/conf.d/users.properties.toml create mode 100644 activemq/rootfs/etc/confd/confd.toml create mode 100644 activemq/rootfs/etc/confd/templates/credentials.properties.tmpl create mode 100644 activemq/rootfs/etc/confd/templates/groups.properties.tmpl create mode 100644 activemq/rootfs/etc/confd/templates/jetty-realm.properties.tmpl create mode 100644 activemq/rootfs/etc/confd/templates/users.properties.tmpl create mode 100644 activemq/rootfs/etc/services.d/activemq/finish create mode 100644 activemq/rootfs/etc/services.d/activemq/run create mode 100644 alpaca/.dockerignore create mode 100644 alpaca/Dockerfile create mode 100644 alpaca/README.md create mode 100644 alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.fits.blueprint.xml.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.homarus.blueprint.xml.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.houdini.blueprint.xml.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.ocr.blueprint.xml.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.http.client.cfg.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.indexing.fcrepo.cfg.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.indexing.triplestore.cfg.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.indexing.triplestore.cfg.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.service.activemq.cfg.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.service.cfg.toml create mode 100644 alpaca/rootfs/etc/confd/conf.d/org.ops4j.pax.logging.cfg.toml create mode 100644 alpaca/rootfs/etc/confd/confd.toml create mode 100644 alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.fits.blueprint.xml.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.homarus.blueprint.xml.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.houdini.blueprint.xml.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.ocr.blueprint.xml.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.http.client.cfg.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.indexing.fcrepo.cfg.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.indexing.triplestore.cfg.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.indexing.triplestore.cfg.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.service.activemq.cfg.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.service.cfg.tmpl create mode 100644 alpaca/rootfs/etc/confd/templates/org.ops4j.pax.logging.cfg.tmpl create mode 100644 base/.dockerignore create mode 100644 base/Dockerfile create mode 100644 base/README.md create mode 100755 base/rootfs/etc/cleanup.d/empty-ash-history.sh create mode 100755 base/rootfs/etc/cleanup.d/empty-bash-history.sh create mode 100755 base/rootfs/etc/cleanup.d/empty-tmp.sh create mode 100644 base/rootfs/etc/confd/conf.d/.gitignore create mode 100644 base/rootfs/etc/confd/confd.toml create mode 100644 base/rootfs/etc/confd/templates/.gitignore create mode 100644 base/rootfs/etc/cont-finish.d/.gitignore create mode 100644 base/rootfs/etc/cont-init.d/01-confd-render-templates.sh create mode 100644 base/rootfs/etc/fix-attrs.d/.gitignore create mode 100644 base/rootfs/etc/services.d/.gitignore create mode 100644 base/rootfs/opt/.gitignore create mode 100644 base/rootfs/run/islandora/.gitignore create mode 100755 base/rootfs/sbin/apk-install.sh create mode 100755 base/rootfs/sbin/apk-uninstall.sh create mode 100755 base/rootfs/usr/local/bin/cleanup.sh create mode 100755 base/rootfs/usr/local/bin/git-clone-cached.sh create mode 100755 base/rootfs/usr/local/bin/wait-for-mysql.sh create mode 100755 base/rootfs/usr/local/bin/wait-for-open-port.sh create mode 100644 blazegraph/.dockerignore create mode 100644 blazegraph/Dockerfile create mode 100644 blazegraph/README.md create mode 100644 blazegraph/rootfs/etc/confd/conf.d/RWStore.properties.toml create mode 100644 blazegraph/rootfs/etc/confd/conf.d/log4j.properties.toml create mode 100644 blazegraph/rootfs/etc/confd/confd.toml create mode 100644 blazegraph/rootfs/etc/confd/templates/RWStore.properties.tmpl create mode 100644 blazegraph/rootfs/etc/confd/templates/log4j.properties.tmpl create mode 100644 build.gradle.kts create mode 100644 cantaloupe/.dockerignore create mode 100644 cantaloupe/Dockerfile create mode 100644 cantaloupe/README.md create mode 100644 cantaloupe/rootfs/etc/confd/conf.d/cataloupe.properties.toml create mode 100644 cantaloupe/rootfs/etc/confd/conf.d/setenv.sh.toml create mode 100644 cantaloupe/rootfs/etc/confd/confd.toml create mode 100644 cantaloupe/rootfs/etc/confd/templates/cantaloupe.properties.tmpl create mode 100755 cantaloupe/rootfs/etc/confd/templates/setenv.sh.tmpl create mode 100755 commands/continuous.sh create mode 100755 commands/drush.sh create mode 100755 commands/etcdctl.sh create mode 100755 commands/mysql.sh create mode 100755 commands/open-in-browser.sh create mode 100755 commands/shell.sh create mode 100644 composer/.dockerignore create mode 100644 composer/Dockerfile create mode 100644 composer/README.md create mode 100644 crayfish/.dockerignore create mode 100644 crayfish/Dockerfile create mode 100644 crayfish/README.md create mode 100644 crayfish/rootfs/etc/confd/conf.d/default.conf.toml create mode 100644 crayfish/rootfs/etc/confd/conf.d/syn-settings.xml.toml create mode 100644 crayfish/rootfs/etc/confd/templates/default.conf.tmpl create mode 100644 crayfish/rootfs/etc/confd/templates/syn-settings.xml.tmpl create mode 100644 crayfits/.dockerignore create mode 100644 crayfits/Dockerfile create mode 100644 crayfits/README.md create mode 100644 crayfits/rootfs/etc/confd/conf.d/.env.local.toml create mode 100644 crayfits/rootfs/etc/confd/conf.d/default.conf.toml create mode 100644 crayfits/rootfs/etc/confd/confd.toml create mode 100644 crayfits/rootfs/etc/confd/templates/.env.local.tmpl create mode 100644 crayfits/rootfs/etc/confd/templates/default.conf.tmpl create mode 100644 docker-compose.yml create mode 100644 drupal/.dockerignore create mode 100644 drupal/Dockerfile create mode 100644 drupal/README.md create mode 100644 drupal/rootfs/etc/confd/conf.d/create-drupal-databases.sh.toml create mode 100644 drupal/rootfs/etc/confd/conf.d/create-drupal-databases.sql.toml create mode 100644 drupal/rootfs/etc/confd/conf.d/drupal-install-sites.sh.toml create mode 100644 drupal/rootfs/etc/confd/confd.toml create mode 100644 drupal/rootfs/etc/confd/templates/create-drupal-databases.sh.tmpl create mode 100644 drupal/rootfs/etc/confd/templates/create-drupal-databases.sql.tmpl create mode 100644 drupal/rootfs/etc/confd/templates/drupal-install-sites.sh.tmpl create mode 100644 drupal/rootfs/etc/cont-init.d/03-drupal-setup.sh create mode 100644 drupal/rootfs/etc/nginx/conf.d/default.conf create mode 100644 fcrepo/.dockerignore create mode 100644 fcrepo/Dockerfile create mode 100644 fcrepo/README.md create mode 100644 fcrepo/rootfs/etc/confd/conf.d/activemq.xml.toml create mode 100644 fcrepo/rootfs/etc/confd/conf.d/allowed-external-content.txt.toml create mode 100644 fcrepo/rootfs/etc/confd/conf.d/claw.cnd.toml create mode 100644 fcrepo/rootfs/etc/confd/conf.d/context.xml.toml create mode 100644 fcrepo/rootfs/etc/confd/conf.d/fcrepo-config.xml.toml create mode 100644 fcrepo/rootfs/etc/confd/conf.d/repository.json.toml create mode 100644 fcrepo/rootfs/etc/confd/conf.d/setenv.sh.toml create mode 100644 fcrepo/rootfs/etc/confd/conf.d/syn-settings.xml.toml create mode 100644 fcrepo/rootfs/etc/confd/confd.toml create mode 100644 fcrepo/rootfs/etc/confd/templates/activemq.xml.tmpl create mode 100644 fcrepo/rootfs/etc/confd/templates/allowed-external-content.txt.tmpl create mode 100644 fcrepo/rootfs/etc/confd/templates/claw.cnd.tmpl create mode 100644 fcrepo/rootfs/etc/confd/templates/context.xml.tmpl create mode 100644 fcrepo/rootfs/etc/confd/templates/fcrepo-config.xml.tmpl create mode 100644 fcrepo/rootfs/etc/confd/templates/repository.json.tmpl create mode 100644 fcrepo/rootfs/etc/confd/templates/setenv.sh.tmpl create mode 100644 fcrepo/rootfs/etc/confd/templates/syn-settings.xml.tmpl create mode 100644 fcrepo/rootfs/etc/cont-init.d/03-fcrepo-setup.sh create mode 100644 fits/.dockerignore create mode 100644 fits/Dockerfile create mode 100644 fits/README.md create mode 100644 fits/rootfs/etc/confd/conf.d/catalina.properties.toml create mode 100644 fits/rootfs/etc/confd/conf.d/fits-service.properties.toml create mode 100644 fits/rootfs/etc/confd/confd.toml create mode 100644 fits/rootfs/etc/confd/templates/catalina.properties.tmpl create mode 100644 fits/rootfs/etc/confd/templates/fits-service.properties.tmpl create mode 100644 gemini/.dockerignore create mode 100644 gemini/Dockerfile create mode 100644 gemini/README.md create mode 100644 gemini/rootfs/etc/confd/conf.d/config.yaml.toml create mode 100644 gemini/rootfs/etc/confd/conf.d/create-gemini-database.sh.toml create mode 100644 gemini/rootfs/etc/confd/conf.d/create-gemini-database.sql.toml create mode 100644 gemini/rootfs/etc/confd/confd.toml create mode 100644 gemini/rootfs/etc/confd/templates/config.yaml.tmpl create mode 100644 gemini/rootfs/etc/confd/templates/create-gemini-database.sh.tmpl create mode 100644 gemini/rootfs/etc/confd/templates/create-gemini-database.sql.tmpl create mode 100644 gemini/rootfs/etc/cont-init.d/03-gemini-setup.sh create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 homarus/.dockerignore create mode 100644 homarus/Dockerfile create mode 100644 homarus/README.md create mode 100644 homarus/rootfs/etc/confd/conf.d/config.yaml.toml create mode 100644 homarus/rootfs/etc/confd/confd.toml create mode 100644 homarus/rootfs/etc/confd/templates/config.yaml.tmpl create mode 100644 houdini/.dockerignore create mode 100644 houdini/Dockerfile create mode 100644 houdini/README.md create mode 100644 houdini/rootfs/etc/confd/conf.d/monolog.yaml.toml create mode 100644 houdini/rootfs/etc/confd/confd.toml create mode 100644 houdini/rootfs/etc/confd/templates/monolog.yaml.tmpl create mode 100644 houdini/rootfs/var/www/crayfish/Houdini/config/packages/crayfish_commons.yaml create mode 100644 houdini/rootfs/var/www/crayfish/Houdini/config/services.yaml create mode 100644 hypercube/.dockerignore create mode 100644 hypercube/Dockerfile create mode 100644 hypercube/README.md create mode 100644 hypercube/rootfs/etc/confd/conf.d/config.yaml.toml create mode 100644 hypercube/rootfs/etc/confd/confd.toml create mode 100644 hypercube/rootfs/etc/confd/templates/config.yaml.tmpl create mode 100644 imagemagick/.dockerignore create mode 100644 imagemagick/Dockerfile create mode 100644 imagemagick/README.md create mode 100644 imagemagick/build/APKBUILD create mode 100644 imagemagick/build/disable-avaraging-tests.patch create mode 100644 java/.dockerignore create mode 100644 java/Dockerfile create mode 100644 java/README.md create mode 100755 java/rootfs/usr/local/bin/install-apache-service.sh create mode 100644 karaf/.dockerignore create mode 100644 karaf/Dockerfile create mode 100644 karaf/README.md create mode 100644 karaf/rootfs/etc/confd/conf.d/users.properties.toml create mode 100644 karaf/rootfs/etc/confd/templates/users.properties.tmpl create mode 100644 karaf/rootfs/etc/cont-init.d/04-karaf-startup.sh create mode 100644 karaf/rootfs/etc/services.d/karaf/finish create mode 100644 karaf/rootfs/etc/services.d/karaf/run create mode 100644 mariadb/.dockerignore create mode 100644 mariadb/Dockerfile create mode 100644 mariadb/README.md create mode 100644 mariadb/rootfs/etc/confd/conf.d/mariadb-server.cnf.toml create mode 100644 mariadb/rootfs/etc/confd/conf.d/my.cnf.toml create mode 100644 mariadb/rootfs/etc/confd/conf.d/set-root-user-password.sql.toml create mode 100644 mariadb/rootfs/etc/confd/confd.toml create mode 100644 mariadb/rootfs/etc/confd/templates/mariadb-server.cnf.tmpl create mode 100644 mariadb/rootfs/etc/confd/templates/my.cnf.tmpl create mode 100644 mariadb/rootfs/etc/confd/templates/set-root-user-password.sql.tmpl create mode 100755 mariadb/rootfs/etc/cont-init.d/03-mysql-setup.sh create mode 100644 mariadb/rootfs/etc/services.d/mysqld/finish create mode 100644 mariadb/rootfs/etc/services.d/mysqld/run create mode 100644 matomo/.dockerignore create mode 100644 matomo/Dockerfile create mode 100644 matomo/README.md create mode 100644 matomo/rootfs/etc/confd/conf.d/create-matomo-database.sh.toml create mode 100644 matomo/rootfs/etc/confd/conf.d/create-matomo-database.sql.toml create mode 100644 matomo/rootfs/etc/confd/conf.d/default.conf.toml create mode 100644 matomo/rootfs/etc/confd/confd.toml create mode 100644 matomo/rootfs/etc/confd/templates/create-matomo-database.sh.tmpl create mode 100644 matomo/rootfs/etc/confd/templates/create-matomo-database.sql.tmpl create mode 100644 matomo/rootfs/etc/confd/templates/default.conf.tmpl create mode 100644 matomo/rootfs/etc/cont-init.d/03-matomo-setup.sh create mode 100644 milliner/.dockerignore create mode 100644 milliner/Dockerfile create mode 100644 milliner/README.md create mode 100644 milliner/rootfs/etc/confd/conf.d/config.yaml.toml create mode 100644 milliner/rootfs/etc/confd/confd.toml create mode 100644 milliner/rootfs/etc/confd/templates/config.yaml.tmpl create mode 100644 nginx/.dockerignore create mode 100644 nginx/Dockerfile create mode 100644 nginx/README.md create mode 100644 nginx/rootfs/etc/confd/conf.d/nginx.conf.toml create mode 100644 nginx/rootfs/etc/confd/conf.d/php-fpm.conf.toml create mode 100644 nginx/rootfs/etc/confd/conf.d/php.ini.toml create mode 100644 nginx/rootfs/etc/confd/conf.d/www.conf.toml create mode 100644 nginx/rootfs/etc/confd/templates/nginx.conf.tmpl create mode 100644 nginx/rootfs/etc/confd/templates/php-fpm.conf.tmpl create mode 100644 nginx/rootfs/etc/confd/templates/php.ini.tmpl create mode 100644 nginx/rootfs/etc/confd/templates/www.conf.tmpl create mode 100644 nginx/rootfs/etc/cont-init.d/02-fpm-install.sh create mode 100755 nginx/rootfs/etc/cont-init.d/02-ngnix-install.sh create mode 100644 nginx/rootfs/etc/nginx/modules/daemon.conf create mode 100644 nginx/rootfs/etc/services.d/fpm/finish create mode 100644 nginx/rootfs/etc/services.d/fpm/run create mode 100644 nginx/rootfs/etc/services.d/nginx/finish create mode 100644 nginx/rootfs/etc/services.d/nginx/run create mode 100644 recast/.dockerignore create mode 100644 recast/Dockerfile create mode 100644 recast/README.md create mode 100644 recast/rootfs/etc/confd/conf.d/config.yaml.toml create mode 100644 recast/rootfs/etc/confd/confd.toml create mode 100644 recast/rootfs/etc/confd/templates/config.yaml.tmpl create mode 100644 sandbox/.dockerignore create mode 100644 sandbox/Dockerfile create mode 100644 sandbox/README.md create mode 100644 sandbox/rootfs/etc/confd/conf.d/blazegraph.properties.toml create mode 100644 sandbox/rootfs/etc/confd/conf.d/inference.nt.toml create mode 100644 sandbox/rootfs/etc/confd/conf.d/sandbox-setup.sh.toml create mode 100644 sandbox/rootfs/etc/confd/confd.toml create mode 100644 sandbox/rootfs/etc/confd/templates/blazegraph.properties.tmpl create mode 100644 sandbox/rootfs/etc/confd/templates/inference.nt.tmpl create mode 100644 sandbox/rootfs/etc/confd/templates/sandbox-setup.sh.tmpl create mode 100644 sandbox/rootfs/etc/cont-init.d/04-sandbox-setup.sh create mode 100644 sandbox/rootfs/opt/islandora/configs/jwt/jwt.config.yml create mode 100644 sandbox/rootfs/opt/islandora/configs/jwt/key.key.islandora_rsa_key.yml create mode 100644 settings.gradle.kts create mode 100644 solr/.dockerignore create mode 100644 solr/Dockerfile create mode 100644 solr/README.md create mode 100644 solr/rootfs/etc/services.d/solr/finish create mode 100644 solr/rootfs/etc/services.d/solr/run create mode 100644 tomcat/.dockerignore create mode 100644 tomcat/Dockerfile create mode 100644 tomcat/README.md create mode 100644 tomcat/rootfs/etc/confd/conf.d/manager.context.toml create mode 100644 tomcat/rootfs/etc/confd/conf.d/setenv.sh.toml create mode 100644 tomcat/rootfs/etc/confd/conf.d/tomcat-users.toml create mode 100644 tomcat/rootfs/etc/confd/templates/manager.context.xml.tmpl create mode 100755 tomcat/rootfs/etc/confd/templates/setenv.sh.tmpl create mode 100644 tomcat/rootfs/etc/confd/templates/tomcat-users.xml.tmpl create mode 100755 tomcat/rootfs/etc/cont-init.d/02-ngnix-install.sh create mode 100644 tomcat/rootfs/etc/nginx/conf.d/default.conf create mode 100644 tomcat/rootfs/etc/nginx/modules/daemon.conf create mode 100644 tomcat/rootfs/etc/services.d/nginx/finish create mode 100644 tomcat/rootfs/etc/services.d/nginx/run create mode 100644 tomcat/rootfs/etc/services.d/tomcat/finish create mode 100644 tomcat/rootfs/etc/services.d/tomcat/run create mode 100755 tomcat/rootfs/usr/local/bin/install-war-into-tomcat.sh diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..61fe8de4 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,20 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Gradle files +gradlew text eol=lf diff=bash +*.gradle text eol=lf diff=java +*.gradle.kts text eol=lf diff=java + +# Force bash scripts to always use lf line endings so that if a repo is accessed +# in Unix via a file share from Windows, the scripts will work. +*.bat text eol=crlf diff=batch +*.sh text eol=lf diff=bash +*.properties text eol=lf +run text eol=lf diff=bash +finish text eol=lf diff=bash + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.jar binary +*.war binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0e6273db --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.idea/ +.gradle/ +build +scratch +scratch.md diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..33df578f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "files.associations": { + "*.cfg.tmpl": "properties", + "*.cnf.tmpl": "properties", + "*.conf.tmpl": "properties", + "*.ini.tmpl": "properties", + "*.json.tmpl": "json", + "*.local.tmpl": "shellscript", + "*.properties.tmpl": "properties", + "*.sh.tmpl": "shellscript", + "*.sql.tmpl": "sql", + "*.xml.tmpl": "xml" + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..0ebca8a1 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# ISLE: Docker Prototype + +[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](./LICENSE) + +## Introduction + +Experiments with Docker and Islandora. + +## Design Decisions + +1. Reasonable defaults for a development environment, requiring no Environment variables / Etcd keys exist prior to running. \ No newline at end of file diff --git a/abuild/.dockerignore b/abuild/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/abuild/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/abuild/Dockerfile b/abuild/Dockerfile new file mode 100644 index 00000000..bbe26470 --- /dev/null +++ b/abuild/Dockerfile @@ -0,0 +1,12 @@ +# syntax=docker/dockerfile:experimental +FROM alpine:3.11.6 + +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + ln -s /var/cache/apk /etc/apk/cache && \ + apk add --update \ + alpine-sdk \ + coreutils \ + && \ + adduser -G abuild -g "Alpine Package Builder" -s /bin/ash -D builder && \ + echo "builder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers diff --git a/abuild/README.md b/abuild/README.md new file mode 100644 index 00000000..357c4dc0 --- /dev/null +++ b/abuild/README.md @@ -0,0 +1,76 @@ +# ABuild + +Docker image for `abuild` which is a tool used for create `apk` package for +consumption by other docker containers. + +It is not meant to be deployed as a service, but rather as base for when +creating packages as is done in `islandora/imagemagick`. + +Consumers are expected to follow this pattern: + +Create a folder `/build` in the project directory where the `APKBUILD` file +resides (which describes how to compile and build the package). + +Define a docker file that: + +1. Installs the packages required for building (but not necessarily running) the + package. +2. Run `abuild-keygen` to generate a private/public key pair for signing the + package. +3. Run `abuild` to build the package using `APKBUILD`. + +```dockerfile +# syntax=docker/dockerfile:experimental +FROM islandora/abuild:latest + +# Include packages required for building the package (not necessarily the ones require for running). +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + apk --update add \ + package-require-for-building-1 \ + package-require-for-building-2 \ + +COPY /build /build + +WORKDIR /build + +RUN chown -R builder /build + +ARG PACKAGER="Packer Name " + +USER builder + +RUN export PACKAGER="${PACKAGER}" && \ + abuild-keygen -ain && \ + abuild-apk update && \ + abuild +``` + +Subsequent images which consume the package can then bring it in via a +combination of multi-stage build, and Buildkit bind mounts like so: + +```dockerfile +FROM islandora/package_image:latest as PACKAGE_IMAGE + +FROM islandora/crayfish:latest + +RUN --mount=type=bind,from=PACKAGE_IMAGE,source=/home/builder/packages/x86_64,target=/packages \ + --mount=type=bind,from=PACKAGE_IMAGE,source=/etc/apk/keys,target=/etc/apk/keys \ + --mount=type=cache,target=/root/.composer/cache \ + apk add /packages/PACKAGE_NAME-*.apk && \ + ... other build steps ...&& \ + cleanup.sh +``` + +Where the image is brought in as `PACKAGE_IMAGE` and the directory where the +generated `.pkg` resides as well as the location of `apk` key files are mounted +into the destination image. + +## Dependencies + +Requires `alpine:3.11.6` docker image to build. + +## Reference + +- +- diff --git a/activemq/.dockerignore b/activemq/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/activemq/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/activemq/Dockerfile b/activemq/Dockerfile new file mode 100644 index 00000000..1945cd25 --- /dev/null +++ b/activemq/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/java:latest + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + ACTIVEMQ_VERSION="5.14.5" && \ + install-apache-service.sh \ + --name activemq \ + --version "${ACTIVEMQ_VERSION}" \ + --key "62ED4DF0BACB8793" \ + --mirror "https://archive.apache.org/dist/activemq/${ACTIVEMQ_VERSION}" \ + --file "apache-activemq-${ACTIVEMQ_VERSION}-bin.tar.gz" \ + examples webapps-demo docs + +WORKDIR /opt/activemq + +EXPOSE 61616 5672 61613 1883 61614 8161 + +VOLUME [ "/opt/activemq/data" ] + +COPY rootfs / diff --git a/activemq/README.md b/activemq/README.md new file mode 100644 index 00000000..b7621ed0 --- /dev/null +++ b/activemq/README.md @@ -0,0 +1,94 @@ +# ActiveMQ + +Docker image for [ActiveMQ] version 5.14.5. + +Please refer to the [ActiveMQ Documentation] for more in-depth information. + +As a quick example this will bring up an instance of ActiveMQ, and allow you to +log into the [WebConsole] on `http://localhost:8161` as the user `admin` with +the password `password`. + +```bash +docker run --rm -ti -p 8161:8161 islandora/activemq +``` + +> N.B. if no credentials are given you will not be able to log in via the +[WebConsole]. + +## Dependencies + +Requires `islandora/java` docker image to build. Please refer to the +[Java Image README](../java/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Ports + +| Port | Description | +| :---- | :----------- | +| 1883 | [MQTT] | +| 5672 | [AMPQ] | +| 8161 | [WebConsole] | +| 61613 | [STOMP] | +| 61614 | [WS] | +| 61616 | [OpenWire] | + +## Volumes + +| Path | Description | +| :----------------- | :------------------ | +| /opt/activemq/data | [AMQ Message Store] | + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :-------------------------- | :--------------------------- | :------- | :--------------------------------------- | +| ACTIVEMQ_USER | /activemq/user | admin | See [Security]: credentials.properties | +| ACTIVEMQ_PASSWORD | /activemq/password | password | See [Security]: credentials.properties | +| ACTIVEMQ_WEB_ADMIN_NAME | /activemq/web/admin/name | admin | See [WebConsole]: jetty-realm.properties | +| ACTIVEMQ_WEB_ADMIN_PASSWORD | /activemq/web/admin/password | password | See [WebConsole]: jetty-realm.properties | +| ACTIVEMQ_WEB_ADMIN_ROLES | /activemq/web/admin/roles | admin | See [WebConsole]: jetty-realm.properties | + +Additional users/groups/etc can be defined by adding more environment variables, +following the above conventions: + +| Environment Variable | Etcd Key | Description | +| :-------------------------------- | :--------------------------------- | :--------------------------------------- | +| ACTIVEMQ_USER_{USER}_NAME | /activemq/user/{USER}/name | See [Security]: users.properties | +| ACTIVEMQ_USER_{USER}_PASSWORD | /activemq/user/{USER}/password | See [Security]: users.properties | +| ACTIVEMQ_GROUP_{GROUP}_NAME | /activemq/group/{GROUP}/name | See [Security]: groups.properties | +| ACTIVEMQ_GROUP_{GROUP}_MEMBERS | /activemq/group/{GROUP}/members | See [Security]: groups.properties | +| ACTIVEMQ_WEB_USER_{USER}_NAME | /activemq/web/user/{USER}/name | See [WebConsole]: jetty-realm.properties | +| ACTIVEMQ_WEB_USER_{USER}_PASSWORD | /activemq/web/user/{USER}/password | See [WebConsole]: jetty-realm.properties | +| ACTIVEMQ_WEB_USER_{USER}_ROLES | /activemq/web/user/{USER}/roles | See [WebConsole]: jetty-realm.properties | + +> N.B. These do not have defaults. + +For example to add a new user `someone` to the [WebConsole] you would need to +define the following: + +| Environment Variable | Etcd Key | Value | +| :--------------------------------- | :---------------------------------- | :------- | +| ACTIVEMQ_WEB_USER_SOMEONE_NAME | /activemq/web/user/someone/name | someone | +| ACTIVEMQ_WEB_USER_SOMEONE_PASSWORD | /activemq/web/user/someone/password | password | +| ACTIVEMQ_WEB_USER_SOMEONE_ROLES | /activemq/web/user/someone/roles | admin | + +## Logs + +| Path | Description | +| :------------------------------ | :------------- | +| STDOUT | [ActiveMQ Log] | +| /opt/activemq/data/activemq.log | [ActiveMQ Log] | +| /opt/activemq/data/audit.log | [Audit Log] | + +[ActiveMQ Documentation]: https://activemq.apache.org/components/classic/documentation +[ActiveMQ Log]: https://activemq.apache.org/how-do-i-change-the-logging +[ActiveMQ]: http://activemq.apache.org/ +[AMPQ]: https://activemq.apache.org/amqp +[AMQ Message Store]: https://activemq.apache.org/amq-message-store +[Audit Log]: https://activemq.apache.org/audit-logging +[MQTT]: https://activemq.apache.org/mqtt +[OpenWire]: https://activemq.apache.org/openwire +[Security]: https://activemq.apache.org/security +[STOMP]: https://activemq.apache.org/stomp +[WebConsole]: https://activemq.apache.org/web-console +[WS]: https://activemq.apache.org/ws-notification diff --git a/activemq/rootfs/etc/confd/conf.d/credentials.properties.toml b/activemq/rootfs/etc/confd/conf.d/credentials.properties.toml new file mode 100644 index 00000000..8db2948b --- /dev/null +++ b/activemq/rootfs/etc/confd/conf.d/credentials.properties.toml @@ -0,0 +1,7 @@ +[template] +src = "credentials.properties.tmpl" +dest = "/opt/activemq/conf/credentials.properties" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/user", "/password" ] diff --git a/activemq/rootfs/etc/confd/conf.d/groups.properties.toml b/activemq/rootfs/etc/confd/conf.d/groups.properties.toml new file mode 100644 index 00000000..beb212b3 --- /dev/null +++ b/activemq/rootfs/etc/confd/conf.d/groups.properties.toml @@ -0,0 +1,7 @@ +[template] +src = "groups.properties.tmpl" +dest = "/opt/activemq/conf/groups.properties" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/group" ] diff --git a/activemq/rootfs/etc/confd/conf.d/jetty-realm.properties.toml b/activemq/rootfs/etc/confd/conf.d/jetty-realm.properties.toml new file mode 100644 index 00000000..d58f38a1 --- /dev/null +++ b/activemq/rootfs/etc/confd/conf.d/jetty-realm.properties.toml @@ -0,0 +1,7 @@ +[template] +src = "jetty-realm.properties.tmpl" +dest = "/opt/activemq/conf/jetty-realm.properties" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/web/user" ] diff --git a/activemq/rootfs/etc/confd/conf.d/users.properties.toml b/activemq/rootfs/etc/confd/conf.d/users.properties.toml new file mode 100644 index 00000000..6adc80b9 --- /dev/null +++ b/activemq/rootfs/etc/confd/conf.d/users.properties.toml @@ -0,0 +1,7 @@ +[template] +src = "users.properties.tmpl" +dest = "/opt/activemq/conf/users.properties" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/user" ] diff --git a/activemq/rootfs/etc/confd/confd.toml b/activemq/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..bbfe3bbc --- /dev/null +++ b/activemq/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/activemq" diff --git a/activemq/rootfs/etc/confd/templates/credentials.properties.tmpl b/activemq/rootfs/etc/confd/templates/credentials.properties.tmpl new file mode 100644 index 00000000..fdc94116 --- /dev/null +++ b/activemq/rootfs/etc/confd/templates/credentials.properties.tmpl @@ -0,0 +1,3 @@ +# Defines credentials that will be used by components (like web console) to access the broker +activemq.username={{ getv "/user" "admin" }} +activemq.password={{ getv "/password" "password" }} diff --git a/activemq/rootfs/etc/confd/templates/groups.properties.tmpl b/activemq/rootfs/etc/confd/templates/groups.properties.tmpl new file mode 100644 index 00000000..ef272b95 --- /dev/null +++ b/activemq/rootfs/etc/confd/templates/groups.properties.tmpl @@ -0,0 +1,4 @@ +# Defines groups and the users that belong to them. +# group=user[,user ...] +{{ range $dir := lsdir "/group" }}{{ getv (printf "/group/%s/name" $dir) }}={{ getv (printf "/group/%s/members" $dir) }} +{{ end }} diff --git a/activemq/rootfs/etc/confd/templates/jetty-realm.properties.tmpl b/activemq/rootfs/etc/confd/templates/jetty-realm.properties.tmpl new file mode 100644 index 00000000..f1c6291a --- /dev/null +++ b/activemq/rootfs/etc/confd/templates/jetty-realm.properties.tmpl @@ -0,0 +1,5 @@ +# Defines users that can access the web (console, demo, etc.) +# username: password [,rolename ...] +{{ getv "/web/admin/name" "admin" }}: {{ getv "/web/admin/password" "password" }}, {{ getv "/web/admin/roles" "admin" }} +{{ range $dir := lsdir "/web/user" }}{{ getv (printf "/web/user/%s/name" $dir) }}: {{ getv (printf "/web/user/%s/password" $dir) }}, {{ getv (printf "/web/user/%s/roles" $dir) }} +{{ end }} diff --git a/activemq/rootfs/etc/confd/templates/users.properties.tmpl b/activemq/rootfs/etc/confd/templates/users.properties.tmpl new file mode 100644 index 00000000..b44780bf --- /dev/null +++ b/activemq/rootfs/etc/confd/templates/users.properties.tmpl @@ -0,0 +1,2 @@ +{{ range $dir := lsdir "/user" }}{{ getv (printf "/user/%s/name" $dir) }}={{ getv (printf "/user/%s/password" $dir) }} +{{ end }} diff --git a/activemq/rootfs/etc/services.d/activemq/finish b/activemq/rootfs/etc/services.d/activemq/finish new file mode 100644 index 00000000..f8984dd3 --- /dev/null +++ b/activemq/rootfs/etc/services.d/activemq/finish @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -S1 +# -*- mode: sh -*- +# vi: set ft=sh : +s6-svscanctl -t /var/run/s6/services diff --git a/activemq/rootfs/etc/services.d/activemq/run b/activemq/rootfs/etc/services.d/activemq/run new file mode 100644 index 00000000..1c175abc --- /dev/null +++ b/activemq/rootfs/etc/services.d/activemq/run @@ -0,0 +1,6 @@ +#!/usr/bin/execlineb -P +# -*- mode: sh -*- +# vi: set ft=sh : +with-contenv +s6-setuidgid activemq +/opt/activemq/bin/activemq console diff --git a/alpaca/.dockerignore b/alpaca/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/alpaca/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/alpaca/Dockerfile b/alpaca/Dockerfile new file mode 100644 index 00000000..f71b7c28 --- /dev/null +++ b/alpaca/Dockerfile @@ -0,0 +1,41 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/karaf:latest + +# Install common features and repos +RUN bin/start && \ + ALPACA_VERSION=1.0.3 && \ + ACTIVEMQ_VERSION=5.15.0 && \ + CAMEL_VERSION=2.20.4 && \ + bin/client -r 10 -d 5 "feature:repo-add mvn:ca.islandora.alpaca/islandora-karaf/${ALPACA_VERSION}/xml/features" && \ + bin/client -r 10 -d 5 "feature:repo-add mvn:org.apache.activemq/activemq-karaf/${ACTIVEMQ_VERSION}/xml/features" && \ + bin/client -r 10 -d 5 "feature:repo-add mvn:org.apache.camel.karaf/apache-camel/${CAMEL_VERSION}/xml/features" && \ + bin/client -r 10 -d 5 "feature:install fcrepo-service-activemq" && \ + bin/client -r 10 -d 5 "feature:install fcrepo-service-camel" && \ + bin/client -r 10 -d 5 "feature:install islandora-http-client" && \ + bin/stop && \ + rm -rf instances/* + +# Derivative connector +RUN bin/start && \ + bin/client -r 10 -d 5 "feature:install islandora-connector-derivative" && \ + bin/stop && \ + rm -rf instances/* + +# Fcrepo indexing THIS IS THE PROBLEM +RUN bin/start && \ + bin/client -r 10 -d 5 "feature:install islandora-indexing-fcrepo" && \ + bin/stop && \ + rm -rf instances/* + +# Triple indexing +RUN bin/start && \ + bin/client -r 10 -d 5 "feature:install fcrepo-indexing-triplestore" && \ + bin/client -r 10 -d 5 "feature:install islandora-indexing-triplestore" && \ + bin/stop && \ + rm -rf instances/* + +RUN chown -R karaf:karaf /opt/karaf + +COPY rootfs / + +ENV JAVA_OPTS="-Dfile.encoding=UTF-8 -Dnet.sf.ehcache.skipUpdateCheck=true -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+UseParNewGC -XX:MaxPermSize=128m -Xms512m -Xmx8g" \ No newline at end of file diff --git a/alpaca/README.md b/alpaca/README.md new file mode 100644 index 00000000..6fb02361 --- /dev/null +++ b/alpaca/README.md @@ -0,0 +1,79 @@ +# Alpaca + +Docker image for [Alpaca] version 1.0.3. + +Please refer to the [Alpaca Documentation] for more in-depth information. + +As a quick example this will bring up an instance of Alpaca, and allow you to +log view the [WebConsole] on as the user `admin` with +the password `password`. + +```bash +docker run --rm -ti -p 8181:8181 \ + -e "KARAF_ADMIN_NAME=admin" \ + -e "KARAF_ADMIN_PASSWORD=password" \ + islandora/alpaca +``` + +## Dependencies + +Requires `islandora/karaf` docker image to build. Please refer to the +[Karaf Image README](../karaf/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :----------------------------------------- | :----------------------------------------- | :-------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ALPACA_ACTIVEMQ_PASSWORD | /alpaca/activemq/password | password | Password to authenticate with | +| ALPACA_ACTIVEMQ_URL | /alpaca/activemq/url | tcp://broker:61616 | The url for connecting to the ActiveMQ broker, shared by all components | +| ALPACA_ACTIVEMQ_USER | /alpaca/activemq/user | admin | User to authenticate as | +| ALPACA_FCREPO_AUTH_HOST | /alpaca/fcrepo/auth/host | | User to authenticate as | +| ALPACA_FCREPO_AUTH_PASSWORD | /alpaca/fcrepo/auth/password | | Password to authenticate with | +| ALPACA_FCREPO_AUTH_USER | /alpaca/fcrepo/auth/user | | URL to authenticate against | +| ALPACA_FCREPO_URL | /alpaca/fcrepo/url | http://fcrepo/fcrepo/rest | The url of fcrepo rest API | +| ALPACA_FITS_QUEUE | /alpaca/fits/queue | broker:queue:islandora-connector-fits | ActiveMQ Queue to consume from | +| ALPACA_FITS_REDELIVERIES | /alpaca/fits/redeliveries | 10 | Number of attempts to redeliver if an exception occurs | +| ALPACA_FITS_SERVICE | /alpaca/fits/service | http://crayfits:8000 | Url of micro-service | +| ALPACA_HOMARUS_QUEUE | /alpaca/homarus/queue | broker:queue:islandora-connector-homarus | ActiveMQ Queue to consume from | +| ALPACA_HOMARUS_REDELIVERIES | /alpaca/homarus/redeliveries | 10 | Number of attempts to redeliver if an exception occurs | +| ALPACA_HOMARUS_SERVICE | /alpaca/homarus/service | http://homarus:8000/convert | Url of micro-service | +| ALPACA_HOUDINI_QUEUE | /alpaca/houdini/queue | broker:queue:islandora-connector-houdini | ActiveMQ Queue to consume from | +| ALPACA_HOUDINI_REDELIVERIES | /alpaca/houdini/redeliveries | 10 | Number of attempts to redeliver if an exception occurs | +| ALPACA_HOUDINI_SERVICE | /alpaca/houdini/service | http://houdini:8000/convert | Url of micro-service | +| ALPACA_HTTP_TOKEN | /alpaca/http/token | islandora | The static token value to be used for authentication by the HttpClient available as an OSGi service for other services to use against the Fedora repository | +| ALPACA_INDEXING_GEMINI_URL | /alpaca/indexing/gemini/url | http://gemini:8000 | Url of micro-service | +| ALPACA_INDEXING_MILLINER_URL | /alpaca/indexing/milliner/url | http://milliner:8000 | Url of micro-service | +| ALPACA_INDEXING_REDELIVERIES | /alpaca/indexing/redeliveries | 10 | Number of attempts to redeliver if an exception occurs | +| ALPACA_INDEXING_STREAM_FILE_DELETE | /alpaca/indexing/stream/file/delete | broker:queue:islandora-indexing-fcrepo-file-delete | ActiveMQ Queue to consume from | +| ALPACA_INDEXING_STREAM_FILE_INDEX | /alpaca/indexing/stream/file/index | broker:queue:islandora-indexing-fcrepo-file | ActiveMQ Queue to consume from | +| ALPACA_INDEXING_STREAM_INPUT | /alpaca/indexing/stream/input | broker:topic:fedora | ActiveMQ Topic to consume | +| ALPACA_INDEXING_STREAM_MEDIA_INDEX | /alpaca/indexing/stream/media/index | broker:queue:islandora-indexing-fcrepo-media | ActiveMQ Queue to consume from | +| ALPACA_INDEXING_STREAM_NODE_DELETE | /alpaca/indexing/stream/node/delete | broker:queue:islandora-indexing-fcrepo-delete | ActiveMQ Queue to consume from | +| ALPACA_INDEXING_STREAM_NODE_INDEX | /alpaca/indexing/stream/node/index | broker:queue:islandora-indexing-fcrepo-content | ActiveMQ Queue to consume from | +| ALPACA_INDEXING_STREAM_TRIPLESTORE_DELETE | /alpaca/indexing/stream/triplestore/delete | broker:queue:islandora-indexing-triplestore-delete | ActiveMQ Queue to consume from | +| ALPACA_INDEXING_STREAM_TRIPLESTORE_INDEX | /alpaca/indexing/stream/triplestore/index | broker:queue:islandora-indexing-triplestore-index | ActiveMQ Queue to consume from | +| ALPACA_INDEXING_STREAM_TRIPLESTORE_REINDEX | /alpaca/indexing/stream/reindex | broker:queue:triplestore.reindex | ActiveMQ Queue to consume from | +| ALPACA_INDEXING_URL | /alpaca/indexing/url | http://blazegraph/bigdata/namespace/islandora/sparql | Url to triple store indexer | +| ALPACA_LOGGER_CAMEL_LEVEL | /alpaca/logger/camel/level | WARN | Camel [Log Level] | +| ALPACA_LOGGER_ISLANDORA_LEVEL | /alpaca/logger/islandora/level | WARN | Islandora [Log Level] | +| ALPACA_LOGGER_ROOT_LEVEL | /alpaca/logger/root/level | WARN | Root [Log Level] | +| ALPACA_OCR_QUEUE | /alpaca/ocr/queue | broker:queue:islandora-connector-ocr | ActiveMQ Queue to consume from | +| ALPACA_OCR_REDELIVERIES | /alpaca/ocr/redeliveries | 10 | Number of attempts to redeliver if an exception occurs | +| ALPACA_OCR_SERVICE | /alpaca/ocr/service | http://hypercube:8000 | Url of micro-service | + +## Logs + +| Path | Description | +| :-------------------------------- | :--------------- | +| /opt/karaf/data/log/camel.log | Camel Log | +| /opt/karaf/data/log/islandora.log | Islandora Log | + +[Alpaca Documentation]: https://islandora.github.io/documentation/ +[Alpaca]: https://github.com/Islandora/Alpaca +[JMX]: https://karaf.apache.org/manual/latest/#_monitoring_and_management_using_jmx +[Karaf Directory Structure]: https://karaf.apache.org/manual/latest/#_directory_structure +[Log Level]: https://logging.apache.org/log4j/2.x/manual/customloglevels.html +[RMI]: https://karaf.apache.org/manual/latest/monitoring +[SSH]: https://karaf.apache.org/manual/latest/remote +[WebConsole]: https://karaf.apache.org/manual/latest/webconsole diff --git a/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.fits.blueprint.xml.toml b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.fits.blueprint.xml.toml new file mode 100644 index 00000000..3e24c34e --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.fits.blueprint.xml.toml @@ -0,0 +1,7 @@ +[template] +src = "ca.islandora.alpaca.connector.fits.blueprint.xml.tmpl" +dest = "/opt/karaf/deploy/ca.islandora.alpaca.connector.fits.blueprint.xml" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/fits" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.homarus.blueprint.xml.toml b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.homarus.blueprint.xml.toml new file mode 100644 index 00000000..708413b4 --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.homarus.blueprint.xml.toml @@ -0,0 +1,7 @@ +[template] +src = "ca.islandora.alpaca.connector.homarus.blueprint.xml.tmpl" +dest = "/opt/karaf/deploy/ca.islandora.alpaca.connector.homarus.blueprint.xml" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/homarus" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.houdini.blueprint.xml.toml b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.houdini.blueprint.xml.toml new file mode 100644 index 00000000..2dcba334 --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.houdini.blueprint.xml.toml @@ -0,0 +1,7 @@ +[template] +src = "ca.islandora.alpaca.connector.houdini.blueprint.xml.tmpl" +dest = "/opt/karaf/deploy/ca.islandora.alpaca.connector.houdini.blueprint.xml" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/houdini" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.ocr.blueprint.xml.toml b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.ocr.blueprint.xml.toml new file mode 100644 index 00000000..e75d086e --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.connector.ocr.blueprint.xml.toml @@ -0,0 +1,7 @@ +[template] +src = "ca.islandora.alpaca.connector.ocr.blueprint.xml.tmpl" +dest = "/opt/karaf/deploy/ca.islandora.alpaca.connector.ocr.blueprint.xml" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/ocr" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.http.client.cfg.toml b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.http.client.cfg.toml new file mode 100644 index 00000000..24b552e6 --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.http.client.cfg.toml @@ -0,0 +1,7 @@ +[template] +src = "ca.islandora.alpaca.http.client.cfg.tmpl" +dest = "/opt/karaf/etc/ca.islandora.alpaca.http.client.cfg" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/http" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.indexing.fcrepo.cfg.toml b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.indexing.fcrepo.cfg.toml new file mode 100644 index 00000000..43f1825b --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.indexing.fcrepo.cfg.toml @@ -0,0 +1,7 @@ +[template] +src = "ca.islandora.alpaca.indexing.fcrepo.cfg.tmpl" +dest = "/opt/karaf/etc/ca.islandora.alpaca.indexing.fcrepo.cfg" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/indexing" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.indexing.triplestore.cfg.toml b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.indexing.triplestore.cfg.toml new file mode 100644 index 00000000..6cdc21e5 --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/ca.islandora.alpaca.indexing.triplestore.cfg.toml @@ -0,0 +1,7 @@ +[template] +src = "ca.islandora.alpaca.indexing.triplestore.cfg.tmpl" +dest = "/opt/karaf/etc/ca.islandora.alpaca.indexing.triplestore.cfg" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/indexing" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.indexing.triplestore.cfg.toml b/alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.indexing.triplestore.cfg.toml new file mode 100644 index 00000000..92385107 --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.indexing.triplestore.cfg.toml @@ -0,0 +1,7 @@ +[template] +src = "org.fcrepo.camel.indexing.triplestore.cfg.tmpl" +dest = "/opt/karaf/etc/org.fcrepo.camel.indexing.triplestore.cfg" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/indexing" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.service.activemq.cfg.toml b/alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.service.activemq.cfg.toml new file mode 100644 index 00000000..393f8cc1 --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.service.activemq.cfg.toml @@ -0,0 +1,7 @@ +[template] +src = "org.fcrepo.camel.service.activemq.cfg.tmpl" +dest = "/opt/karaf/etc/org.fcrepo.camel.service.activemq.cfg" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/activemq" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.service.cfg.toml b/alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.service.cfg.toml new file mode 100644 index 00000000..35c2a5e5 --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/org.fcrepo.camel.service.cfg.toml @@ -0,0 +1,7 @@ +[template] +src = "org.fcrepo.camel.service.cfg.tmpl" +dest = "/opt/karaf/etc/org.fcrepo.camel.service.cfg" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/fcrepo" ] diff --git a/alpaca/rootfs/etc/confd/conf.d/org.ops4j.pax.logging.cfg.toml b/alpaca/rootfs/etc/confd/conf.d/org.ops4j.pax.logging.cfg.toml new file mode 100644 index 00000000..c6fa6d91 --- /dev/null +++ b/alpaca/rootfs/etc/confd/conf.d/org.ops4j.pax.logging.cfg.toml @@ -0,0 +1,7 @@ +[template] +src = "org.ops4j.pax.logging.cfg.tmpl" +dest = "/opt/karaf/etc/org.ops4j.pax.logging.cfg" +uid = 100 +gid = 1000 +mode = "0644" +keys = [ "/log" ] diff --git a/alpaca/rootfs/etc/confd/confd.toml b/alpaca/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..0415e04e --- /dev/null +++ b/alpaca/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/alpaca" diff --git a/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.fits.blueprint.xml.tmpl b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.fits.blueprint.xml.tmpl new file mode 100644 index 00000000..0cd13abf --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.fits.blueprint.xml.tmpl @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + ca.islandora.alpaca.connector.derivative + + + diff --git a/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.homarus.blueprint.xml.tmpl b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.homarus.blueprint.xml.tmpl new file mode 100644 index 00000000..e2bbc7e8 --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.homarus.blueprint.xml.tmpl @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + ca.islandora.alpaca.connector.derivative + + + diff --git a/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.houdini.blueprint.xml.tmpl b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.houdini.blueprint.xml.tmpl new file mode 100644 index 00000000..3c919de9 --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.houdini.blueprint.xml.tmpl @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + ca.islandora.alpaca.connector.derivative + + + diff --git a/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.ocr.blueprint.xml.tmpl b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.ocr.blueprint.xml.tmpl new file mode 100644 index 00000000..19e67394 --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.connector.ocr.blueprint.xml.tmpl @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + ca.islandora.alpaca.connector.derivative + + + diff --git a/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.http.client.cfg.tmpl b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.http.client.cfg.tmpl new file mode 100644 index 00000000..c1a25f04 --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.http.client.cfg.tmpl @@ -0,0 +1 @@ +token.value={{ getv "/http/token" "islandora" }} diff --git a/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.indexing.fcrepo.cfg.tmpl b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.indexing.fcrepo.cfg.tmpl new file mode 100644 index 00000000..49a7e682 --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.indexing.fcrepo.cfg.tmpl @@ -0,0 +1,8 @@ +error.maxRedeliveries={{ getv "/indexing/redeliveries" "10" }} +file.delete.stream={{ getv "/indexing/stream/file/delete" "broker:queue:islandora-indexing-fcrepo-file-delete" }} +file.stream={{ getv "/indexing/stream/file/index" "broker:queue:islandora-indexing-fcrepo-file" }} +gemini.baseUrl={{ getv "/indexing/gemini/url" "http://gemini:8000" }} +media.stream={{ getv "/indexing/stream/media/index" "broker:queue:islandora-indexing-fcrepo-media" }} +milliner.baseUrl={{ getv "/indexing/milliner/url" "http://milliner:8000" }} +node.delete.stream={{ getv "/indexing/stream/node/delete" "broker:queue:islandora-indexing-fcrepo-delete" }} +node.stream={{ getv "/indexing/stream/node/index" "broker:queue:islandora-indexing-fcrepo-content" }} diff --git a/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.indexing.triplestore.cfg.tmpl b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.indexing.triplestore.cfg.tmpl new file mode 100644 index 00000000..aa09e7a2 --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/ca.islandora.alpaca.indexing.triplestore.cfg.tmpl @@ -0,0 +1,4 @@ +index.stream={{ getv "/indexing/stream/triplestore/index" "broker:queue:islandora-indexing-triplestore-index" }} +delete.stream={{ getv "/indexing/stream/triplestore/delete" "broker:queue:islandora-indexing-triplestore-delete" }} +triplestore.baseUrl={{ getv "/indexing/url" "http://blazegraph/bigdata/namespace/islandora/sparql" }} +error.maxRedeliveries={{ getv "/indexing/redeliveries" "10" }} diff --git a/alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.indexing.triplestore.cfg.tmpl b/alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.indexing.triplestore.cfg.tmpl new file mode 100644 index 00000000..5d6003cc --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.indexing.triplestore.cfg.tmpl @@ -0,0 +1,3 @@ +input.stream={{ getv "/indexing/stream/input" "broker:topic:fedora"}} +triplestore.baseUrl={{ getv "/indexing/url" "http://blazegraph/bigdata/namespace/islandora/sparql" }} +triplestore.reindex.stream={{ getv "/indexing/stream/triplestore/reindex" "broker:queue:triplestore.reindex" }} diff --git a/alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.service.activemq.cfg.tmpl b/alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.service.activemq.cfg.tmpl new file mode 100644 index 00000000..c2e28736 --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.service.activemq.cfg.tmpl @@ -0,0 +1,6 @@ +# The JMS connection URI, used for connecting to a local or remote ActiveMQ broker +jms.brokerUrl={{ getv "/activemq/url" "tcp://activemq:61616" }} + +# If authentication is enabled on the activemq broker, add appropriate values here +jms.username={{ getv "/activemq/user" "" }} +jms.password={{ getv "/activemq/password" "" }} diff --git a/alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.service.cfg.tmpl b/alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.service.cfg.tmpl new file mode 100644 index 00000000..c5e1f646 --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/org.fcrepo.camel.service.cfg.tmpl @@ -0,0 +1,7 @@ +# The baseUrl for the fedora repository. +fcrepo.baseUrl={{ getv "/fcrep/url" "http://fcrepo/fcrepo/rest" }} + +# If authentication is enabled on the Fedora repository, add appropriate values here +fcrepo.authUsername={{ getv "/fcrep/auth/user" "" }} +fcrepo.authPassword={{ getv "/fcrep/auth/password" "" }} +fcrepo.authHost={{ getv "/fcrep/host" "" }} diff --git a/alpaca/rootfs/etc/confd/templates/org.ops4j.pax.logging.cfg.tmpl b/alpaca/rootfs/etc/confd/templates/org.ops4j.pax.logging.cfg.tmpl new file mode 100644 index 00000000..762a1076 --- /dev/null +++ b/alpaca/rootfs/etc/confd/templates/org.ops4j.pax.logging.cfg.tmpl @@ -0,0 +1,34 @@ +# Root logger +log4j.rootLogger={{ getv "logger/root/level" "WARN" }}, out, osgi:* +log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer + +# File appender +log4j.appender.out=org.apache.log4j.RollingFileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n +log4j.appender.out.file=${karaf.data}/log/karaf.log +log4j.appender.out.append=true +log4j.appender.out.maxFileSize=1MB +log4j.appender.out.maxBackupIndex=10 + +# Camel Logger +log4j.appender.camel=org.apache.log4j.RollingFileAppender +log4j.appender.camel.layout=org.apache.log4j.PatternLayout +log4j.appender.camel.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n +log4j.appender.camel.file=${karaf.data}/log/camel.log +log4j.appender.camel.append=false +log4j.appender.camel.maxFileSize=1MB +log4j.appender.camel.maxBackupIndex=10 + +log4j.logger.org.apache.camel={{ getv "logger/camel/level" "WARN" }}, camel + +# Islandora Logger +log4j.appender.islandora=org.apache.log4j.RollingFileAppender +log4j.appender.islandora.layout=org.apache.log4j.PatternLayout +log4j.appender.islandora.layout.ConversionPattern=%d{ISO8601} | %-5.5p | %-16.16t | %-32.32c{1} | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n +log4j.appender.islandora.file=${karaf.data}/log/islandora.log +log4j.appender.islandora.append=false +log4j.appender.islandora.maxFileSize=1MB +log4j.appender.islandora.maxBackupIndex=10 + +log4j.logger.ca.islandora.camel={{ getv "logger/islandora/level" "WARN" }}, islandora diff --git a/base/.dockerignore b/base/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/base/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/base/Dockerfile b/base/Dockerfile new file mode 100644 index 00000000..89e4ca37 --- /dev/null +++ b/base/Dockerfile @@ -0,0 +1,45 @@ +# syntax=docker/dockerfile:experimental +FROM alpine:3.11.6 + +# Install packages and tools required by all downstream images. +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + --mount=id=downloads,type=cache,target=/opt/downloads \ + ln -s /var/cache/apk /etc/apk/cache && \ + apk add --update \ + bash \ + curl \ + git \ + gnupg \ + mariadb-client \ + mysql-client \ + netcat-openbsd \ + openssl \ + postgresql-client \ + wget \ + && \ + S6_VERSION="1.22.1.0" && \ + CONFD_VERSION="0.15.0" && \ + CONFD_SHA256="7f3aba1d803543dd1df3944d014f055112cf8dadf0a583c76dd5f46578ebe3c2" && \ + wget -N -P /opt/downloads https://github.com/just-containers/s6-overlay/releases/download/v${S6_VERSION}/s6-overlay-amd64.tar.gz && \ + wget -N -P /opt/downloads https://github.com/just-containers/s6-overlay/releases/download/v${S6_VERSION}/s6-overlay-amd64.tar.gz.sig && \ + gpg --keyserver hkp://keys.gnupg.net:80 --recv-key 2536CA16DF4FCDA2 && \ + gpg /opt/downloads/s6-overlay-amd64.tar.gz.sig && \ + wget -N -P /opt/downloads https://github.com/kelseyhightower/confd/releases/download/v${CONFD_VERSION}/confd-${CONFD_VERSION}-linux-amd64 && \ + sha256sum /opt/downloads/confd-${CONFD_VERSION}-linux-amd64 | cut -f1 -d' ' | xargs test ${CONFD_SHA256} == && \ + tar -xzf /opt/downloads/s6-overlay-amd64.tar.gz -C / && \ + cp /opt/downloads/confd-${CONFD_VERSION}-linux-amd64 /usr/local/bin/confd && \ + chmod a+x /usr/local/bin/confd && \ + echo '' > /root/.ash_history + +# Start s6 +ENTRYPOINT [ "/init" ] + +LABEL License="MIT License" + +# https://github.com/just-containers/s6-overlay#customizing-s6-behaviour +ENV S6_LOGGING=0 \ + S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \ + TERM=xterm + +COPY rootfs / diff --git a/base/README.md b/base/README.md new file mode 100644 index 00000000..374af3b4 --- /dev/null +++ b/base/README.md @@ -0,0 +1,31 @@ +# Base + +Base Docker image from which almost all others are derived. It is not meant to +be run on its own. + +It's based off off [Alpine Linux], and includes [s6 overlay] and [confd]. + +## Dependencies + +Requires `alpine:3.11.6` + +## Settings + +| Environment Variable | Default | Description | +| :---------------------- | :------ | :--------------------------------------- | +| ETCD_HOST | etcd | The host where etcd, can be found | +| ETCD_HOST_PORT | 2379 | The port where etcd can be accessed | +| ETCD_CONNECTION_TIMEOUT | 0 | Timeout to wait for a connection to etcd | + +If [etcd] cannot be reached the container will use environment variables as a +[backend] for [confd]. The timeout is set to `0` by default to ensure containers +start quickly in a development environment where etcd is not running. + +Users do not require [etcd] to run the containers, environment variables can be +used instead for simplicity. + +[Alpine Linux]: https://alpinelinux.org +[backend]: https://github.com/kelseyhightower/confd/blob/34a6ce8897ab3bde10f49c30c815fe496d592860/docs/configuration-guide.md +[confd]: https://github.com/kelseyhightower/confd +[etcd]: https://github.com/etcd-io/etcd +[s6 overlay]: https://github.com/just-containers/s6-overlay diff --git a/base/rootfs/etc/cleanup.d/empty-ash-history.sh b/base/rootfs/etc/cleanup.d/empty-ash-history.sh new file mode 100755 index 00000000..5da7ff59 --- /dev/null +++ b/base/rootfs/etc/cleanup.d/empty-ash-history.sh @@ -0,0 +1,2 @@ +#!/bin/sh +echo '' > /root/.ash_history diff --git a/base/rootfs/etc/cleanup.d/empty-bash-history.sh b/base/rootfs/etc/cleanup.d/empty-bash-history.sh new file mode 100755 index 00000000..290e95f7 --- /dev/null +++ b/base/rootfs/etc/cleanup.d/empty-bash-history.sh @@ -0,0 +1,2 @@ +#!/bin/sh +echo '' > /root/.bash_history diff --git a/base/rootfs/etc/cleanup.d/empty-tmp.sh b/base/rootfs/etc/cleanup.d/empty-tmp.sh new file mode 100755 index 00000000..31c594e8 --- /dev/null +++ b/base/rootfs/etc/cleanup.d/empty-tmp.sh @@ -0,0 +1,2 @@ +#!/bin/sh +rm -rf /tmp/* diff --git a/base/rootfs/etc/confd/conf.d/.gitignore b/base/rootfs/etc/confd/conf.d/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/base/rootfs/etc/confd/conf.d/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/base/rootfs/etc/confd/confd.toml b/base/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..c1565b39 --- /dev/null +++ b/base/rootfs/etc/confd/confd.toml @@ -0,0 +1,5 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false diff --git a/base/rootfs/etc/confd/templates/.gitignore b/base/rootfs/etc/confd/templates/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/base/rootfs/etc/confd/templates/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/base/rootfs/etc/cont-finish.d/.gitignore b/base/rootfs/etc/cont-finish.d/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/base/rootfs/etc/cont-finish.d/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/base/rootfs/etc/cont-init.d/01-confd-render-templates.sh b/base/rootfs/etc/cont-init.d/01-confd-render-templates.sh new file mode 100644 index 00000000..013f066e --- /dev/null +++ b/base/rootfs/etc/cont-init.d/01-confd-render-templates.sh @@ -0,0 +1,17 @@ +#!/usr/bin/with-contenv bash +set -e + +readonly ETCD_HOST=${ETCD_HOST:-etcd} +readonly ETCD_HOST_PORT=${ETCD_HOST_PORT:-2379} +readonly ETCD_CONNECTION_TIMEOUT=${ETCD_CONNECTION_TIMEOUT:-0} +readonly CONFD_LOG_LEVEL=${CONFD_LOG_LEVEL:-error} +readonly CONFD_POLLING_INTERVAL=${CONFD_POLLING_INTERVAL:-30} + +echo "Looking for etcd server... http://${ETCD_HOST}:${ETCD_HOST_PORT}" +if timeout ${ETCD_CONNECTION_TIMEOUT} wait-for-open-port.sh ${ETCD_HOST} ${ETCD_HOST_PORT} &> /dev/null; then + echo "Found etcd server..." + confd -onetime -sync-only -log-level ${CONFD_LOG_LEVEL} -backend etcdv3 -node ${ETCD_HOST}:${ETCD_HOST_PORT} +else + echo "Timeout exceeded using env backend..." + confd -onetime -sync-only -log-level ${CONFD_LOG_LEVEL} -backend env +fi diff --git a/base/rootfs/etc/fix-attrs.d/.gitignore b/base/rootfs/etc/fix-attrs.d/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/base/rootfs/etc/fix-attrs.d/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/base/rootfs/etc/services.d/.gitignore b/base/rootfs/etc/services.d/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/base/rootfs/etc/services.d/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/base/rootfs/opt/.gitignore b/base/rootfs/opt/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/base/rootfs/opt/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/base/rootfs/run/islandora/.gitignore b/base/rootfs/run/islandora/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/base/rootfs/run/islandora/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/base/rootfs/sbin/apk-install.sh b/base/rootfs/sbin/apk-install.sh new file mode 100755 index 00000000..504bbbef --- /dev/null +++ b/base/rootfs/sbin/apk-install.sh @@ -0,0 +1,2 @@ +#!/bin/sh +apk add --update "$@" diff --git a/base/rootfs/sbin/apk-uninstall.sh b/base/rootfs/sbin/apk-uninstall.sh new file mode 100755 index 00000000..223e0374 --- /dev/null +++ b/base/rootfs/sbin/apk-uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/sh +apk del --purge --update "$@" diff --git a/base/rootfs/usr/local/bin/cleanup.sh b/base/rootfs/usr/local/bin/cleanup.sh new file mode 100755 index 00000000..ba844e5f --- /dev/null +++ b/base/rootfs/usr/local/bin/cleanup.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +set -e + +readonly PROGNAME=$(basename $0) +readonly ARGS="$@" + +function usage { + cat <<- EOF + usage: $PROGNAME + + Runs all the scripts in /etc/cleanup.d + + OPTIONS: + -h --help Show this help. + -x --debug Debug this script. + + Examples: + Clone repository: + $PROGNAME +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + while getopts "hx" OPTION + do + case $OPTION in + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + return 0 +} + +function main { + cmdline ${ARGS} + for file in /etc/cleanup.d/*; do + $file + done +} +main diff --git a/base/rootfs/usr/local/bin/git-clone-cached.sh b/base/rootfs/usr/local/bin/git-clone-cached.sh new file mode 100755 index 00000000..62356eff --- /dev/null +++ b/base/rootfs/usr/local/bin/git-clone-cached.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +set -e + +readonly PROGNAME=$(basename $0) +readonly ARGS="$@" + +readonly DOWNLOAD_CACHE_DIRECTORY=/opt/downloads + +function usage { + cat <<- EOF + usage: $PROGNAME options [FILE]... + + Does a git clone utilizing the Buildkit caching mechanism. + + OPTIONS: + -u --url The URL of the repository to clone. + -c --commit The commit hash or tag to checkout. + -w --worktree The directory to checkout the repository into. + -s --strip Remove the git repo as well as any files passed as parameters to save space. + -h --help Show this help. + -x --debug Debug this script. + + Examples: + Clone repository: + $PROGNAME \\ + --url https://github.com/Islandora-CLAW/Alpaca.git \\ + --commit "${COMMIT}" \\ + --worktree /opt/alpaca +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --url) args="${args}-u ";; + --commit) args="${args}-c ";; + --worktree) args="${args}-w ";; + --strip) args="${args}-s ";; + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + while getopts "u:c:w:shx" OPTION + do + case $OPTION in + u) + readonly URL=${OPTARG} + ;; + c) + readonly COMMIT=${OPTARG} + ;; + w) + readonly WORKTREE=${OPTARG} + ;; + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + if [[ -z $URL || -z $COMMIT || -z $WORKTREE ]]; then + echo "Missing one or more required options: --url --commit --worktree" + exit 1 + fi + + # All remaning parameters are files to be removed from the repo if --strip was specified. + shift $((OPTIND-1)) + readonly REMOVE=("$@") + + return 0 +} + +function main { + cmdline ${ARGS} + local repo=$(basename ${WORKTREE}) + git clone --mirror ${URL} ${DOWNLOAD_CACHE_DIRECTORY}/${repo} || true + git clone ${DOWNLOAD_CACHE_DIRECTORY}/${repo} ${WORKTREE} + git -C ${WORKTREE} fetch --all + git -C ${WORKTREE} reset --hard ${COMMIT} + if [[ -z $STRIP ]]; then + rm -fr ${WORKTREE}/.git + for i in "${REMOVE[@]}"; do + rm -fr "${WORKTREE}/${i}" + done + fi +} +main diff --git a/base/rootfs/usr/local/bin/wait-for-mysql.sh b/base/rootfs/usr/local/bin/wait-for-mysql.sh new file mode 100755 index 00000000..ea1c8959 --- /dev/null +++ b/base/rootfs/usr/local/bin/wait-for-mysql.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +set -e + +readonly PROGNAME=$(basename $0) +readonly ARGS="$@" + +function usage { + cat <<- EOF + usage: $PROGNAME options + + Waits for an connection to an mysql database as the given user, or until the + timeout is exceeded. + + Exits non-zero if not successful. + + OPTIONS: + -H --host The URL of the repository to clone. + -P --port The commit hash or tag to checkout. + -u --user The directory to checkout the repository into. + -p --password Remove the git repo as well as any files passed as parameters to save space. + -t --timeout Time to wait for a connection to the database, defaults to 60 seconds. + -h --help Show this help. + -x --debug Debug this script. + + Examples: + Check if database is acccessible: + $PROGNAME \\ + --host mariadb \\ + --port 3306 \\ + --user root \\ + --password password +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --host) args="${args}-H ";; + --port) args="${args}-P ";; + --user) args="${args}-u ";; + --password) args="${args}-p ";; + --timeout) args="${args}-t ";; + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + while getopts "H:P:u:p:thx" OPTION + do + case $OPTION in + H) + readonly DB_HOST=${OPTARG} + ;; + P) + readonly DB_PORT=${OPTARG} + ;; + u) + readonly DB_USER=${OPTARG} + ;; + p) + readonly DB_PASSWORD=${OPTARG} + ;; + t) + readonly TIMEOUT=${OPTARG} + ;; + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + if [[ -z $DB_HOST || -z $DB_PORT || -z $DB_USER || -x $DB_PASSWORD ]]; then + echo "Missing one or more required options: --host --port --user --password" + exit 1 + fi + + return 0 +} + +function main { + cmdline ${ARGS} + local duration=${TIMEOUT:-60} + echo "Waiting for up to ${duration} seconds to connect to Database ${DB_HOST}:${DB_PORT}" + if timeout ${duration} wait-for-open-port.sh ${DB_HOST} ${DB_PORT}; then + echo "Database found" + else + exit 1 + fi + echo "Validating Database credentials for ${DB_USER}" + if mysqladmin -s --user=${DB_USER} --password=${DB_PASSWORD} --host=${DB_HOST} --port=${DB_PORT} --protocol=tcp ping; then + echo "Credentials are valid" + exit 0 + else + echo "Credentials are invalid" + exit 1 + fi +} +main diff --git a/base/rootfs/usr/local/bin/wait-for-open-port.sh b/base/rootfs/usr/local/bin/wait-for-open-port.sh new file mode 100755 index 00000000..fece693a --- /dev/null +++ b/base/rootfs/usr/local/bin/wait-for-open-port.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +set -e + +readonly PROGNAME=$(basename $0) +readonly ARGS="$@" + +function usage { + cat <<- EOF + usage: $PROGNAME HOST PORT + + Waits for the given PORT to be open on HOST, re-checks every second. + + Use in conjunction with timeout. + + OPTIONS: + -h --help Show this help. + -x --debug Debug this script. + + Examples: + Check if database is acccessible: + timeout 10 $PROGNAME database 3306 +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + while getopts "hx" OPTION + do + case $OPTION in + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + shift $((OPTIND-1)) + + if [ "$#" -ne 2 ]; then + echo "Illegal number of parameters" + usage + return 1 + fi + + readonly HOST=${1}; shift + readonly PORT=${1} + + return 0 +} + +function main { + cmdline ${ARGS} + echo "Waiting for ${PORT} on ${HOST} to open." + while ! nc -z -w5 $HOST $PORT &> /dev/null; do + sleep 1 + done + exit 0; +} +main diff --git a/blazegraph/.dockerignore b/blazegraph/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/blazegraph/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/blazegraph/Dockerfile b/blazegraph/Dockerfile new file mode 100644 index 00000000..27bfc93b --- /dev/null +++ b/blazegraph/Dockerfile @@ -0,0 +1,17 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/tomcat:latest + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + BLAZEGRAPH_VERSION=CANDIDATE_2_1_5 && \ + install-war-into-tomcat.sh \ + --name "bigdata" \ + --url "https://github.com/blazegraph/database/releases/download/BLAZEGRAPH_RELEASE_${BLAZEGRAPH_VERSION}/blazegraph.war" \ + --key "b22f1a1aa8e536443db9a57da63720813374ef59e4021cfa9ad0e98f9a420e85" + +COPY rootfs / + +RUN mkdir /data && \ + chown tomcat:tomcat /data && \ + chown -R tomcat:tomcat /opt/tomcat + +VOLUME /data diff --git a/blazegraph/README.md b/blazegraph/README.md new file mode 100644 index 00000000..85a71bfe --- /dev/null +++ b/blazegraph/README.md @@ -0,0 +1,37 @@ +# Blazegraph + +Docker image for [Blazegraph] version 2.1.5. + +Please refer to the [Blazegraph Documentation] for more in-depth information. + +As a quick example this will bring up an instance of [Blazegraph], and allow you +to view on . + +```bash +docker run --rm -ti -p 80:80 islandora/blazegraph +``` + +## Dependencies + +Requires `islandora/tomcat` docker image to build. Please refer to the +[Tomcat Image README](../tomcat/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Volumes + +| Path | Description | +| :---- | :--------------------------- | +| /data | Location of the backing file | + +## Logs + +| Path | Description | +| :--------------------------------- | :---------- | +| /opt/tomcat/logs/rules.log | | +| /opt/tomcat/logs/queryLog.csv | | +| /opt/tomcat/logs/queryRunState.log | | +| /opt/tomcat/logs/solutions.csv | | +| /opt/tomcat/logs/sparql.txt | | + +[Blazegraph Documentation]: https://github.com/blazegraph/database/wiki/About_Blazegraph +[Blazegraph]: https://blazegraph.com/ diff --git a/blazegraph/rootfs/etc/confd/conf.d/RWStore.properties.toml b/blazegraph/rootfs/etc/confd/conf.d/RWStore.properties.toml new file mode 100644 index 00000000..6a68991a --- /dev/null +++ b/blazegraph/rootfs/etc/confd/conf.d/RWStore.properties.toml @@ -0,0 +1,6 @@ +[template] +src = "RWStore.properties.tmpl" +dest = "/opt/tomcat/webapps/bigdata/WEB-INF/classes/RWStore.properties" +uid = 100 +gid = 1000 +keys = ["/"] diff --git a/blazegraph/rootfs/etc/confd/conf.d/log4j.properties.toml b/blazegraph/rootfs/etc/confd/conf.d/log4j.properties.toml new file mode 100644 index 00000000..0a634d63 --- /dev/null +++ b/blazegraph/rootfs/etc/confd/conf.d/log4j.properties.toml @@ -0,0 +1,6 @@ +[template] +src = "log4j.properties.tmpl" +dest = "/opt/tomcat/webapps/bigdata/WEB-INF/classes/log4j.properties" +uid = 100 +gid = 1000 +keys = ["/"] diff --git a/blazegraph/rootfs/etc/confd/confd.toml b/blazegraph/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..e6508b2d --- /dev/null +++ b/blazegraph/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/blazegraph" diff --git a/blazegraph/rootfs/etc/confd/templates/RWStore.properties.tmpl b/blazegraph/rootfs/etc/confd/templates/RWStore.properties.tmpl new file mode 100644 index 00000000..7303205d --- /dev/null +++ b/blazegraph/rootfs/etc/confd/templates/RWStore.properties.tmpl @@ -0,0 +1,49 @@ +# +# Note: These options are applied when the journal and the triple store are +# first created. + +## +## Journal options. +## + +# The backing file. This contains all your data. You want to put this someplace +# safe. The default locator will wind up in the directory from which you start +# your servlet container. +com.bigdata.journal.AbstractJournal.file=/data/bigdata.jnl + +# The persistence engine. Use 'Disk' for the WORM or 'DiskRW' for the RWStore. +com.bigdata.journal.AbstractJournal.bufferMode=DiskRW + +# Setup for the RWStore recycler rather than session protection. +com.bigdata.service.AbstractTransactionService.minReleaseAge=1 + +# Enable group commit. See http://wiki.blazegraph.com/wiki/index.php/GroupCommit and BLZG-192. +#com.bigdata.journal.Journal.groupCommit=true + +com.bigdata.btree.writeRetentionQueue.capacity=4000 +com.bigdata.btree.BTree.branchingFactor=128 + +# 200M initial extent. +com.bigdata.journal.AbstractJournal.initialExtent=209715200 +com.bigdata.journal.AbstractJournal.maximumExtent=209715200 + +## +## Setup for QUADS mode without the full text index. +## +com.bigdata.rdf.sail.truthMaintenance=false +com.bigdata.rdf.store.AbstractTripleStore.quads=true +com.bigdata.rdf.store.AbstractTripleStore.statementIdentifiers=false +com.bigdata.rdf.store.AbstractTripleStore.textIndex=false +com.bigdata.rdf.store.AbstractTripleStore.axiomsClass=com.bigdata.rdf.axioms.NoAxioms + +# Bump up the branching factor for the lexicon indices on the default kb. +com.bigdata.namespace.kb.lex.com.bigdata.btree.BTree.branchingFactor=400 + +# Bump up the branching factor for the statement indices on the default kb. +com.bigdata.namespace.kb.spo.com.bigdata.btree.BTree.branchingFactor=1024 + +# Uncomment to enable collection of OS level performance counters. When +# collected they will be self-reported through the /counters servlet and +# the workbench "Performance" tab. +# +com.bigdata.journal.Journal.collectPlatformStatistics=false diff --git a/blazegraph/rootfs/etc/confd/templates/log4j.properties.tmpl b/blazegraph/rootfs/etc/confd/templates/log4j.properties.tmpl new file mode 100644 index 00000000..4f5c76f0 --- /dev/null +++ b/blazegraph/rootfs/etc/confd/templates/log4j.properties.tmpl @@ -0,0 +1,110 @@ +# Default log4j configuration. See the individual classes for the +# specific loggers, but generally they are named for the class in +# which they are defined. + +# Default log4j configuration for testing purposes. +# +# You probably want to set the default log level to ERROR. +# +log4j.rootCategory=WARN, dest1 +#log4j.rootCategory=WARN, dest2 + +# Loggers. +# Note: logging here at INFO or DEBUG will significantly impact throughput! +log4j.logger.com.bigdata=WARN +log4j.logger.com.bigdata.btree=WARN + +# Normal data loader (single threaded). +#log4j.logger.com.bigdata.rdf.store.DataLoader=INFO + +# dest1 +log4j.appender.dest1=org.apache.log4j.ConsoleAppender +log4j.appender.dest1.layout=org.apache.log4j.PatternLayout +log4j.appender.dest1.layout.ConversionPattern=%-5p: %F:%L: %m%n + +# dest2 includes the thread name and elapsed milliseconds. +# Note: %r is elapsed milliseconds. +# Note: %t is the thread name. +# See http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html +log4j.appender.dest2=org.apache.log4j.ConsoleAppender +log4j.appender.dest2.layout=org.apache.log4j.PatternLayout +log4j.appender.dest2.layout.ConversionPattern=%-5p: %r %X{hostname} %X{serviceUUID} %X{taskname} %X{timestamp} %X{resources} %t %l: %m%n + +## +# Rule execution log. This is a formatted log file (comma delimited). +log4j.logger.com.bigdata.relation.rule.eval.RuleLog=INFO,ruleLog +log4j.additivity.com.bigdata.relation.rule.eval.RuleLog=false +log4j.appender.ruleLog=org.apache.log4j.FileAppender +log4j.appender.ruleLog.Threshold=ALL +log4j.appender.ruleLog.File=/opt/tomcat/logs/rules.log +log4j.appender.ruleLog.Append=true +# I find that it is nicer to have this unbuffered since you can see what +# is going on and to make sure that I have complete rule evaluation logs +# on shutdown. +log4j.appender.ruleLog.BufferedIO=false +log4j.appender.ruleLog.layout=org.apache.log4j.PatternLayout +log4j.appender.ruleLog.layout.ConversionPattern=%m + +## +# Summary query evaluation log (tab delimited file). Uncomment the next line to enable. +#log4j.logger.com.bigdata.bop.engine.QueryLog=INFO,queryLog +log4j.additivity.com.bigdata.bop.engine.QueryLog=false +log4j.appender.queryLog=org.apache.log4j.FileAppender +log4j.appender.queryLog.Threshold=ALL +log4j.appender.queryLog.File=/opt/tomcat/logs/queryLog.csv +log4j.appender.queryLog.Append=true +# I find that it is nicer to have this unbuffered since you can see what +# is going on and to make sure that I have complete rule evaluation logs +# on shutdown. +log4j.appender.queryLog.BufferedIO=false +log4j.appender.queryLog.layout=org.apache.log4j.PatternLayout +log4j.appender.queryLog.layout.ConversionPattern=%m + +## +# BOp run state trace (tab delimited file). Uncomment the next line to enable. +#log4j.logger.com.bigdata.bop.engine.RunState$TableLog=INFO,queryRunStateLog +log4j.additivity.com.bigdata.bop.engine.RunState$TableLog=false +log4j.appender.queryRunStateLog=org.apache.log4j.FileAppender +log4j.appender.queryRunStateLog.Threshold=ALL +log4j.appender.queryRunStateLog.File=/opt/tomcat/logs/queryRunState.log +log4j.appender.queryRunStateLog.Append=true +# I find that it is nicer to have this unbuffered since you can see what +# is going on and to make sure that I have complete rule evaluation logs +# on shutdown. +log4j.appender.queryRunStateLog.BufferedIO=false +log4j.appender.queryRunStateLog.layout=org.apache.log4j.PatternLayout +log4j.appender.queryRunStateLog.layout.ConversionPattern=%m + +## +# Solutions trace (tab delimited file). Uncomment the next line to enable. +#log4j.logger.com.bigdata.bop.engine.SolutionsLog=INFO,solutionsLog +log4j.additivity.com.bigdata.bop.engine.SolutionsLog=false +log4j.appender.solutionsLog=org.apache.log4j.ConsoleAppender +#log4j.appender.solutionsLog=org.apache.log4j.FileAppender +log4j.appender.solutionsLog.Threshold=ALL +log4j.appender.solutionsLog.File=/opt/tomcat/logs/solutions.csv +log4j.appender.solutionsLog.Append=true +# I find that it is nicer to have this unbuffered since you can see what +# is going on and to make sure that I have complete rule evaluation logs +# on shutdown. +log4j.appender.solutionsLog.BufferedIO=false +log4j.appender.solutionsLog.layout=org.apache.log4j.PatternLayout +log4j.appender.solutionsLog.layout.ConversionPattern=SOLUTION:\t%m + +## +# SPARQL query trace (plain text file). Uncomment 2nd line to enable. +log4j.logger.com.bigdata.rdf.sparql.ast.eval.ASTEvalHelper=WARN +#log4j.logger.com.bigdata.rdf.sparql.ast.eval.ASTEvalHelper=INFO,sparqlLog +log4j.additivity.com.bigdata.rdf.sparql.ast.eval.ASTEvalHelper=false +log4j.appender.sparqlLog=org.apache.log4j.ConsoleAppender +#log4j.appender.sparqlLog=org.apache.log4j.FileAppender +log4j.appender.sparqlLog.Threshold=ALL +log4j.appender.sparqlLog.File=/opt/tomcat/logs/sparql.txt +log4j.appender.sparqlLog.Append=true +# I find that it is nicer to have this unbuffered since you can see what +# is going on and to make sure that I have complete rule evaluation logs +# on shutdown. +log4j.appender.sparqlLog.BufferedIO=false +log4j.appender.sparqlLog.layout=org.apache.log4j.PatternLayout +log4j.appender.sparqlLog.layout.ConversionPattern=#----------%d-----------tx=%X{tx}\n%m\n + diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..07ae98f8 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,117 @@ +import com.bmuschko.gradle.docker.tasks.image.DockerPushImage + +plugins { + id("com.bmuschko.docker-remote-api") version "6.4.0" +} + +extensions.findByName("buildScan")?.withGroovyBuilder { + setProperty("termsOfServiceUrl", "https://gradle.com/terms-of-service") + setProperty("termsOfServiceAgree", "yes") +} + +val repository: String by project + +subprojects { + // Make all build directories relative to the root, only supports projects up to a depth of one for now. + buildDir = rootProject.buildDir.resolve(projectDir.relativeTo(rootDir)) + layout.buildDirectory.set(buildDir) + + // If there is a docker file in the project add the appropriate tasks. + if (projectDir.resolve("Dockerfile").exists()) { + apply(plugin = "com.bmuschko.docker-remote-api") + val tags: String by project + val image = "$repository/$name" + + val imageTags = setOf( + "$image:latest", + "$image:${version}" + ) + tags.split(' ').filter { it.isNotEmpty() }.map { "$image:$it" } + + val buildDockerImage = tasks.register("build") { + group = "islandora" + description = "Creates Docker image." + images.addAll(imageTags) + inputDir.set(projectDir) + } + + tasks.register("push") { + images.set(buildDockerImage.map { it.images.get() }) + } + } +} + +// https://docs.docker.com/engine/reference/builder/#from +// FROM [--platform=] [AS ] +// FROM [--platform=] [:] [AS ] +// FROM [--platform=] [@] [AS ] +val extractProjectDependenciesFromDockerfileRegex = """FROM[ \t]+(:?--platform=[^ ]+[ \t]+)?islandora/([^ :@]+)""".toRegex() +subprojects { + tasks.withType { + val contents = inputDir.get().asFile.resolve("Dockerfile").readText() + // Extract the image name without the prefix 'islandora' it should match an existing project. + val matches = extractProjectDependenciesFromDockerfileRegex.findAll(contents) + + // If the project is found and it has a build task, link the dependency. + matches.forEach { + rootProject.findProject(it.groupValues[2]) + ?.tasks + ?.withType() + ?.first() + ?.let { buildTask -> + // If generated image id changes, rebuild. + inputs.file(buildTask.imageIdFile.asFile) + dependsOn(buildTask) + // This used to replace the FROM statements such that the referred to the Image ID rather + // than "latest". Though this is currently broken when BuildKit is enabled: + // https://github.com/moby/moby/issues/39769 + } + } + } +} + +//============================================================================= +// Helper functions. +//============================================================================= + +// Override the DockerBuildImage command to use the CLI since BuildKit is not supported in the java docker api. +// https://github.com/docker-java/docker-java/issues/1381 +open class DockerBuildImage : DefaultTask() { + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + val inputDir = project.objects.directoryProperty() + + @Input + @get:Optional + val buildArgs = project.objects.mapProperty() + + @Input + @get:Optional + val images = project.objects.setProperty() + + @OutputFile + val imageIdFile = project.objects.fileProperty() + + @Internal + val imageId = project.objects.property() + + init { + logging.captureStandardOutput(LogLevel.INFO) + logging.captureStandardError(LogLevel.ERROR) + imageIdFile.set(project.buildDir.resolve(".docker/${path.replace(":", "_")}-imageId.txt")) + } + + @TaskAction + fun exec() { + val command = mutableListOf("docker", "build") + command.addAll(buildArgs.get().flatMap { listOf("--build-arg", "${it.key}=${it.value}") }) + command.addAll(images.get().flatMap { listOf("--tag", it) }) + command.addAll(listOf("--iidfile", imageIdFile.get().asFile.absolutePath)) + command.add(".") + project.exec { + environment("DOCKER_BUILDKIT" to 1) + workingDir = inputDir.get().asFile + commandLine = command + } + imageId.set(imageIdFile.map { it.asFile.readText() }) + } +} diff --git a/cantaloupe/.dockerignore b/cantaloupe/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/cantaloupe/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/cantaloupe/Dockerfile b/cantaloupe/Dockerfile new file mode 100644 index 00000000..34cfb679 --- /dev/null +++ b/cantaloupe/Dockerfile @@ -0,0 +1,31 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/tomcat:latest + +# Opted for OpenJPG over Kakadu but that could be changed. +# Check 00-cantaloupe-setup-environment for the defaults. +# For reference see: https://cantaloupe-project.github.io/manual/3.3/processors.html +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + apk-install.sh \ + imagemagick \ + ffmpeg \ + openjpeg + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + CANTALOUPE_VERSION="3.3.1" && \ + wget -N -P /opt/downloads "https://github.com/medusa-project/cantaloupe/releases/download/v${CANTALOUPE_VERSION}/Cantaloupe-${CANTALOUPE_VERSION}.zip" && \ + unzip "/opt/downloads/Cantaloupe-${CANTALOUPE_VERSION}.zip" -d /tmp && \ + install-war-into-tomcat.sh \ + --name "cantaloupe" \ + --file "/tmp/Cantaloupe-${CANTALOUPE_VERSION}/Cantaloupe-${CANTALOUPE_VERSION}.war" \ + --key "a56593bae377b4db6b1e0cd5c8a9d62fdda41632f6295b5d4fe846e66c15768a" && \ + rm -fr /tmp/Cantaloupe-* + + +COPY rootfs / + +RUN mkdir /data && \ + chown tomcat:tomcat /data && \ + chown -R tomcat:tomcat /opt/tomcat + +VOLUME /data diff --git a/cantaloupe/README.md b/cantaloupe/README.md new file mode 100644 index 00000000..8dde76f6 --- /dev/null +++ b/cantaloupe/README.md @@ -0,0 +1,205 @@ +# Cantaloupe + +Docker image for [Cantaloupe] version 3.3.1. + +Please refer to the [Cantaloupe Documentation] for more in-depth information. + +As a quick example this will bring up an instance of [Cantaloupe], and allow you +to view on . + +```bash +docker run --rm -ti -p 80:80 islandora/cantaloupe +``` + +## Dependencies + +Requires `islandora/tomcat` docker image to build. Please refer to the +[Tomcat Image README](../tomcat/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Volumes + +| Path | Description | +| :---- | :------------------- | +| /data | [Cantaloupe Caching] | + +## Settings + +| Environment Variable | Etcd Key | Default | +| :------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------- | :--------------------------------------------------------- | +| CANTALOUPE_HTTP_ENABLED | /cantaloupe/http/enabled | true | +| CANTALOUPE_HTTP_HOST | /cantaloupe/http/host | 0.0.0.0 | +| CANTALOUPE_HTTP_PORT | /cantaloupe/http/port | 8182 | +| CANTALOUPE_HTTPS_ENABLED | /cantaloupe/https/enabled | false | +| CANTALOUPE_HTTPS_HOST | /cantaloupe/https/host | 0.0.0.0 | +| CANTALOUPE_HTTPS_PORT | /cantaloupe/https/port | 8183 | +| CANTALOUPE_HTTPS_KEY_STORE_TYPE | /cantaloupe/https/key/store/type | JKS | +| CANTALOUPE_HTTPS_KEY_STORE_PASSWORD | /cantaloupe/https/key/store/password | password | +| CANTALOUPE_HTTPS_KEY_STORE_PATH | /cantaloupe/https/key/store/path | /path/to/keystore.jks | +| CANTALOUPE_HTTPS_KEY_PASSWORD | /cantaloupe/https/key/password | password | +| CANTALOUPE_AUTH_BASIC_ENABLED | /cantaloupe/auth/basic/enabled | false | +| CANTALOUPE_AUTH_BASIC_USERNAME | /cantaloupe/auth/basic/username | admin | +| CANTALOUPE_AUTH_BASIC_SECRET | /cantaloupe/auth/basic/secret | password | +| CANTALOUPE_ADMIN_ENABLED | /cantaloupe/admin/enabled | true | +| CANTALOUPE_ADMIN_PASSWORD | /cantaloupe/admin/password | password | +| CANTALOUPE_BASE_URI | /cantaloupe/base/uri | | +| CANTALOUPE_SLASH_SUBSTITUTE | /cantaloupe/slash/substitute | | +| CANTALOUPE_MAX_PIXELS | /cantaloupe/max/pixels | 400000000 | +| CANTALOUPE_PRINT_STACK_TRACE_ON_ERROR_PAGES | /cantaloupe/print/stack/trace/on/error/pages | true | +| CANTALOUPE_DELEGATE_SCRIPT_ENABLED | /cantaloupe/delegate/script/enabled | false | +| CANTALOUPE_DELEGATE_SCRIPT_PATHNAME | /cantaloupe/delegate/script/pathname | delegates.rb | +| CANTALOUPE_DELEGATE_SCRIPT_CACHE_ENABLED | /cantaloupe/delegate/script/cache/enabled | false | +| CANTALOUPE_ENDPOINT_IIIF_1_ENABLED | /cantaloupe/endpoint/iiif/1/enabled | true | +| CANTALOUPE_ENDPOINT_IIIF_2_ENABLED | /cantaloupe/endpoint/iiif/2/enabled | true | +| CANTALOUPE_ENDPOINT_IIIF_CONTENT_DISPOSITION | /cantaloupe/endpoint/iiif/content/disposition | inline | +| CANTALOUPE_ENDPOINT_IIIF_MIN_TILE_SIZE | /cantaloupe/endpoint/iiif/min/tile/size | 1024 | +| CANTALOUPE_ENDPOINT_IIIF_2_RESTRICT_TO_SIZES | /cantaloupe/endpoint/iiif/2/restrict/to/sizes | false | +| CANTALOUPE_ENDPOINT_API_ENABLED | /cantaloupe/endpoint/api/enabled | false | +| CANTALOUPE_ENDPOINT_API_USERNAME | /cantaloupe/endpoint/api/username | | +| CANTALOUPE_ENDPOINT_API_SECRET | /cantaloupe/endpoint/api/secret | | +| CANTALOUPE_RESOLVER_STATIC | /cantaloupe/resolver/static | HttpResolver | +| CANTALOUPE_RESOLVER_DELEGATE | /cantaloupe/resolver/delegate | false | +| CANTALOUPE_FILESYSTEMRESOLVER_LOOKUP_STRATEGY | /cantaloupe/filesystemresolver/lookup/strategy | BasicLookupStrategy | +| CANTALOUPE_FILESYSTEMRESOLVER_BASICLOOKUPSTRATEGY_PATH_PREFIX | /cantaloupe/filesystemresolver/basiclookupstrategy/path/prefix | /var/www/drupal/web/ | +| CANTALOUPE_FILESYSTEMRESOLVER_BASICLOOKUPSTRATEGY_PATH_SUFFIX | /cantaloupe/filesystemresolver/basiclookupstrategy/path/suffix | | +| CANTALOUPE_HTTPRESOLVER_LOOKUP_STRATEGY | /cantaloupe/httpresolver/lookup/strategy | BasicLookupStrategy | +| CANTALOUPE_HTTPRESOLVER_BASICLOOKUPSTRATEGY_URL_PREFIX | /cantaloupe/httpresolver/basiclookupstrategy/url/prefix | | +| CANTALOUPE_HTTPRESOLVER_BASICLOOKUPSTRATEGY_URL_SUFFIX | /cantaloupe/httpresolver/basiclookupstrategy/url/suffix | | +| CANTALOUPE_HTTPRESOLVER_AUTH_BASIC_USERNAME | /cantaloupe/httpresolver/auth/basic/username | | +| CANTALOUPE_HTTPRESOLVER_AUTH_BASIC_SECRET | /cantaloupe/httpresolver/auth/basic/secret | | +| CANTALOUPE_JDBCRESOLVER_URL | /cantaloupe/jdbcresolver/url | jdbc:postgresql://database:5432/cantaloupe | +| CANTALOUPE_JDBCRESOLVER_USER | /cantaloupe/jdbcresolver/user | admin | +| CANTALOUPE_JDBCRESOLVER_PASSWORD | /cantaloupe/jdbcresolver/password | password | +| CANTALOUPE_JDBCRESOLVER_CONNECTION_TIMEOUT | /cantaloupe/jdbcresolver/connection/timeout | 10 | +| CANTALOUPE_AMAZONS3RESOLVER_ACCESS_KEY_ID | /cantaloupe/amazons3resolver/access/key/id | | +| CANTALOUPE_AMAZONS3RESOLVER_SECRET_KEY | /cantaloupe/amazons3resolver/secret/key | | +| CANTALOUPE_AMAZONS3RESOLVER_BUCKET_NAME | /cantaloupe/amazons3resolver/bucket/name | | +| CANTALOUPE_AMAZONS3RESOLVER_BUCKET_REGION | /cantaloupe/amazons3resolver/bucket/region | | +| CANTALOUPE_AMAZONS3RESOLVER_LOOKUP_STRATEGY | /cantaloupe/amazons3resolver/lookup/strategy | BasicLookupStrategy | +| CANTALOUPE_AZURESTORAGERESOLVER_ACCOUNT_NAME | /cantaloupe/azurestorageresolver/account/name | | +| CANTALOUPE_AZURESTORAGERESOLVER_ACCOUNT_KEY | /cantaloupe/azurestorageresolver/account/key | | +| CANTALOUPE_AZURESTORAGERESOLVER_CONTAINER_NAME | /cantaloupe/azurestorageresolver/container/name | | +| CANTALOUPE_AZURESTORAGERESOLVER_LOOKUP_STRATEGY | /cantaloupe/azurestorageresolver/lookup/strategy | BasicLookupStrategy | +| CANTALOUPE_PROCESSOR_AVI | /cantaloupe/processor/avi | FfmpegProcessor | +| CANTALOUPE_PROCESSOR_BMP | /cantaloupe/processor/bmp | ImageMagickProcessor | +| CANTALOUPE_PROCESSOR_DCM | /cantaloupe/processor/dcm | ImageMagickProcessor | +| CANTALOUPE_PROCESSOR_GIF | /cantaloupe/processor/gif | ImageMagickProcessor | +| CANTALOUPE_PROCESSOR_JP2 | /cantaloupe/processor/jp2 | OpenJpegProcessor | +| CANTALOUPE_PROCESSOR_JPG | /cantaloupe/processor/jpg | ImageMagickProcessor | +| CANTALOUPE_PROCESSOR_MOV | /cantaloupe/processor/mov | FfmpegProcessor | +| CANTALOUPE_PROCESSOR_MP4 | /cantaloupe/processor/mp4 | FfmpegProcessor | +| CANTALOUPE_PROCESSOR_MPG | /cantaloupe/processor/mpg | FfmpegProcessor | +| CANTALOUPE_PROCESSOR_PDF | /cantaloupe/processor/pdf | ImageMagickProcessor | +| CANTALOUPE_PROCESSOR_PNG | /cantaloupe/processor/png | ImageMagickProcessor | +| CANTALOUPE_PROCESSOR_TIF | /cantaloupe/processor/tif | ImageMagickProcessor | +| CANTALOUPE_PROCESSOR_WEBM | /cantaloupe/processor/webm | FfmpegProcessor | +| CANTALOUPE_PROCESSOR_WEBP | /cantaloupe/processor/webp | ImageMagickProcessor | +| CANTALOUPE_PROCESSOR_FALLBACK | /cantaloupe/processor/fallback | Java2dProcessor | +| CANTALOUPE_PROCESSOR_NORMALIZE | /cantaloupe/processor/normalize | false | +| CANTALOUPE_PROCESSOR_BACKGROUND_COLOR | /cantaloupe/processor/background/color | black | +| CANTALOUPE_PROCESSOR_DOWNSCALE_FILTER | /cantaloupe/processor/downscale/filter | bicubic | +| CANTALOUPE_PROCESSOR_UPSCALE_FILTER | /cantaloupe/processor/upscale/filter | bicubic | +| CANTALOUPE_PROCESSOR_SHARPEN | /cantaloupe/processor/sharpen | 0 | +| CANTALOUPE_PROCESSOR_JPG_PROGRESSIVE | /cantaloupe/processor/jpg/progressive | true | +| CANTALOUPE_PROCESSOR_JPG_QUALITY | /cantaloupe/processor/jpg/quality | 80 | +| CANTALOUPE_PROCESSOR_TIF_COMPRESSION | /cantaloupe/processor/tif/compression | LZW | +| CANTALOUPE_STREAMPROCESSOR_RETRIEVAL_STRATEGY | /cantaloupe/streamprocessor/retrieval/strategy | StreamStrategy | +| CANTALOUPE_FFMPEGPROCESSOR_PATH_TO_BINARIES | /cantaloupe/ffmpegprocessor/path/to/binaries | | +| CANTALOUPE_GRAPHICSMAGICKPROCESSOR_PATH_TO_BINARIES | /cantaloupe/graphicsmagickprocessor/path/to/binaries | | +| CANTALOUPE_IMAGEMAGICKPROCESSOR_PATH_TO_BINARIES | /cantaloupe/imagemagickprocessor/path/to/binaries | | +| CANTALOUPE_KAKADUPROCESSOR_PATH_TO_BINARIES | /cantaloupe/kakaduprocessor/path/to/binaries | | +| CANTALOUPE_OPENJPEGPROCESSOR_PATH_TO_BINARIES | /cantaloupe/openjpegprocessor/path/to/binaries | | +| CANTALOUPE_PDFBOXPROCESSOR_DPI | /cantaloupe/pdfboxprocessor/dpi | 150 | +| CANTALOUPE_CACHE_CLIENT_ENABLED | /cantaloupe/cache/client/enabled | true | +| CANTALOUPE_CACHE_CLIENT_MAX_AGE | /cantaloupe/cache/client/max/age | 2592000 | +| CANTALOUPE_CACHE_CLIENT_SHARED_MAX_AGE | /cantaloupe/cache/client/shared/max/age | | +| CANTALOUPE_CACHE_CLIENT_PUBLIC | /cantaloupe/cache/client/public | true | +| CANTALOUPE_CACHE_CLIENT_PRIVATE | /cantaloupe/cache/client/private | false | +| CANTALOUPE_CACHE_CLIENT_NO_CACHE | /cantaloupe/cache/client/no/cache | false | +| CANTALOUPE_CACHE_CLIENT_NO_STORE | /cantaloupe/cache/client/no/store | false | +| CANTALOUPE_CACHE_CLIENT_MUST_REVALIDATE | /cantaloupe/cache/client/must/revalidate | false | +| CANTALOUPE_CACHE_CLIENT_PROXY_REVALIDATE | /cantaloupe/cache/client/proxy/revalidate | false | +| CANTALOUPE_CACHE_CLIENT_NO_TRANSFORM | /cantaloupe/cache/client/no/transform | true | +| CANTALOUPE_CACHE_SOURCE | /cantaloupe/cache/source | FilesystemCache | +| CANTALOUPE_CACHE_DERIVATIVE | /cantaloupe/cache/derivative | FilesystemCache | +| CANTALOUPE_CACHE_SERVER_TTL_SECONDS | /cantaloupe/cache/server/ttl/seconds | 2592000 | +| CANTALOUPE_CACHE_SERVER_PURGE_MISSING | /cantaloupe/cache/server/purge/missing | false | +| CANTALOUPE_CACHE_SERVER_RESOLVE_FIRST | /cantaloupe/cache/server/resolve/first | false | +| CANTALOUPE_CACHE_SERVER_WORKER_ENABLED | /cantaloupe/cache/server/worker/enabled | false | +| CANTALOUPE_CACHE_SERVER_WORKER_INTERVAL | /cantaloupe/cache/server/worker/interval | 86400 | +| CANTALOUPE_FILESYSTEMCACHE_PATHNAME | /cantaloupe/filesystemcache/pathname | /data | +| CANTALOUPE_FILESYSTEMCACHE_DIR_DEPTH | /cantaloupe/filesystemcache/dir/depth | 3 | +| CANTALOUPE_FILESYSTEMCACHE_DIR_NAME_LENGTH | /cantaloupe/filesystemcache/dir/name/length | 2 | +| CANTALOUPE_JDBCCACHE_URL | /cantaloupe/jdbccache/url | jdbc:postgresql://database:5432/cantaloupe | +| CANTALOUPE_JDBCCACHE_USER | /cantaloupe/jdbccache/user | admin | +| CANTALOUPE_JDBCCACHE_PASSWORD | /cantaloupe/jdbccache/password | password | +| CANTALOUPE_JDBCCACHE_CONNECTION_TIMEOUT | /cantaloupe/jdbccache/connection/timeout | 10 | +| CANTALOUPE_JDBCCACHE_DERIVATIVE_IMAGE_TABLE | /cantaloupe/jdbccache/derivative/image/table | derivative_cache | +| CANTALOUPE_JDBCCACHE_INFO_TABLE | /cantaloupe/jdbccache/info/table | info_cache | +| CANTALOUPE_AMAZONS3CACHE_ACCESS_KEY_ID | /cantaloupe/amazons3cache/access/key/id | | +| CANTALOUPE_AMAZONS3CACHE_SECRET_KEY | /cantaloupe/amazons3cache/secret/key | | +| CANTALOUPE_AMAZONS3CACHE_BUCKET_NAME | /cantaloupe/amazons3cache/bucket/name | | +| CANTALOUPE_AMAZONS3CACHE_BUCKET_REGION | /cantaloupe/amazons3cache/bucket/region | | +| CANTALOUPE_AMAZONS3CACHE_OBJECT_KEY_PREFIX | /cantaloupe/amazons3cache/object/key/prefix | | +| CANTALOUPE_AZURESTORAGECACHE_ACCOUNT_NAME | /cantaloupe/azurestoragecache/account/name | | +| CANTALOUPE_AZURESTORAGECACHE_ACCOUNT_KEY | /cantaloupe/azurestoragecache/account/key | | +| CANTALOUPE_AZURESTORAGECACHE_CONTAINER_NAME | /cantaloupe/azurestoragecache/container/name | | +| CANTALOUPE_AZURESTORAGECACHE_OBJECT_KEY_PREFIX | /cantaloupe/azurestoragecache/object/key/prefix | | +| CANTALOUPE_OVERLAYS_ENABLED | /cantaloupe/overlays/enabled | false | +| CANTALOUPE_OVERLAYS_STRATEGY | /cantaloupe/overlays/strategy | BasicStrategy | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_TYPE | /cantaloupe/overlays/basicstrategy/type | image | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_IMAGE | /cantaloupe/overlays/basicstrategy/image | /path/to/overlay.png | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING | /cantaloupe/overlays/basicstrategy/string | Copyright. All rights reserved. | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING_FONT | /cantaloupe/overlays/basicstrategy/string/font | Helvetica | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING_FONT_SIZE | /cantaloupe/overlays/basicstrategy/string/font/size | 24 | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING_FONT_MIN_SIZE | /cantaloupe/overlays/basicstrategy/string/font/min/size | 18 | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING_FONT_WEIGHT | /cantaloupe/overlays/basicstrategy/string/font/weight | 1.0 | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING_GLYPH_SPACING | /cantaloupe/overlays/basicstrategy/string/glyph/spacing | 0.02 | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING_COLOR | /cantaloupe/overlays/basicstrategy/string/color | white | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING_STROKE_COLOR | /cantaloupe/overlays/basicstrategy/string/stroke/color | black | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING_STROKE_WIDTH | /cantaloupe/overlays/basicstrategy/string/stroke/width | 1 | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_STRING_BACKGROUND_COLOR | /cantaloupe/overlays/basicstrategy/string/background/color | rgba(0, 0, 0, 100) | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_POSITION | /cantaloupe/overlays/basicstrategy/position | bottom right | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_INSET | /cantaloupe/overlays/basicstrategy/inset | 10 | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_OUTPUT_WIDTH_THRESHOLD | /cantaloupe/overlays/basicstrategy/output/width/threshold | 400 | +| CANTALOUPE_OVERLAYS_BASICSTRATEGY_OUTPUT_HEIGHT_THRESHOLD | /cantaloupe/overlays/basicstrategy/output/height/threshold | 300 | +| CANTALOUPE_REDACTION_ENABLED | /cantaloupe/redaction/enabled | false | +| CANTALOUPE_METADATA_PRESERVE | /cantaloupe/metadata/preserve | false | +| CANTALOUPE_METADATA_RESPECT_ORIENTATION | /cantaloupe/metadata/respect/orientation | false | +| CANTALOUPE_LOG_APPLICATION_LEVEL | /cantaloupe/log/application/level | debug | +| CANTALOUPE_LOG_APPLICATION_CONSOLEAPPENDER_ENABLED | /cantaloupe/log/application/consoleappender/enabled | true | +| CANTALOUPE_LOG_APPLICATION_FILEAPPENDER_ENABLED | /cantaloupe/log/application/fileappender/enabled | false | +| CANTALOUPE_LOG_APPLICATION_FILEAPPENDER_PATHNAME | /cantaloupe/log/application/fileappender/pathname | /opt/tomcat/logs/cantaloupe.application.log | +| CANTALOUPE_LOG_APPLICATION_ROLLINGFILEAPPENDER_ENABLED | /cantaloupe/log/application/rollingfileappender/enabled | false | +| CANTALOUPE_LOG_APPLICATION_ROLLINGFILEAPPENDER_PATHNAME | /cantaloupe/log/application/rollingfileappender/pathname | /opt/tomcat/logs/cantaloupe.application.log | +| CANTALOUPE_LOG_APPLICATION_ROLLINGFILEAPPENDER_POLICY | /cantaloupe/log/application/rollingfileappender/policy | TimeBasedRollingPolicy | +| CANTALOUPE_LOG_APPLICATION_ROLLINGFILEAPPENDER_TIMEBASEDROLLINGPOLICY_FILENAME_PATTERN | /cantaloupe/log/application/rollingfileappender/timebasedrollingpolicy/filename/pattern | /opt/tomcat/logs/cantaloupe.application-%d{yyyy-MM-dd}.log | +| CANTALOUPE_LOG_APPLICATION_ROLLINGFILEAPPENDER_TIMEBASEDROLLINGPOLICY_MAX_HISTORY | /cantaloupe/log/application/rollingfileappender/timebasedrollingpolicy/max/history | 30 | +| CANTALOUPE_LOG_APPLICATION_SYSLOGAPPENDER_ENABLED | /cantaloupe/log/application/syslogappender/enabled | false | +| CANTALOUPE_LOG_APPLICATION_SYSLOGAPPENDER_HOST | /cantaloupe/log/application/syslogappender/host | | +| CANTALOUPE_LOG_APPLICATION_SYSLOGAPPENDER_PORT | /cantaloupe/log/application/syslogappender/port | 514 | +| CANTALOUPE_LOG_APPLICATION_SYSLOGAPPENDER_FACILITY | /cantaloupe/log/application/syslogappender/facility | LOCAL0 | +| CANTALOUPE_LOG_ACCESS_CONSOLEAPPENDER_ENABLED | /cantaloupe/log/access/consoleappender/enabled | false | +| CANTALOUPE_LOG_ACCESS_FILEAPPENDER_ENABLED | /cantaloupe/log/access/fileappender/enabled | true | +| CANTALOUPE_LOG_ACCESS_FILEAPPENDER_PATHNAME | /cantaloupe/log/access/fileappender/pathname | /opt/tomcat/logs/cantaloupe.access.log | +| CANTALOUPE_LOG_ACCESS_ROLLINGFILEAPPENDER_ENABLED | /cantaloupe/log/access/rollingfileappender/enabled | false | +| CANTALOUPE_LOG_ACCESS_ROLLINGFILEAPPENDER_PATHNAME | /cantaloupe/log/access/rollingfileappender/pathname | /opt/tomcat/logs/cantaloupe.access.log | +| CANTALOUPE_LOG_ACCESS_ROLLINGFILEAPPENDER_POLICY | /cantaloupe/log/access/rollingfileappender/policy | TimeBasedRollingPolicy | +| CANTALOUPE_LOG_ACCESS_ROLLINGFILEAPPENDER_TIMEBASEDROLLINGPOLICY_FILENAME_PATTERN | /cantaloupe/log/access/rollingfileappender/timebasedrollingpolicy/filename/pattern | /opt/tomcat/logs/cantaloupe.access-%d{yyyy-MM-dd}.log | +| CANTALOUPE_LOG_ACCESS_ROLLINGFILEAPPENDER_TIMEBASEDROLLINGPOLICY_MAX_HISTORY | /cantaloupe/log/access/rollingfileappender/timebasedrollingpolicy/max/history | 30 | +| CANTALOUPE_LOG_ACCESS_SYSLOGAPPENDER_ENABLED | /cantaloupe/log/access/syslogappender/enabled | false | +| CANTALOUPE_LOG_ACCESS_SYSLOGAPPENDER_HOST | /cantaloupe/log/access/syslogappender/host | | +| CANTALOUPE_LOG_ACCESS_SYSLOGAPPENDER_PORT | /cantaloupe/log/access/syslogappender/port | 514 | +| CANTALOUPE_LOG_ACCESS_SYSLOGAPPENDER_FACILITY | /cantaloupe/log/access/syslogappender/facility | LOCAL0 | + +## Logs + +| Path | Description | +| :------------------------------------------ | :---------------- | +| /opt/tomcat/logs/cantaloupe.access.log | [Cantaloupe Logs] | +| /opt/tomcat/logs/cantaloupe.application.log | [Cantaloupe Logs] | + +[Cantaloupe Caching]: https://cantaloupe-project.github.io/manual/3.1/caching.html +[Cantaloupe Documentation]: https://cantaloupe-project.github.io/manual/3.1/getting-started.html +[Cantaloupe Logs]: https://cantaloupe-project.github.io/manual/3.1/logging.html +[Cantaloupe]: https://cantaloupe-project.github.io/ diff --git a/cantaloupe/rootfs/etc/confd/conf.d/cataloupe.properties.toml b/cantaloupe/rootfs/etc/confd/conf.d/cataloupe.properties.toml new file mode 100644 index 00000000..65a5bda1 --- /dev/null +++ b/cantaloupe/rootfs/etc/confd/conf.d/cataloupe.properties.toml @@ -0,0 +1,7 @@ +[template] +src = "cantaloupe.properties.tmpl" +dest = "/opt/tomcat/conf/cantaloupe.properties" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/" ] diff --git a/cantaloupe/rootfs/etc/confd/conf.d/setenv.sh.toml b/cantaloupe/rootfs/etc/confd/conf.d/setenv.sh.toml new file mode 100644 index 00000000..7f0fbd2f --- /dev/null +++ b/cantaloupe/rootfs/etc/confd/conf.d/setenv.sh.toml @@ -0,0 +1,6 @@ +[template] +src = "setenv.sh.tmpl" +dest = "/opt/tomcat/bin/setenv.sh" +uid = 100 +gid = 1000 +keys = ["/java/opts", "/catalina/opts"] diff --git a/cantaloupe/rootfs/etc/confd/confd.toml b/cantaloupe/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..5ad14b76 --- /dev/null +++ b/cantaloupe/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/cantaloupe" diff --git a/cantaloupe/rootfs/etc/confd/templates/cantaloupe.properties.tmpl b/cantaloupe/rootfs/etc/confd/templates/cantaloupe.properties.tmpl new file mode 100644 index 00000000..cc370a1a --- /dev/null +++ b/cantaloupe/rootfs/etc/confd/templates/cantaloupe.properties.tmpl @@ -0,0 +1,311 @@ +########################################################################### +# GENERAL SETTINGS +########################################################################### + +http.enabled = {{ getv "/http/enabled" "true" }} +http.host = {{ getv "/http/host" "0.0.0.0" }} +http.port = {{ getv "/http/port" "8182" }} +https.enabled = {{ getv "/https/enabled" "false" }} +https.host = {{ getv "/https/host" "0.0.0.0" }} +https.port = {{ getv "/https/port" "8183" }} +https.key_store_type = {{ getv "/https/key/store/type" "JKS" }} +https.key_store_password = {{ getv "/https/key/store/password" "password" }} +https.key_store_path = {{ getv "/https/key/store/path" "/path/to/keystore.jks" }} +https.key_password = {{ getv "/https/key/password" "password" }} +auth.basic.enabled = {{ getv "/auth/basic/enabled" "false" }} +auth.basic.username = {{ getv "/auth/basic/username" "admin" }} +auth.basic.secret = {{ getv "/auth/basic/secret" "password" }} +admin.enabled = {{ getv "/admin/enabled" "true" }} +admin.password = {{ getv "/admin/password" "password" }} +base_uri = {{ getv "/base/uri" "" }} +slash_substitute = {{ getv "/slash/substitute" "" }} +max_pixels = {{ getv "/max/pixels" "400000000" }} +print_stack_trace_on_error_pages = {{ getv "/print/stack/trace/on/error/pages" "true" }} + +########################################################################### +# DELEGATE SCRIPT +########################################################################### + +delegate_script.enabled = {{ getv "/delegate/script/enabled" "false" }} +delegate_script.pathname = {{ getv "/delegate/script/pathname" "delegates.rb" }} +delegate_script.cache.enabled = {{ getv "/delegate/script/cache/enabled" "false" }} + +########################################################################### +# ENDPOINTS +########################################################################### + +endpoint.iiif.1.enabled = {{ getv "/endpoint/iiif/1/enabled" "true" }} +endpoint.iiif.2.enabled = {{ getv "/endpoint/iiif/2/enabled" "true" }} +endpoint.iiif.content_disposition = {{ getv "/endpoint/iiif/content/disposition" "inline" }} +endpoint.iiif.min_tile_size = {{ getv "/endpoint/iiif/min/tile/size" "1024" }} +endpoint.iiif.2.restrict_to_sizes = {{ getv "/endpoint/iiif/2/restrict/to/sizes" "false" }} +endpoint.api.enabled = {{ getv "/endpoint/api/enabled" "false" }} +endpoint.api.username = {{ getv "/endpoint/api/username" "" }} +endpoint.api.secret = {{ getv "/endpoint/api/secret" "" }} + +########################################################################### +# RESOLVERS +########################################################################### + +resolver.static = {{ getv "/resolver/static" "HttpResolver" }} +resolver.delegate = {{ getv "/resolver/delegate" "false" }} + +#---------------------------------------- +# FilesystemResolver +#---------------------------------------- + +FilesystemResolver.lookup_strategy = {{ getv "/filesystemresolver/lookup/strategy" "BasicLookupStrategy" }} +FilesystemResolver.BasicLookupStrategy.path_prefix = {{ getv "/filesystemresolver/basiclookupstrategy/path/prefix" "/var/www/drupal/web/" }} +FilesystemResolver.BasicLookupStrategy.path_suffix = {{ getv "/filesystemresolver/basiclookupstrategy/path/suffix" "" }} + +#---------------------------------------- +# HttpResolver +#---------------------------------------- + +HttpResolver.lookup_strategy = {{ getv "/httpresolver/lookup/strategy" "BasicLookupStrategy" }} +HttpResolver.BasicLookupStrategy.url_prefix = {{ getv "/httpresolver/basiclookupstrategy/url/prefix" "" }} +HttpResolver.BasicLookupStrategy.url_suffix = {{ getv "/httpresolver/basiclookupstrategy/url/suffix" "" }} +HttpResolver.auth.basic.username = {{ getv "/httpresolver/auth/basic/username" "" }} +HttpResolver.auth.basic.secret = {{ getv "/httpresolver/auth/basic/secret" "" }} + +#---------------------------------------- +# JdbcResolver +#---------------------------------------- + +JdbcResolver.url = {{ getv "/jdbcresolver/url" "jdbc:postgresql://database:5432/cantaloupe" }} +JdbcResolver.user = {{ getv "/jdbcresolver/user" "admin" }} +JdbcResolver.password = {{ getv "/jdbcresolver/password" "password" }} +JdbcResolver.connection_timeout = {{ getv "/jdbcresolver/connection/timeout" "10" }} + +#---------------------------------------- +# AmazonS3Resolver +#---------------------------------------- + +AmazonS3Resolver.access_key_id = {{ getv "/amazons3resolver/access/key/id" "" }} +AmazonS3Resolver.secret_key = {{ getv "/amazons3resolver/secret/key" "" }} +AmazonS3Resolver.bucket.name = {{ getv "/amazons3resolver/bucket/name" "" }} +AmazonS3Resolver.bucket.region = {{ getv "/amazons3resolver/bucket/region" "" }} +AmazonS3Resolver.lookup_strategy = {{ getv "/amazons3resolver/lookup/strategy" "BasicLookupStrategy" }} + +#---------------------------------------- +# AzureStorageResolver +#---------------------------------------- + +AzureStorageResolver.account_name = {{ getv "/azurestorageresolver/account/name" "" }} +AzureStorageResolver.account_key = {{ getv "/azurestorageresolver/account/key" "" }} +AzureStorageResolver.container_name = {{ getv "/azurestorageresolver/container/name" "" }} +AzureStorageResolver.lookup_strategy = {{ getv "/azurestorageresolver/lookup/strategy" "BasicLookupStrategy" }} + +########################################################################### +# PROCESSORS +########################################################################### + +#---------------------------------------- +# Processor Selection +#---------------------------------------- + +processor.avi = {{ getv "/processor/avi" "FfmpegProcessor" }} +processor.bmp = {{ getv "/processor/bmp" "ImageMagickProcessor" }} +processor.dcm = {{ getv "/processor/dcm" "ImageMagickProcessor" }} +processor.gif = {{ getv "/processor/gif" "ImageMagickProcessor" }} +processor.jp2 = {{ getv "/processor/jp2" "OpenJpegProcessor" }} +processor.jpg = {{ getv "/processor/jpg" "ImageMagickProcessor" }} +processor.mov = {{ getv "/processor/mov" "FfmpegProcessor" }} +processor.mp4 = {{ getv "/processor/mp4" "FfmpegProcessor" }} +processor.mpg = {{ getv "/processor/mpg" "FfmpegProcessor" }} +processor.pdf = {{ getv "/processor/pdf" "ImageMagickProcessor" }} +processor.png = {{ getv "/processor/png" "ImageMagickProcessor" }} +processor.tif = {{ getv "/processor/tif" "ImageMagickProcessor" }} +processor.webm = {{ getv "/processor/webm" "FfmpegProcessor" }} +processor.webp = {{ getv "/processor/webp" "ImageMagickProcessor" }} +processor.fallback = {{ getv "/processor/fallback" "Java2dProcessor" }} + +#---------------------------------------- +# Global Processor Configuration +#---------------------------------------- + +processor.normalize = {{ getv "/processor/normalize" "false" }} +processor.background_color = {{ getv "/processor/background/color" "black" }} +processor.downscale_filter = {{ getv "/processor/downscale/filter" "bicubic" }} +processor.upscale_filter = {{ getv "/processor/upscale/filter" "bicubic" }} +processor.sharpen = {{ getv "/processor/sharpen" "0" }} +processor.jpg.progressive = {{ getv "/processor/jpg/progressive" "true" }} +processor.jpg.quality = {{ getv "/processor/jpg/quality" "80" }} +processor.tif.compression = {{ getv "/processor/tif/compression" "LZW" }} +StreamProcessor.retrieval_strategy = {{ getv "/streamprocessor/retrieval/strategy" "StreamStrategy" }} + +#---------------------------------------- +# FfmpegProcessor +#---------------------------------------- + +FfmpegProcessor.path_to_binaries = {{ getv "/ffmpegprocessor/path/to/binaries" "" }} + +#---------------------------------------- +# GraphicsMagickProcessor +#---------------------------------------- + +GraphicsMagickProcessor.path_to_binaries = {{ getv "/graphicsmagickprocessor/path/to/binaries" "" }} + +#---------------------------------------- +# ImageMagickProcessor +#---------------------------------------- + +ImageMagickProcessor.path_to_binaries = {{ getv "/imagemagickprocessor/path/to/binaries" "" }} + +#---------------------------------------- +# KakaduProcessor +#---------------------------------------- + +KakaduProcessor.path_to_binaries = {{ getv "/kakaduprocessor/path/to/binaries" "" }} + +#---------------------------------------- +# OpenJpegProcessor +#---------------------------------------- + +OpenJpegProcessor.path_to_binaries = {{ getv "/openjpegprocessor/path/to/binaries" "" }} + +#---------------------------------------- +# PdfBoxProcessor +#---------------------------------------- + +PdfBoxProcessor.dpi = {{ getv "/pdfboxprocessor/dpi" "150" }} + +########################################################################### +# CLIENT-SIDE CACHING +########################################################################### + +cache.client.enabled = {{ getv "/cache/client/enabled" "true" }} +cache.client.max_age = {{ getv "/cache/client/max/age" "2592000" }} +cache.client.shared_max_age = {{ getv "/cache/client/shared/max/age" "" }} +cache.client.public = {{ getv "/cache/client/public" "true" }} +cache.client.private = {{ getv "/cache/client/private" "false" }} +cache.client.no_cache = {{ getv "/cache/client/no/cache" "false" }} +cache.client.no_store = {{ getv "/cache/client/no/store" "false" }} +cache.client.must_revalidate = {{ getv "/cache/client/must/revalidate" "false" }} +cache.client.proxy_revalidate = {{ getv "/cache/client/proxy/revalidate" "false" }} +cache.client.no_transform = {{ getv "/cache/client/no/transform" "true" }} + +########################################################################### +# SERVER-SIDE CACHING +########################################################################### + +cache.source = {{ getv "/cache/source" "FilesystemCache" }} +cache.derivative = {{ getv "/cache/derivative" "FilesystemCache" }} +cache.server.ttl_seconds = {{ getv "/cache/server/ttl/seconds" "2592000" }} +cache.server.purge_missing = {{ getv "/cache/server/purge/missing" "false" }} +cache.server.resolve_first = {{ getv "/cache/server/resolve/first" "false" }} +cache.server.worker.enabled = {{ getv "/cache/server/worker/enabled" "false" }} +cache.server.worker.interval = {{ getv "/cache/server/worker/interval" "86400" }} + +#---------------------------------------- +# FilesystemCache +#---------------------------------------- + +FilesystemCache.pathname = {{ getv "/filesystemcache/pathname" "/data" }} +FilesystemCache.dir.depth = {{ getv "/filesystemcache/dir/depth" "3" }} +FilesystemCache.dir.name_length = {{ getv "/filesystemcache/dir/name/length" "2" }} + +#---------------------------------------- +# JdbcCache +#---------------------------------------- + +JdbcCache.url = {{ getv "/jdbccache/url" "jdbc:postgresql://database:5432/cantaloupe" }} +JdbcCache.user = {{ getv "/jdbccache/user" "admin" }} +JdbcCache.password = {{ getv "/jdbccache/password" "password" }} +JdbcCache.connection_timeout = {{ getv "/jdbccache/connection/timeout" "10" }} +JdbcCache.derivative_image_table = {{ getv "/jdbccache/derivative/image/table" "derivative_cache" }} +JdbcCache.info_table = {{ getv "/jdbccache/info/table" "info_cache" }} + +#---------------------------------------- +# AmazonS3Cache +#---------------------------------------- + +AmazonS3Cache.access_key_id = {{ getv "/amazons3cache/access/key/id" "" }} +AmazonS3Cache.secret_key = {{ getv "/amazons3cache/secret/key" "" }}} +AmazonS3Cache.bucket.name = {{ getv "/amazons3cache/bucket/name" "" }}} +AmazonS3Cache.bucket.region = {{ getv "/amazons3cache/bucket/region" "" }}} +AmazonS3Cache.object_key_prefix = {{ getv "/amazons3cache/object/key/prefix" "" }}} + +#---------------------------------------- +# AzureStorageCache +#---------------------------------------- + +AzureStorageCache.account_name = {{ getv "/azurestoragecache/account/name" "" }} +AzureStorageCache.account_key = {{ getv "/azurestoragecache/account/key" "" }} +AzureStorageCache.container_name = {{ getv "/azurestoragecache/container/name" "" }} +AzureStorageCache.object_key_prefix = {{ getv "/azurestoragecache/object/key/prefix" "" }} + +########################################################################### +# OVERLAYS +########################################################################### + +overlays.enabled = {{ getv "/overlays/enabled" "false" }} +overlays.strategy = {{ getv "/overlays/strategy" "BasicStrategy" }} +overlays.BasicStrategy.type = {{ getv "/overlays/basicstrategy/type" "image" }} +overlays.BasicStrategy.image = {{ getv "/overlays/basicstrategy/image" "/path/to/overlay.png" }} +overlays.BasicStrategy.string = {{ getv "/overlays/basicstrategy/string" "Copyright. All rights reserved." }} +overlays.BasicStrategy.string.font = {{ getv "/overlays/basicstrategy/string/font" "Helvetica" }} +overlays.BasicStrategy.string.font.size = {{ getv "/overlays/basicstrategy/string/font/size" "24" }} +overlays.BasicStrategy.string.font.min_size = {{ getv "/overlays/basicstrategy/string/font/min/size" "18" }} +overlays.BasicStrategy.string.font.weight = {{ getv "/overlays/basicstrategy/string/font/weight" "1.0" }} +overlays.BasicStrategy.string.glyph_spacing = {{ getv "/overlays/basicstrategy/string/glyph/spacing" "0.02" }} +overlays.BasicStrategy.string.color = {{ getv "/overlays/basicstrategy/string/color" "white" }} +overlays.BasicStrategy.string.stroke.color = {{ getv "/overlays/basicstrategy/string/stroke/color" "black" }} +overlays.BasicStrategy.string.stroke.width = {{ getv "/overlays/basicstrategy/string/stroke/width" "1" }} +overlays.BasicStrategy.string.background.color = {{ getv "/overlays/basicstrategy/string/background/color" "rgba(0, 0, 0, 100)" }} +overlays.BasicStrategy.position = {{ getv "/overlays/basicstrategy/position" "bottom right" }} +overlays.BasicStrategy.inset = {{ getv "/overlays/basicstrategy/inset" "10" }} +overlays.BasicStrategy.output_width_threshold = {{ getv "/overlays/basicstrategy/output/width/threshold" "400" }} +overlays.BasicStrategy.output_height_threshold = {{ getv "/overlays/basicstrategy/output/height/threshold" "300" }} + +########################################################################### +# REDACTIONS +########################################################################### + +redaction.enabled = {{ getv "/redaction/enabled" "false" }} + +########################################################################### +# METADATA +########################################################################### + +metadata.preserve = {{ getv "/metadata/preserve" "false" }} +metadata.respect_orientation = {{ getv "/metadata/respect/orientation" "false" }} + +########################################################################### +# LOGGING +########################################################################### + +#---------------------------------------- +# Application Log +#---------------------------------------- + +log.application.level = {{ getv "/log/application/level" "debug" }} +log.application.ConsoleAppender.enabled = {{ getv "/log/application/consoleappender/enabled" "true" }} +log.application.FileAppender.enabled = {{ getv "/log/application/fileappender/enabled" "false" }} +log.application.FileAppender.pathname = {{ getv "/log/application/fileappender/pathname" "/opt/tomcat/logs/cantaloupe.application.log" }} +log.application.RollingFileAppender.enabled = {{ getv "/log/application/rollingfileappender/enabled" "false" }} +log.application.RollingFileAppender.pathname = {{ getv "/log/application/rollingfileappender/pathname" "/opt/tomcat/logs/cantaloupe.application.log" }} +log.application.RollingFileAppender.policy = {{ getv "/log/application/rollingfileappender/policy" "TimeBasedRollingPolicy" }} +log.application.RollingFileAppender.TimeBasedRollingPolicy.filename_pattern = {{ getv "/log/application/rollingfileappender/timebasedrollingpolicy/filename/pattern" "/opt/tomcat/logs/cantaloupe.application-%d{yyyy-MM-dd}.log" }} +log.application.RollingFileAppender.TimeBasedRollingPolicy.max_history = {{ getv "/log/application/rollingfileappender/timebasedrollingpolicy/max/history" "30" }} +log.application.SyslogAppender.enabled = {{ getv "/log/application/syslogappender/enabled" "false" }} +log.application.SyslogAppender.host = {{ getv "/log/application/syslogappender/host" "" }} +log.application.SyslogAppender.port = {{ getv "/log/application/syslogappender/port" "514" }} +log.application.SyslogAppender.facility = {{ getv "/log/application/syslogappender/facility" "LOCAL0" }} + +#---------------------------------------- +# Access Log +#---------------------------------------- + +log.access.ConsoleAppender.enabled = {{ getv "/log/access/consoleappender/enabled" "false" }} +log.access.FileAppender.enabled = {{ getv "/log/access/fileappender/enabled" "true" }} +log.access.FileAppender.pathname = {{ getv "/log/access/fileappender/pathname" "/opt/tomcat/logs/cantaloupe.access.log" }} +log.access.RollingFileAppender.enabled = {{ getv "/log/access/rollingfileappender/enabled" "false" }} +log.access.RollingFileAppender.pathname = {{ getv "/log/access/rollingfileappender/pathname" "/opt/tomcat/logs/cantaloupe.access.log" }} +log.access.RollingFileAppender.policy = {{ getv "/log/access/rollingfileappender/policy" "TimeBasedRollingPolicy" }} +log.access.RollingFileAppender.TimeBasedRollingPolicy.filename_pattern = {{ getv "/log/access/rollingfileappender/timebasedrollingpolicy/filename/pattern" "/opt/tomcat/logs/cantaloupe.access-%d{yyyy-MM-dd}.log" }} +log.access.RollingFileAppender.TimeBasedRollingPolicy.max_history = {{ getv "/log/access/rollingfileappender/timebasedrollingpolicy/max/history" "30" }} +log.access.SyslogAppender.enabled = {{ getv "/log/access/syslogappender/enabled" "false" }} +log.access.SyslogAppender.host = {{ getv "/log/access/syslogappender/host" "" }} +log.access.SyslogAppender.port = {{ getv "/log/access/syslogappender/port" "514" }} +log.access.SyslogAppender.facility = {{ getv "/log/access/syslogappender/facility" "LOCAL0" }} diff --git a/cantaloupe/rootfs/etc/confd/templates/setenv.sh.tmpl b/cantaloupe/rootfs/etc/confd/templates/setenv.sh.tmpl new file mode 100755 index 00000000..c4643303 --- /dev/null +++ b/cantaloupe/rootfs/etc/confd/templates/setenv.sh.tmpl @@ -0,0 +1,4 @@ +#!/bin/sh +export JAVA_OPTS="{{ getv "/java/opts" "" }}" +export CATALINA_OPTS="{{ getv "/catalina/opts" "" }}" +export CATALINA_OPTS="${CATALINA_OPTS} -Dcantaloupe.config=/opt/tomcat/conf/cantaloupe.properties" diff --git a/commands/continuous.sh b/commands/continuous.sh new file mode 100755 index 00000000..1b05a697 --- /dev/null +++ b/commands/continuous.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash + +set -e + +readonly PROGNAME=$(basename $0) +readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly ROOT="$(realpath ${PROGDIR}/..)" +readonly ARGS="$@" + +# Kill background processes. +trap "kill 0" SIGINT + +function usage { + cat <<- EOF + usage: $PROGNAME options [images, and services...] + + Continously builds and runs the given images and services. + + OPTIONS: + -h --help Show this help. + -x --debug Debug this script. + + Examples: + $PROGNAME +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + while getopts "hx" OPTION + do + case $OPTION in + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + # All remaning parameters are files to be removed from the repo if --strip was specified. + shift $((OPTIND-1)) + readonly INPUT=("$@") + + return 0 +} + +function contains { + local search="${1}"; shift + local list=("$@") + local i= + for i in "${list[@]}"; + do + if [[ "${search}" == "${i}" ]] + then + return 0 + fi + done + return 1 +} + +function services { + docker-compose -f "${ROOT}/docker-compose.yml" config --services +} + +function images { + pushd ${PROGDIR}/.. &> /dev/null + ./gradlew tasks --all -q | grep -e ":build[^a-zA-Z]" | sed -e 's/^\([a-zA-Z0-9_-]*\):build.*$/\1/' + popd &> /dev/null +} + +function main { + cmdline ${ARGS} + local images=($(images)) + local services=($(services)) + local build=() + local run=() + local i= + local unknown_arg="true" + + for i in "${INPUT[@]}" + do + unknown_argument="true" + if contains "${i}" "${images[@]}"; then + build+=(${i}) + unknown_argument="false" + fi + if contains "${i}" "${services[@]}"; then + run+=(${i}) + unknown_argument="false" + fi + if [[ "${unknown_argument}" == "true" ]]; then + echo "Error unknown image or service: ${i}" + exit 1 + fi + done + + pushd ${PROGDIR}/.. &> /dev/null + if [ -z "${INPUT}" ]; then + ./gradlew build --continuous & + docker-compose -f "${ROOT}/docker-compose.yml" up & + else + ./gradlew $(printf ":%q:build " ${build[@]}) --continuous & + docker-compose -f "${ROOT}/docker-compose.yml" up watchtower ${run[@]} & + fi + popd &> /dev/null + + wait +} +main diff --git a/commands/drush.sh b/commands/drush.sh new file mode 100755 index 00000000..787885fc --- /dev/null +++ b/commands/drush.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly ROOT="$(realpath ${PROGDIR}/..)" +docker-compose -f "${ROOT}/docker-compose.yml" exec drupal s6-setuidgid nginx php -d memory_limit=-1 /usr/local/bin/drush ${@} diff --git a/commands/etcdctl.sh b/commands/etcdctl.sh new file mode 100755 index 00000000..21412a97 --- /dev/null +++ b/commands/etcdctl.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly ROOT="$(realpath ${PROGDIR}/..)" +docker-compose -f "${ROOT}/docker-compose.yml" exec etcd etcdctl ${@} diff --git a/commands/mysql.sh b/commands/mysql.sh new file mode 100755 index 00000000..dc8619c3 --- /dev/null +++ b/commands/mysql.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly ROOT="$(realpath ${PROGDIR}/..)" +docker-compose -f "${ROOT}/docker-compose.yml" exec database mysql ${@} diff --git a/commands/open-in-browser.sh b/commands/open-in-browser.sh new file mode 100755 index 00000000..6b19bd35 --- /dev/null +++ b/commands/open-in-browser.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash + +set -e + +readonly PROGNAME=$(basename $0) +readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly ARGS="$@" + +function usage { + cat <<- EOF + usage: $PROGNAME SERVICE + + Opens the given SERVICE in the users browser. + + OPTIONS: + -h --help Show this help. + -x --debug Debug this script. + + Examples: + Opens activemq web console: + $PROGNAME activemq +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + while getopts "hx" OPTION + do + case $OPTION in + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + shift $((OPTIND-1)) + readonly SERVICE="${1}" + + # Check if the service exists and is running. + docker-compose ps ${SERVICE} &> /dev/null || ( echo "Service ${SERVICE} does not exist."; exit 1 ) + [[ "$(docker-compose ps -q ${SERVICE})" == "" ]] && (echo "Service ${SERVICE} is not running."; exit 1) + + return 0 +} + +function open { + local url="${1}" + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + xdg-open "${url}" + elif [[ "$OSTYPE" == "darwin"* ]]; then + osascript -e "open location \"${URL}\"" + elif [[ "$OSTYPE" == "cygwin" ]]; then + cygstart "${url}" + else + echo "Unknown OS ${OSTYPE}" + exit 1 + fi +} + +function image { + local service="${1}" + docker-compose images ${service} | tail -1 | awk '{print $1}' +} + +function ip { + local service="${1}" + local image=$(image ${service}) + local template="{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}" + # Assumes the default network is listed first. + docker inspect -f "${template}" "${image}" | head -n1 +} + +function url { + local service="${1}"; shift + local port="${1}"; shift + local path="${1}"; shift + echo "http://$(ip ${service}):${port}${path}" +} + +function main { + cmdline ${ARGS} + + case "${SERVICE}" in + activemq) open "http://activemq.localhost/admin" &> /dev/null;; + alpaca) open $(url alpaca 8181 /system/console) &> /dev/null;; + blazegraph) open "http://blazegraph.localhost/bigdata" &> /dev/null;; + cantaloupe) open $(url cantaloupe 8080 /cantaloupe/) &> /dev/null;; + crayfits) open $(url crayfits 8000 /) &> /dev/null;; + fcrepo) open "http://fcrepo.localhost/fcrepo/rest" &> /dev/null;; + gemini) open $(url gemini 8000 /) &> /dev/null;; + homarus) open $(url homarus 8000 /) &> /dev/null;; + houdini) open $(url houdini 8000 /) &> /dev/null;; + hypercube) open $(url hypercube 8000 /) &> /dev/null;; + drupal) open "http://drupal.localhost" &> /dev/null;; + milliner) open $(url milliner 8000 /) &> /dev/null;; + recast) open $(url recast 8000 /) &> /dev/null;; + solr) open "http://solr.localhost/solr" &> /dev/null;; + matomo) open "http://matomo.localhost" &> /dev/null;; + *) exit 1;; + esac +} +main diff --git a/commands/shell.sh b/commands/shell.sh new file mode 100755 index 00000000..1b64b15d --- /dev/null +++ b/commands/shell.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly ROOT="$(realpath ${PROGDIR}/..)" +docker-compose -f "${ROOT}/docker-compose.yml" exec ${1} ash diff --git a/composer/.dockerignore b/composer/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/composer/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/composer/Dockerfile b/composer/Dockerfile new file mode 100644 index 00000000..1d031abe --- /dev/null +++ b/composer/Dockerfile @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/nginx:latest as composer + +# Overwrite with your own arguments or explicitly copy in +# your own composer.json / composer.lock files instead. +# https://getcomposer.org/doc/03-cli.md#create-project +ARG composer_project="drupal/recommended-project" + +WORKDIR /build + +RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + composer create-project --no-install ${composer_project} /build && \ + composer require -- drush/drush && \ + composer install + +FROM islandora/drupal:latest + +RUN --mount=type=bind,from=composer,source=/build,target=/build \ + cp -r /build/* /var/www/drupal && \ + chown -R nginx:nginx /var/www/drupal && \ + wget -N -P /opt/downloads https://github.com/drush-ops/drush-launcher/releases/latest/download/drush.phar && \ + cp /opt/downloads/drush.phar /usr/local/bin/drush && \ + chmod a+x /usr/local/bin/drush && \ + cleanup.sh diff --git a/composer/README.md b/composer/README.md new file mode 100644 index 00000000..148b37ad --- /dev/null +++ b/composer/README.md @@ -0,0 +1,6 @@ +# Composer + +Serves as a template for those that wish to build a production installation +using a composer.json/composer.lock file from another repository. + +Copying the resulting composer install site into the drupal image. diff --git a/crayfish/.dockerignore b/crayfish/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/crayfish/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/crayfish/Dockerfile b/crayfish/Dockerfile new file mode 100644 index 00000000..869e5837 --- /dev/null +++ b/crayfish/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/nginx:latest + +ARG COMMIT=1.1.1 + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + git-clone-cached.sh \ + --url https://github.com/Islandora/Crayfish.git \ + --commit "${COMMIT}" \ + --worktree /var/www/crayfish && \ + mkdir /var/log/islandora && \ + chown nginx:nginx /var/log/islandora && \ + chown -R nginx:nginx /var/www && \ + cleanup.sh + +COPY /rootfs / + +STOPSIGNAL SIGWINCH + +EXPOSE 8000 diff --git a/crayfish/README.md b/crayfish/README.md new file mode 100644 index 00000000..d54b9389 --- /dev/null +++ b/crayfish/README.md @@ -0,0 +1,31 @@ +# Crayfish + +Docker image for [Crayfish] version 1.1.1. + +Acts as base Docker image for Crayfish based micro-services. It is not meant to +be run on its own. + +## Dependencies + +Requires `islandora/nginx` docker image to build. Please refer to the +[Nginx Image README](../nginx/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Ports + +| Port | Description | +| :--- | :---------- | +| 8000 | HTTP | + +## Settings + +> N.B. For all of the settings below images that descend from +> ``islandora/crayfish`` will apply prefix to every setting. So for example +> `JWT_ADMIN_TOKEN` would become `GEMINI_JWT_ADMIN_TOKEN` this is to allow for +> different settings on a per-service basis. + +| Environment Variable | Etcd Key | Default | Description | +| :------------------- | :--------------- | :-------- | :---------- | +| JWT_ADMIN_TOKEN | /jwt/admin/token | islandora | JWT Token | + +[Crayfish]: https://github.com/Islandora/Crayfish/tree/master diff --git a/crayfish/rootfs/etc/confd/conf.d/default.conf.toml b/crayfish/rootfs/etc/confd/conf.d/default.conf.toml new file mode 100644 index 00000000..1df8dbe5 --- /dev/null +++ b/crayfish/rootfs/etc/confd/conf.d/default.conf.toml @@ -0,0 +1,7 @@ +[template] +src = "default.conf.tmpl" +dest = "/etc/nginx/conf.d/default.conf" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/" ] diff --git a/crayfish/rootfs/etc/confd/conf.d/syn-settings.xml.toml b/crayfish/rootfs/etc/confd/conf.d/syn-settings.xml.toml new file mode 100644 index 00000000..bf5d102e --- /dev/null +++ b/crayfish/rootfs/etc/confd/conf.d/syn-settings.xml.toml @@ -0,0 +1,7 @@ +[template] +src = "syn-settings.xml.tmpl" +dest = "/var/www/crayfish/syn-settings.xml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/jwt" ] diff --git a/crayfish/rootfs/etc/confd/templates/default.conf.tmpl b/crayfish/rootfs/etc/confd/templates/default.conf.tmpl new file mode 100644 index 00000000..b9df4e91 --- /dev/null +++ b/crayfish/rootfs/etc/confd/templates/default.conf.tmpl @@ -0,0 +1,29 @@ +# From: https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/ +server { + listen 8000; + root /var/www/html; + + location / { + # try to serve file directly, fallback to index.php + try_files $uri /index.php$is_args$args; + } + + location ~ ^/index\.php(/|$) { + fastcgi_pass unix:/var/run/php-fpm7/php-fpm7.sock; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + include fastcgi_params; + + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + fastcgi_param DOCUMENT_ROOT $realpath_root; + # Prevents URIs that include the front controller. This will 404: + # http://domain.tld/index.php/some-path + # Remove the internal directive to allow URIs like this + internal; + } + + # return 404 for all other php files not matching the front controller + # this prevents access to other php files you don't want to be accessible. + location ~ \.php$ { + return 404; + } +} diff --git a/crayfish/rootfs/etc/confd/templates/syn-settings.xml.tmpl b/crayfish/rootfs/etc/confd/templates/syn-settings.xml.tmpl new file mode 100644 index 00000000..e33c6d64 --- /dev/null +++ b/crayfish/rootfs/etc/confd/templates/syn-settings.xml.tmpl @@ -0,0 +1,7 @@ + + + + + {{ getv "/jwt/admin/token" "islandora" }} + + diff --git a/crayfits/.dockerignore b/crayfits/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/crayfits/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/crayfits/Dockerfile b/crayfits/Dockerfile new file mode 100644 index 00000000..dd2ae0ee --- /dev/null +++ b/crayfits/Dockerfile @@ -0,0 +1,22 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/nginx:latest + +ARG COMMIT=4e0faeb31f84e74e7cecc083b2f096d55e425fbb + +RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + --mount=id=downloads,type=cache,target=/opt/downloads \ + git-clone-cached.sh \ + --url https://github.com/roblib/CrayFits.git \ + --commit "${COMMIT}" \ + --worktree /var/www/crayfits && \ + composer install -d /var/www/crayfits && \ + mkdir /var/log/islandora && \ + chown nginx:nginx /var/log/islandora && \ + chown -R nginx:nginx /var/www && \ + cleanup.sh + +COPY /rootfs / + +STOPSIGNAL SIGWINCH + +EXPOSE 8000 diff --git a/crayfits/README.md b/crayfits/README.md new file mode 100644 index 00000000..d1b770b2 --- /dev/null +++ b/crayfits/README.md @@ -0,0 +1,31 @@ +# Crayfits + +Docker image for [CrayFits] (**unreleased version**). + +Acts as base Docker image for CrayFits based micro-services. It is not meant to +be run on its own. + +## Dependencies + +Requires `islandora/nginx` docker image to build. Please refer to the +[Nginx Image README](../nginx/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Ports + +| Port | Description | +| :--- | :---------- | +| 8000 | HTTP | + +## Settings + +> N.B. For all of the settings below images that descend from +> ``islandora/crayfits`` will apply prefix to every setting. So for example +> `JWT_ADMIN_TOKEN` would become `GEMINI_JWT_ADMIN_TOKEN` this is to allow for +> different settings on a per-service basis. + +| Environment Variable | Etcd Key | Default | Description | +| :---------------------- | :----------------------- | :--------------------- | :--------------------------- | +| CRAYFITS_WEBSERVICE_URI | /crayfits/webservice/uri | fits/fits/examine | The URL of the FITS servlet. | + +[CrayFits]: https://github.com/roblib/CrayFits diff --git a/crayfits/rootfs/etc/confd/conf.d/.env.local.toml b/crayfits/rootfs/etc/confd/conf.d/.env.local.toml new file mode 100644 index 00000000..6926e23c --- /dev/null +++ b/crayfits/rootfs/etc/confd/conf.d/.env.local.toml @@ -0,0 +1,7 @@ +[template] +src = ".env.local.tmpl" +dest = "/var/www/crayfits/.env.local" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/webservice" ] \ No newline at end of file diff --git a/crayfits/rootfs/etc/confd/conf.d/default.conf.toml b/crayfits/rootfs/etc/confd/conf.d/default.conf.toml new file mode 100644 index 00000000..1df8dbe5 --- /dev/null +++ b/crayfits/rootfs/etc/confd/conf.d/default.conf.toml @@ -0,0 +1,7 @@ +[template] +src = "default.conf.tmpl" +dest = "/etc/nginx/conf.d/default.conf" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/" ] diff --git a/crayfits/rootfs/etc/confd/confd.toml b/crayfits/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..20778f09 --- /dev/null +++ b/crayfits/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/crayfits" diff --git a/crayfits/rootfs/etc/confd/templates/.env.local.tmpl b/crayfits/rootfs/etc/confd/templates/.env.local.tmpl new file mode 100644 index 00000000..50554e82 --- /dev/null +++ b/crayfits/rootfs/etc/confd/templates/.env.local.tmpl @@ -0,0 +1 @@ +FITS_WEBSERVICE_URI={{ getv "/webservice/uri" "fits/fits/examine" }} \ No newline at end of file diff --git a/crayfits/rootfs/etc/confd/templates/default.conf.tmpl b/crayfits/rootfs/etc/confd/templates/default.conf.tmpl new file mode 100644 index 00000000..22106cfc --- /dev/null +++ b/crayfits/rootfs/etc/confd/templates/default.conf.tmpl @@ -0,0 +1,29 @@ +# From: https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/ +server { + listen 8000; + root /var/www/crayfits/public; + + location / { + # try to serve file directly, fallback to index.php + try_files $uri /index.php$is_args$args; + } + + location ~ ^/index\.php(/|$) { + fastcgi_pass unix:/var/run/php-fpm7/php-fpm7.sock; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + include fastcgi_params; + + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + fastcgi_param DOCUMENT_ROOT $realpath_root; + # Prevents URIs that include the front controller. This will 404: + # http://domain.tld/index.php/some-path + # Remove the internal directive to allow URIs like this + internal; + } + + # return 404 for all other php files not matching the front controller + # this prevents access to other php files you don't want to be accessible. + location ~ \.php$ { + return 404; + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..0c465283 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,213 @@ +# file: docker-compose.yml +# DO NOT USE IN PRODUCTION +# +# Development docker-compose for testing locally: +# - All users are 'admin' +# - All passwords are 'password' +# +# With the exception of the database where the user is +# 'root' rather than admin. And the database users for +# each service are named after their respective service +# to avoid conflict. +# +# The Traefik image acts as Reverse Proxy to the services, +# assuming you can map to port 80 on your machine. The +# loop back for localhost should make the following urls +# accessible in your browser: +# +# - activemq.localhost/admin +# - blazegraph.localhost/bigdata +# - drupal.localhost +# - fcrepo.localhost/fcrepo/reset +# - matomo.localhost +# +# For other images you can use the command: +# +# ./commands/open-in-browser SERVICE +version: "3.8" +networks: + external: + internal: false + default: + internal: true +volumes: + drupal-config-data: + drupal-sites-data: + etcd-data: + jwt-data: + jwt-public-data: + karaf-data: + matomo-config-data: + mysql-data: + mysql-files: + solr-data: +services: + # Single node cluster. + # + # If disabled the system will fall back on Environment Variables. + # To set key/value pairs use the following: + # + # ./commands/etcdctl.sh put /drupal/site/default/name "default" + # + etcd: + image: gcr.io/etcd-development/etcd:v3.4.7 + environment: + ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2379" + ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379" + volumes: + - etcd-data:/data + ports: + - 2379 + - 2380 + - 4001 + command: > + etcd + --data-dir=/data + watchtower: + image: v2tec/watchtower + volumes: + - /var/run/docker.sock:/var/run/docker.sock + command: --interval 1 --no-pull + activemq: + image: islandora/activemq:latest + labels: + # Do not expose in production. + - traefik.http.services.activemq.loadbalancer.server.port=8161 + - traefik.http.routers.activemq_http.service=activemq + - traefik.http.routers.activemq_http.entrypoints=http + alpaca: + image: islandora/alpaca:latest + volumes: + - karaf-data:/opt/karaf/data + depends_on: + - activemq + blazegraph: + image: islandora/blazegraph:latest + networks: + default: + aliases: + - blazegraph.localhost + labels: + - traefik.http.services.blazegraph.loadbalancer.server.port=80 + - traefik.http.routers.blazegraph_http.service=blazegraph + - traefik.http.routers.blazegraph_http.entrypoints=http + cantaloupe: + image: islandora/cantaloupe:latest + crayfits: + image: islandora/crayfits:latest + depends_on: + - fits + # Database is chosen as the service name rather than mariadb, + # as institutions may want to swap out back-ends later and + # database is a more sensible default. + database: + image: islandora/mariadb:latest + volumes: + - mysql-data:/var/lib/mysql + - mysql-files:/var/lib/mysql-files + drupal: + image: islandora/sandbox:latest + restart: unless-stopped + volumes: + - drupal-config-data:/var/www/drupal/config + - drupal-sites-data:/var/www/drupal/web/sites + - solr-data:/opt/solr/server/solr + - jwt-data:/opt/keys/claw/ + depends_on: + - solr + - fcrepo + - database + - activemq + networks: + default: + aliases: + - drupal.localhost + labels: + - traefik.http.services.drupal.loadbalancer.server.port=80 + - traefik.http.routers.drupal_http.service=drupal + - traefik.http.routers.drupal_http.entrypoints=http + fcrepo: + image: islandora/fcrepo:latest + volumes: + - jwt-data:/opt/keys/claw/ + depends_on: + - activemq + - database + networks: + default: + aliases: + - fcrepo.localhost + labels: + # Do not expose in production. + - traefik.http.services.fcrepo.loadbalancer.server.port=80 + - traefik.http.routers.fcrepo_http.service=fcrepo + - traefik.http.routers.fcrepo_http.entrypoints=http + fits: + image: islandora/fits + gemini: + image: islandora/gemini:latest + volumes: + - jwt-data:/opt/keys/claw/ + depends_on: + - database + homarus: + image: islandora/homarus:latest + volumes: + - jwt-data:/opt/keys/claw/ + houdini: + image: islandora/houdini:latest + volumes: + - jwt-data:/opt/keys/claw/ + hypercube: + image: islandora/hypercube:latest + volumes: + - jwt-data:/opt/keys/claw/ + matomo: + image: islandora/matomo + volumes: + - matomo-config-data:/opt/matomo/config + depends_on: + - database + networks: + default: + external: # Needs external access to request plugins, etc. + labels: + # Do not expose in production over http, setup https. + - traefik.http.services.matomo.loadbalancer.server.port=80 + - traefik.http.routers.matomo_http.service=matomo + - traefik.http.routers.matomo_http.entrypoints=http + milliner: + image: islandora/milliner:latest + volumes: + - jwt-data:/opt/keys/claw/ + recast: + image: islandora/recast:latest + volumes: + - jwt-data:/opt/keys/claw/ + solr: + image: islandora/solr:latest + volumes: + - solr-data:/opt/solr/server/solr + labels: + # Do not expose in production. + - traefik.http.services.solr.loadbalancer.server.port=8983 + - traefik.http.routers.solr_http.service=solr + - traefik.http.routers.solr_http.entrypoints=http + traefik: + image: traefik:2.2.1 + command: > + --api.insecure=true + --entryPoints.http.address=:80 + --entryPoints.https.address=:443 + --providers.docker + --providers.docker.network=external + '--providers.docker.defaultRule=Host(`{{ index .Labels "com.docker.compose.service" }}.localhost`)' + ports: + - 80:80 + - 443:443 + - 8080 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + networks: + - external + - default diff --git a/drupal/.dockerignore b/drupal/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/drupal/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/drupal/Dockerfile b/drupal/Dockerfile new file mode 100644 index 00000000..9a1b2e61 --- /dev/null +++ b/drupal/Dockerfile @@ -0,0 +1,10 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/nginx:latest + +RUN mkdir -p /var/www/drupal && chown nginx:nginx /var/www/drupal + +WORKDIR /var/www/drupal + +COPY rootfs / + +EXPOSE 80 \ No newline at end of file diff --git a/drupal/README.md b/drupal/README.md new file mode 100644 index 00000000..74ffbf5d --- /dev/null +++ b/drupal/README.md @@ -0,0 +1,60 @@ +# Drupal + +Docker image for [Drupal]. + +Acts as base Docker image for Drupal based projects, it doesn't install Drupal +as consumers of this image are expected to provide their own composer file. +Instead it provides startup scripts that allow Drupal to be installed when the +image is first run. + +## Dependencies + +Requires `islandora/nginx` docker image to build. Please refer to the +[Nginx Image README](../nginx/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Ports + +| Port | Description | +| :--- | :---------- | +| 80 | HTTP | + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------------------ | :------------------------------- | :---------------------- | :-------------------------------------------------------- | +| DRUPAL_DB_DRIVER | /drupal/db/driver | mysql | The database driver | +| DRUPAL_DB_HOST | /drupal/db/host | database | The database host | +| DRUPAL_DB_PORT | /drupal/db/port | 3306 | The database port | +| DRUPAL_DB_ROOT_PASSWORD | /drupal/db/root/password | password | The database root user (used to create the site database) | +| DRUPAL_DB_ROOT_USER | /drupal/db/root/user | root | The database root user password | +| DRUPAL_DEFAULT_ACCOUNT_EMAIL | /drupal/default/account/email | webmaster@localhost.com | The email to use for the admin account | +| DRUPAL_DEFAULT_ACCOUNT_NAME | /drupal/default/account/name | admin | The Drupal administrator user | +| DRUPAL_DEFAULT_ACCOUNT_PASSWORD | /drupal/default/account/password | password | The Drupal administrator user password | +| DRUPAL_DEFAULT_DB_NAME | /drupal/default/db/name | drupal_default | The name of the sites database | +| DRUPAL_DEFAULT_DB_PASSWORD | /drupal/default/db/password | password | The database users password | +| DRUPAL_DEFAULT_DB_USER | /drupal/default/db/user | drupal_default | The database user used by the site. | +| DRUPAL_DEFAULT_EMAIL | /drupal/default/email | webmaster@localhost.com | The Drupal administrators email | +| DRUPAL_DEFAULT_LOCALE | /drupal/default/locale | en | The Drupal sites locale | +| DRUPAL_DEFAULT_NAME | /drupal/default/name | default | The Drupal sites name | +| DRUPAL_DEFAULT_PROFILE | /drupal/default/profile | standard | The installation profile to use | + +Additional multi-sites can be defined by adding more environment variables, +following the above conventions, only the `DRUPAL_SITE_{SITE}_NAME` is required +to create an additional site: + +| Environment Variable | Etcd Key | Default | Description | +| :---------------------------------- | :----------------------------------- | :---------------------- | :-------------------------------------------- | +| DRUPAL_SITE_{SITE}_ACCOUNT_EMAIL | /drupal/site/{SITE}/account/email | webmaster@localhost.com | The email to use for the admin account | +| DRUPAL_SITE_{SITE}_ACCOUNT_NAME | /drupal/site/{SITE}/account/name | admin | The Drupal administrator user | +| DRUPAL_SITE_{SITE}_ACCOUNT_PASSWORD | /drupal/site/{SITE}/account/password | password | The Drupal administrator user password | +| DRUPAL_SITE_{SITE}_DB_NAME | /drupal/site/{SITE}/db/name | drupal_{SITE} | The name of the sites database | +| DRUPAL_SITE_{SITE}_DB_PASSWORD | /drupal/site/{SITE}/db/password | password | The database users password | +| DRUPAL_SITE_{SITE}_DB_USER | /drupal/site/{SITE}/db/user | drupal_{SITE} | The database user used by the site. | +| DRUPAL_SITE_{SITE}_EMAIL | /drupal/site/{SITE}/email | webmaster@localhost.com | The Drupal administrators email | +| DRUPAL_SITE_{SITE}_LOCALE | /drupal/site/{SITE}/locale | en | The Drupal sites locale | +| DRUPAL_SITE_{SITE}_NAME | /drupal/site/{SITE}/name | | The Drupal sites name | +| DRUPAL_SITE_{SITE}_PROFILE | /drupal/site/{SITE}/profile | standard | The installation profile to use | +| DRUPAL_SITE_{SITE}_SUBDIR | /drupal/site/{SITE}/subdir | {SITE} | The subdirectory to install the sub-site into | + +[Drupal]: https://www.drupal.org/ diff --git a/drupal/rootfs/etc/confd/conf.d/create-drupal-databases.sh.toml b/drupal/rootfs/etc/confd/conf.d/create-drupal-databases.sh.toml new file mode 100644 index 00000000..c45d00c4 --- /dev/null +++ b/drupal/rootfs/etc/confd/conf.d/create-drupal-databases.sh.toml @@ -0,0 +1,7 @@ +[template] +src = "create-drupal-databases.sh.tmpl" +dest = "/var/run/islandora/create-drupal-databases.sh" +uid = 0 +gid = 0 +mode = "0700" +keys = [ "/db" ] diff --git a/drupal/rootfs/etc/confd/conf.d/create-drupal-databases.sql.toml b/drupal/rootfs/etc/confd/conf.d/create-drupal-databases.sql.toml new file mode 100644 index 00000000..88c1d306 --- /dev/null +++ b/drupal/rootfs/etc/confd/conf.d/create-drupal-databases.sql.toml @@ -0,0 +1,7 @@ +[template] +src = "create-drupal-databases.sql.tmpl" +dest = "/var/run/islandora/create-drupal-databases.sql" +uid = 0 +gid = 0 +mode = "0600" +keys = [ "/site" ] diff --git a/drupal/rootfs/etc/confd/conf.d/drupal-install-sites.sh.toml b/drupal/rootfs/etc/confd/conf.d/drupal-install-sites.sh.toml new file mode 100644 index 00000000..3d569b18 --- /dev/null +++ b/drupal/rootfs/etc/confd/conf.d/drupal-install-sites.sh.toml @@ -0,0 +1,7 @@ +[template] +src = "drupal-install-sites.sh.tmpl" +dest = "/var/run/islandora/drupal-install-sites.sh" +uid = 0 +gid = 0 +mode = "0700" +keys = [ "/site", "/db" ] diff --git a/drupal/rootfs/etc/confd/confd.toml b/drupal/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..925d1a48 --- /dev/null +++ b/drupal/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/drupal" diff --git a/drupal/rootfs/etc/confd/templates/create-drupal-databases.sh.tmpl b/drupal/rootfs/etc/confd/templates/create-drupal-databases.sh.tmpl new file mode 100644 index 00000000..c5d6332b --- /dev/null +++ b/drupal/rootfs/etc/confd/templates/create-drupal-databases.sh.tmpl @@ -0,0 +1,33 @@ +#!/usr/bin/with-contenv bash + +set -e + +function main { + local driver="{{ getv "/db/driver" "mysql" }}" + local host="{{ getv "/db/host" "database" }}" + local port="{{ getv "/db/port" "3306" }}" + local user="{{ getv "/db/root/user" "root" }}" + local password="{{ getv "/db/root/password" "password" }}" + + if [[ "${driver}" == "mysql" ]]; then + echo "Waiting for connection to database." + wait-for-mysql.sh \ + --host "${host}" \ + --port "${port}" \ + --user "${user}" \ + --password "${password}" + + echo "Create databases and users if they do not exist." + mysql \ + --user="${user}" \ + --password="${password}" \ + --host="${host}" \ + --port="${port}" \ + --protocol=tcp \ + < /var/run/islandora/create-drupal-databases.sql + else + echo "Only MySQL databases are supported for now." + exit 1 + fi +} +main diff --git a/drupal/rootfs/etc/confd/templates/create-drupal-databases.sql.tmpl b/drupal/rootfs/etc/confd/templates/create-drupal-databases.sql.tmpl new file mode 100644 index 00000000..778faf3a --- /dev/null +++ b/drupal/rootfs/etc/confd/templates/create-drupal-databases.sql.tmpl @@ -0,0 +1,13 @@ +-- Create deafult sites database in mariadb or mysql. +CREATE DATABASE IF NOT EXISTS {{ getv "/default/db/name" "drupal_default" }} CHARACTER SET utf8 COLLATE utf8_general_ci; +CREATE USER IF NOT EXISTS '{{ getv "/default/db/user" "drupal_default" }}'@'%' IDENTIFIED BY '{{ getv "/default/db/password" "password" }}'; +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES ON {{ getv "/default/db/name" "drupal_default" }}.* to '{{ getv "/default/db/user" "drupal_default" }}'@'%' IDENTIFIED BY '{{ getv "/default/db/password" "password" }}'; +FLUSH PRIVILEGES; + +-- Create drupal databases in mariadb or mysql. +{{ range $site := lsdir "/site" }} +CREATE DATABASE IF NOT EXISTS {{ getv (printf "/site/%s/db/name" $site) (printf "drupal_%s" $site) }} CHARACTER SET utf8 COLLATE utf8_general_ci; +CREATE USER IF NOT EXISTS '{{ getv (printf "/site/%s/db/user" $site) (printf "drupal_%s" $site) }}'@'%' IDENTIFIED BY '{{ getv (printf "/site/%s/db/password" $site) "password" }}'; +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES ON {{ getv (printf "/site/%s/db/name" $site) (printf "drupal_%s" $site) }}.* to '{{ getv (printf "/site/%s/db/user" $site) (printf "drupal_%s" $site) }}'@'%' IDENTIFIED BY '{{ getv (printf "/site/%s/db/password" $site) "password" }}'; +FLUSH PRIVILEGES; +{{ end }} diff --git a/drupal/rootfs/etc/confd/templates/drupal-install-sites.sh.tmpl b/drupal/rootfs/etc/confd/templates/drupal-install-sites.sh.tmpl new file mode 100644 index 00000000..ea30f79b --- /dev/null +++ b/drupal/rootfs/etc/confd/templates/drupal-install-sites.sh.tmpl @@ -0,0 +1,101 @@ +#!/usr/bin/with-contenv bash + +set -e + +function main { + local site= + local profile= + local subdir= + local name= + local email= + local locale= + local account_name= + local account_password= + local account_email= + local db_driver= + local db_host= + local db_port= + local db_root_user= + local db_root_password= + local db_name= + local db_user= + local db_password= + + # Install the default site. + name="{{ getv "/default/name" "Default" }}" + profile="{{ getv "/default/profile" "standard" }}" + subdir="{{ getv "/default/subdir" "default" }}" + email="{{ getv "/default/email" "webmaster@localhost.com" }}" + local="{{ getv "/default/locale" "en" }}" + account_name="{{ getv "/default/account/name" "admin" }}" + account_password="{{ getv "/default/account/password" "password" }}" + account_email="{{ getv "/default/account/email" "webmaster@localhost.com" }}" + db_driver="{{ getv "/db/driver" "mysql" }}" + db_host="{{ getv "/db/host" "database" }}" + db_port="{{ getv "/db/port" "3306" }}" + db_root_user="{{ getv "/db/root/user" "root" }}" + db_root_password="{{ getv "/db/root/password" "password" }}" + db_name="{{ getv "/default/db/name" "drupal_default" }}" + db_user="{{ getv "/default/db/user" "drupal_default" }}" + db_password="{{ getv "/default/db/password" "password" }}" + + # Check the number of tables to determine if it has already been installed. + count=$(mysql --user="${db_root_user}" --password="${db_root_password}" --host="${db_host}" --port="${db_port}" --protocol=tcp -Ne "SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_schema = '${db_name}'") + + # Only install if it is not already installed. + if [[ $count -eq 0 ]]; then + echo "Installing Default site." + s6-setuidgid nginx drush -n si "${profile}" \ + --sites-subdir="${subdir}" \ + --site-name="${name}" \ + --site-mail="${email}" \ + --locale="${local}" \ + --account-name="${account_name}" \ + --account-pass="${account_password}" \ + --account-mail="${account_email}" \ + --db-url="${db_driver}://${db_user}:${db_password}@${db_host}:${db_port}/${db_name}" + else + echo "Default Site already is installed." + fi + + # Install all of the specified sub-sites. +{{ range $site := lsdir "/site" }} + site="{{base $site}}" + name="{{ getv (printf "/site/%s/name" $site) }}" + profile="{{ getv (printf "/site/%s/profile" $site) "standard" }}" + subdir="{{ getv (printf "/site/%s/subdir" $site) "${site}" }}" + email="{{ getv (printf "/site/%s/email" $site) "webmaster@localhost.com" }}" + local="{{ getv (printf "/site/%s/locale" $site) "en" }}" + account_name="{{ getv (printf "/site/%s/account/name" $site) "admin" }}" + account_password="{{ getv (printf "/site/%s/account/password" $site) "password" }}" + account_email="{{ getv (printf "/site/%s/account/email" $site) "webmaster@localhost.com" }}" + db_driver="{{ getv "/db/driver" "mysql" }}" + db_host="{{ getv "/db/host" "database" }}" + db_port="{{ getv "/db/port" "3306" }}" + db_root_user="{{ getv "/db/root/user" "root" }}" + db_root_password="{{ getv "/db/root/password" "password" }}" + db_name="{{ getv (printf "/site/%s/db/name" $site) "drupal_${site}" }}" + db_user="{{ getv (printf "/site/%s/db/user" $site) "drupal_${site}" }}" + db_password="{{ getv (printf "/site/%s/db/password" $site) "password" }}" + + # Check the number of tables to determine if it has already been installed. + count=$(mysql --user="${db_root_user}" --password="${db_root_password}" --host="${db_host}" --port="${db_port}" --protocol=tcp -Ne "SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_schema = '${db_name}'") + + # Only install if it is not already installed. + if [[ $count -eq 0 ]]; then + echo "Installing site: {{base $site}}" + s6-setuidgid nginx drush -n si "${profile}" \ + --sites-subdir="${subdir}" \ + --site-name="${name}" \ + --site-mail="${email}" \ + --locale="${local}" \ + --account-name="${account_name}" \ + --account-pass="${account_password}" \ + --account-mail="${account_email}" \ + --db-url="${db_driver}://${db_user}:${db_password}@${db_host}:${db_port}/${db_name}" + else + echo "Site already is installed." + fi +{{ end }} +} +main diff --git a/drupal/rootfs/etc/cont-init.d/03-drupal-setup.sh b/drupal/rootfs/etc/cont-init.d/03-drupal-setup.sh new file mode 100644 index 00000000..5c096dc1 --- /dev/null +++ b/drupal/rootfs/etc/cont-init.d/03-drupal-setup.sh @@ -0,0 +1,4 @@ +#!/usr/bin/with-contenv bash +set -e +/var/run/islandora/create-drupal-databases.sh +/var/run/islandora/drupal-install-sites.sh diff --git a/drupal/rootfs/etc/nginx/conf.d/default.conf b/drupal/rootfs/etc/nginx/conf.d/default.conf new file mode 100644 index 00000000..281eb7fd --- /dev/null +++ b/drupal/rootfs/etc/nginx/conf.d/default.conf @@ -0,0 +1,121 @@ +# From: https://www.nginx.com/resources/wiki/start/topics/recipes/drupal/ +server { + server_name drupal; + root /var/www/drupal/web; + + location = /favicon.ico { + log_not_found off; + access_log off; + } + + location = /robots.txt { + allow all; + log_not_found off; + access_log off; + } + + # Very rarely should these ever be accessed outside of your lan + location ~* \.(txt|log)$ { + allow 192.168.0.0/16; + deny all; + } + + location ~ \..*/.*\.php$ { + return 403; + } + + location ~ ^/sites/.*/private/ { + return 403; + } + + # Block access to scripts in site files directory + location ~ ^/sites/[^/]+/files/.*\.php$ { + deny all; + } + + # Allow "Well-Known URIs" as per RFC 5785 + location ~* ^/.well-known/ { + allow all; + } + + # Block access to "hidden" files and directories whose names begin with a + # period. This includes directories used by version control systems such + # as Subversion or Git to store control files. + location ~ (^|/)\. { + return 403; + } + + location / { + # try_files $uri @rewrite; # For Drupal <= 6 + try_files $uri /index.php?$query_string; # For Drupal >= 7 + } + + location @rewrite { + rewrite ^/(.*)$ /index.php?q=$1; + } + + # Don't allow direct access to PHP files in the vendor directory. + location ~ /vendor/.*\.php$ { + deny all; + return 404; + } + + # Protect files and directories from prying eyes. + location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ { + deny all; + return 404; + } + + # In Drupal 8, we must also match new paths where the '.php' appears in + # the middle, such as update.php/selection. The rule we use is strict, + # and only allows this pattern with the update.php front controller. + # This allows legacy path aliases in the form of + # blog/index.php/legacy-path to continue to route to Drupal nodes. If + # you do not have any paths like that, then you might prefer to use a + # laxer rule, such as: + # location ~ \.php(/|$) { + # The laxer rule will continue to work if Drupal uses this new URL + # pattern with front controllers other than update.php in a future + # release. + location ~ '\.php$|^/update.php' { + fastcgi_split_path_info ^(.+?\.php)(|/.*)$; + # Ensure the php file exists. Mitigates CVE-2019-11043 + try_files $fastcgi_script_name =404; + # Security note: If you're running a version of PHP older than the + # latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini. + # See http://serverfault.com/q/627903/94922 for details. + include fastcgi_params; + # Block httpoxy attacks. See https://httpoxy.org/. + fastcgi_param HTTP_PROXY ""; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param QUERY_STRING $query_string; + fastcgi_intercept_errors on; + # PHP 7 socket location. + fastcgi_pass unix:/var/run/php-fpm7/php-fpm7.sock; + } + + # Fighting with Styles? This little gem is amazing. + # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6 + location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7 + try_files $uri @rewrite; + } + + # Handle private files through Drupal. Private file's path can come + # with a language prefix. + location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7 + try_files $uri /index.php?$query_string; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + try_files $uri @rewrite; + expires max; + log_not_found off; + } + # Enforce clean URLs + # Removes index.php from urls like www.example.com/index.php/my-page --> www.example.com/my-page + # Could be done with 301 for permanent or other redirect codes. + if ($request_uri ~* "^(.*/)index\.php(.*)") { + return 307 $1$2; + } +} diff --git a/fcrepo/.dockerignore b/fcrepo/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/fcrepo/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/fcrepo/Dockerfile b/fcrepo/Dockerfile new file mode 100644 index 00000000..b4039f75 --- /dev/null +++ b/fcrepo/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/tomcat:latest + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + FCREPO_VERSION="5.1.0" && \ + install-war-into-tomcat.sh \ + --name "fcrepo" \ + --url "https://github.com/fcrepo4/fcrepo4/releases/download/fcrepo-${FCREPO_VERSION}/fcrepo-webapp-${FCREPO_VERSION}.war" \ + --key "fdcb43cfd1468a84ddb89c20e4f4c7f54476ab9a24f69beb335d26f2b58ecec5" + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + SYN_VERSION="1.1.0" && \ + wget -N -P /opt/downloads https://github.com/Islandora-CLAW/Syn/releases/download/v${SYN_VERSION}/islandora-syn-${SYN_VERSION}-all.jar && \ + cp /opt/downloads/islandora-syn-${SYN_VERSION}-all.jar /opt/tomcat/lib && \ + cleanup.sh + +COPY rootfs / + +RUN mkdir /data && \ + chown tomcat:tomcat /data && \ + chown -R tomcat:tomcat /opt/tomcat + +VOLUME [ "/data" ] diff --git a/fcrepo/README.md b/fcrepo/README.md new file mode 100644 index 00000000..b80152fd --- /dev/null +++ b/fcrepo/README.md @@ -0,0 +1,56 @@ +# Fcrepo + +Docker image for [Fcrepo] version 5.1.0. + +Please refer to the [Fcrepo Documentation] for more in-depth information. + +As a quick example this will bring up an instance of [Fcrepo], and allow you +to view on . + +```bash +docker run --rm -ti -p 80:80 islandora/fcrepo +``` + +## Dependencies + +Requires `islandora/tomcat` docker image to build. Please refer to the +[Tomcat Image README](../tomcat/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Volumes + +| Path | Description | +| :---- | :--------------------------- | +| /data | Fcrepo Object / Binary Store | + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :----------------------------- | :------------------------------ | :-------------------------------- | :---------- | +| FCREPO_ACTIVEMQ_QUEUE | /fcrepo/activemq/queue | fedora | | +| FCREPO_ACTIVEMQ_TOPIC | /fcrepo/activemq/topic | fedora | | +| FCREPO_BINARYSTORAGE_TYPE | /fcrepo/binarystorage/type | file | | +| FCREPO_BROKER | /fcrepo/broker | tcp://activemq:61616 | | +| FCREPO_CATALINA_OPTS | /fcrepo/catalina/opts | | | +| FCREPO_DB_NAME | /fcrepo/db/name | fcrepo | | +| FCREPO_DB_PASSWORD | /fcrepo/db/password | password | | +| FCREPO_DB_USER | /fcrepo/db/user | admin | | +| FCREPO_JAVA_OPTS | /fcrepo/java/opts | | | +| FCREPO_JWT_ADMIN_TOKEN | /fcrepo/jwt/admin/token | islandora | | +| FCREPO_MODESHAPE_CONFIGURATION | /fcrepo/modeshape/configuration | classpath:/config/repository.json | | +| FCREPO_PERSISTENCE_TYPE | /fcrepo/persistence/type | file | | +| FCREPO_QUEUE | /fcrepo/queue | fedora | | +| FCREPO_S3_BUCKET | /fcrepo/s3/bucket | | | +| FCREPO_S3_PASSWORD | /fcrepo/s3/password | | | +| FCREPO_S3_USER | /fcrepo/s3/user | | | +| FCREPO_TOPIC | /fcrepo/topic | fedora | | + +## Logs + +| Path | Description | +| :------------------------------------------ | :------------ | +| /opt/tomcat/logs/cantaloupe.access.log | [Fcrepo Logs] | +| /opt/tomcat/logs/cantaloupe.application.log | [Fcrepo Logs] | + +[Fcrepo Documentation]: https://wiki.lyrasis.org/display/FF +[Fcrepo]: https://github.com/fcrepo4/fcrepo4 diff --git a/fcrepo/rootfs/etc/confd/conf.d/activemq.xml.toml b/fcrepo/rootfs/etc/confd/conf.d/activemq.xml.toml new file mode 100644 index 00000000..9b161299 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/conf.d/activemq.xml.toml @@ -0,0 +1,7 @@ +[template] +src = "activemq.xml.tmpl" +dest = "/opt/tomcat/webapps/fcrepo/WEB-INF/classes/config/activemq.xml" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/activemq" ] diff --git a/fcrepo/rootfs/etc/confd/conf.d/allowed-external-content.txt.toml b/fcrepo/rootfs/etc/confd/conf.d/allowed-external-content.txt.toml new file mode 100644 index 00000000..bf5ab47b --- /dev/null +++ b/fcrepo/rootfs/etc/confd/conf.d/allowed-external-content.txt.toml @@ -0,0 +1,7 @@ +[template] +src = "allowed-external-content.txt.tmpl" +dest = "/opt/tomcat/webapps/fcrepo/WEB-INF/classes/config/allowed-external-content.txt" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/allow/external" ] diff --git a/fcrepo/rootfs/etc/confd/conf.d/claw.cnd.toml b/fcrepo/rootfs/etc/confd/conf.d/claw.cnd.toml new file mode 100644 index 00000000..298e52d0 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/conf.d/claw.cnd.toml @@ -0,0 +1,6 @@ +[template] +src = "claw.cnd.tmpl" +dest = "/opt/tomcat/webapps/fcrepo/WEB-INF/classes/config/claw.cnd" +uid = 100 +gid = 1000 +mode = "0655" diff --git a/fcrepo/rootfs/etc/confd/conf.d/context.xml.toml b/fcrepo/rootfs/etc/confd/conf.d/context.xml.toml new file mode 100644 index 00000000..16c8f081 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/conf.d/context.xml.toml @@ -0,0 +1,6 @@ +[template] +src = "context.xml.tmpl" +dest = "/opt/tomcat/conf/context.xml" +uid = 100 +gid = 1000 +mode = "0640" diff --git a/fcrepo/rootfs/etc/confd/conf.d/fcrepo-config.xml.toml b/fcrepo/rootfs/etc/confd/conf.d/fcrepo-config.xml.toml new file mode 100644 index 00000000..e994be5b --- /dev/null +++ b/fcrepo/rootfs/etc/confd/conf.d/fcrepo-config.xml.toml @@ -0,0 +1,7 @@ +[template] +src = "fcrepo-config.xml.tmpl" +dest = "/opt/tomcat/webapps/fcrepo/WEB-INF/classes/spring/fcrepo-config.xml" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/activemq" ] diff --git a/fcrepo/rootfs/etc/confd/conf.d/repository.json.toml b/fcrepo/rootfs/etc/confd/conf.d/repository.json.toml new file mode 100644 index 00000000..0bcba8f5 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/conf.d/repository.json.toml @@ -0,0 +1,7 @@ +[template] +src = "repository.json.tmpl" +dest = "/opt/tomcat/webapps/fcrepo/WEB-INF/classes/config/repository.json" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/" ] diff --git a/fcrepo/rootfs/etc/confd/conf.d/setenv.sh.toml b/fcrepo/rootfs/etc/confd/conf.d/setenv.sh.toml new file mode 100644 index 00000000..45d63894 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/conf.d/setenv.sh.toml @@ -0,0 +1,7 @@ +[template] +src = "setenv.sh.tmpl" +dest = "/opt/tomcat/bin/setenv.sh" +uid = 100 +gid = 1000 +mode = "0655" +keys = [ "/modeshape/configuration" ] diff --git a/fcrepo/rootfs/etc/confd/conf.d/syn-settings.xml.toml b/fcrepo/rootfs/etc/confd/conf.d/syn-settings.xml.toml new file mode 100644 index 00000000..58529b7e --- /dev/null +++ b/fcrepo/rootfs/etc/confd/conf.d/syn-settings.xml.toml @@ -0,0 +1,7 @@ +[template] +src = "syn-settings.xml.tmpl" +dest = "/opt/tomcat/conf/syn-settings.xml" +uid = 100 +gid = 1000 +mode = "0655" +keys = [ "/jwt/admin/token" ] diff --git a/fcrepo/rootfs/etc/confd/confd.toml b/fcrepo/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..68d47055 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/fcrepo" diff --git a/fcrepo/rootfs/etc/confd/templates/activemq.xml.tmpl b/fcrepo/rootfs/etc/confd/templates/activemq.xml.tmpl new file mode 100644 index 00000000..ae9a82dd --- /dev/null +++ b/fcrepo/rootfs/etc/confd/templates/activemq.xml.tmpl @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/fcrepo/rootfs/etc/confd/templates/allowed-external-content.txt.tmpl b/fcrepo/rootfs/etc/confd/templates/allowed-external-content.txt.tmpl new file mode 100644 index 00000000..edf7b551 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/templates/allowed-external-content.txt.tmpl @@ -0,0 +1,3 @@ +http://drupal:80/ +{{ range gets "/fcrepo/allow/external/*" }}{{.Value}} +{{ end }} \ No newline at end of file diff --git a/fcrepo/rootfs/etc/confd/templates/claw.cnd.tmpl b/fcrepo/rootfs/etc/confd/templates/claw.cnd.tmpl new file mode 100644 index 00000000..404341f4 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/templates/claw.cnd.tmpl @@ -0,0 +1,34 @@ +/* + * Islandora CLAW namespaces + */ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fcrepo/rootfs/etc/confd/templates/context.xml.tmpl b/fcrepo/rootfs/etc/confd/templates/context.xml.tmpl new file mode 100644 index 00000000..40ad286d --- /dev/null +++ b/fcrepo/rootfs/etc/confd/templates/context.xml.tmpl @@ -0,0 +1,8 @@ + + + + + WEB-INF/web.xml + ${catalina.base}/conf/web.xml + + diff --git a/fcrepo/rootfs/etc/confd/templates/fcrepo-config.xml.tmpl b/fcrepo/rootfs/etc/confd/templates/fcrepo-config.xml.tmpl new file mode 100644 index 00000000..4fbae2e8 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/templates/fcrepo-config.xml.tmpl @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /** = servletContainerAuthFilter,headerProvider,delegatedPrincipalProvider,webACFilter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fcrepo/rootfs/etc/confd/templates/repository.json.tmpl b/fcrepo/rootfs/etc/confd/templates/repository.json.tmpl new file mode 100644 index 00000000..732616b5 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/templates/repository.json.tmpl @@ -0,0 +1,66 @@ +{ + "name" : "repo", + "jndiName" : "", + "workspaces" : { + "predefined" : ["default"], + "default" : "default", + "allowCreation" : true, + "cacheSize" : 10000 + }, + "storage" : { + {{ if eq (getv "/persistence/type" "file") "file" }} + "persistence": { + "type": "file", + "path" : "/data/objects" + }, + {{ end }} + {{ if eq (getv "/persistence/type" "file") "mysql" }} + "persistence": { + "type" : "db", + "connectionUrl": "jdbc:mysql://{{ getv "/db/host" "database" }}:{{ getv "/db/port" "3306" }}/{{ getv "/db/name" "fcrepo" }}?createDatabaseIfNotExist=true", + "driver" : "com.mysql.jdbc.Driver", + "username" : "{{ getv "/db/user" "admin" }}", + "password" : "{{ getv "/db/password" "password" }}" + }, + {{ end }} + {{ if eq (getv "/persistence/type" "file") "postgresql" }} + "persistence": { + "type" : "db", + "connectionUrl": "jdbc:postgresql://{{ getv "/db/host" "database" }}:{{ getv "/db/port" "5432" }}/{{ getv "/db/name" "fcrepo" }}?createDatabaseIfNotExist=true", + "driver" : "org.postgresql.Driver", + "username" : "{{ getv "/db/user" "admin" }}", + "password" : "{{ getv "/db/password" "password" }}" + }, + {{ end }} + {{ if eq (getv "/binarystorage/type" "file") "file" }} + "binaryStorage" : { + "type" : "file", + "directory" : "/data/binaries", + "minimumBinarySizeInBytes" : 4096 + } + {{ end }} + {{ if eq (getv "/binarystorage/type" "file") "s3" }} + "binaryStorage" : { + "type" : "s3", + "username" : "{{ getv "/s3/user" }}", + "password" : "{{ getv "/s3/password" }}", + "bucketName" : "{{ getv "/s3/bucket" }}" + } + {{ end }} + }, + "security" : { + "anonymous" : { + "roles" : ["readonly","readwrite","admin"], + "useOnFailedLogin" : false + }, + "providers" : [ + { "classname" : "org.fcrepo.auth.common.BypassSecurityServletAuthenticationProvider" } + ] + }, + "garbageCollection" : { + "threadPool" : "modeshape-gc", + "initialTime" : "00:00", + "intervalInHours" : 24 + }, + "node-types" : ["fedora-node-types.cnd", "file:/opt/tomcat/webapps/fcrepo/WEB-INF/classes/config/claw.cnd"] +} diff --git a/fcrepo/rootfs/etc/confd/templates/setenv.sh.tmpl b/fcrepo/rootfs/etc/confd/templates/setenv.sh.tmpl new file mode 100644 index 00000000..0f190348 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/templates/setenv.sh.tmpl @@ -0,0 +1,6 @@ +#!/bin/sh +export JAVA_OPTS="{{ getv "/java/opts" "" }}" +export CATALINA_OPTS="{{ getv "/catalina/opts" "" }}" +export CATALINA_OPTS="${CATALINA_OPTS} -Dfcrepo.home=/data/home" +export CATALINA_OPTS="${CATALINA_OPTS} -Dfcrepo.velocity.runtime.log=/opt/tomcat/logs/velocity.log" +export CATALINA_OPTS="${CATALINA_OPTS} -Dfcrepo.modeshape.configuration={{ getv "/modeshape/configuration" "classpath:/config/repository.json" }} -Dfcrepo.jms.baseUrl=http://{{ index (lookupIP (getenv "HOSTNAME")) 0 }}/fcrepo/rest" diff --git a/fcrepo/rootfs/etc/confd/templates/syn-settings.xml.tmpl b/fcrepo/rootfs/etc/confd/templates/syn-settings.xml.tmpl new file mode 100644 index 00000000..db446624 --- /dev/null +++ b/fcrepo/rootfs/etc/confd/templates/syn-settings.xml.tmpl @@ -0,0 +1,6 @@ + + + + {{ getv "/jwt/admin/token" "islandora" }} + + diff --git a/fcrepo/rootfs/etc/cont-init.d/03-fcrepo-setup.sh b/fcrepo/rootfs/etc/cont-init.d/03-fcrepo-setup.sh new file mode 100644 index 00000000..e3bc45ae --- /dev/null +++ b/fcrepo/rootfs/etc/cont-init.d/03-fcrepo-setup.sh @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv bash +set -e +# Key needs to be present before startup otherwise, +# it will ignore subsequent requests even if the key has been generated. +timeout 60 bash -c 'until [[ -f /opt/keys/claw/public.key ]]; do sleep 1; done' diff --git a/fits/.dockerignore b/fits/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/fits/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/fits/Dockerfile b/fits/Dockerfile new file mode 100644 index 00000000..da33a380 --- /dev/null +++ b/fits/Dockerfile @@ -0,0 +1,17 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/tomcat:latest + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + FITSSERVLET_VERSION=1.2.1 && \ + install-war-into-tomcat.sh \ + --name "fits" \ + --url "http://projects.iq.harvard.edu/files/fits/files/fits-${FITSSERVLET_VERSION}.war" \ + --key "13cfcb910092b197757e459353f0c30381febfca6baf3031ac69ff92789b200c" && \ + FITS_VERSION="1.5.0" && \ + FITS_CHECKSUM="1378a78892db103b3a00e45c510b58c70e19a1a401b3720ff4d64a51438bfe0b" && \ + mkdir /opt/fits && \ + wget -N -P /opt/downloads "https://github.com/harvard-lts/fits/releases/download/${FITS_VERSION}/fits-${FITS_VERSION}.zip" && \ + sha256sum "/opt/downloads/fits-${FITS_VERSION}.zip" | cut -f1 -d' ' | xargs test "${FITS_CHECKSUM}" == && \ + unzip /opt/downloads/fits-${FITS_VERSION}.zip -d /opt/fits + +COPY rootfs / diff --git a/fits/README.md b/fits/README.md new file mode 100644 index 00000000..71e7a970 --- /dev/null +++ b/fits/README.md @@ -0,0 +1,36 @@ +# Fits + +Docker image for [Fits] version 5.1.0. + +Please refer to the [Fits Documentation] for more in-depth information. + +As a quick example this will bring up an instance of [Fits], and allow you +to view on . + +```bash +docker run --rm -ti -p 80:80 islandora/fits +``` + +## Dependencies + +Requires `islandora/tomcat` docker image to build. Please refer to the +[Tomcat Image README](../tomcat/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :--------------------------- | :---------------------------- | :------ | :------------------------------------------------------------------------------------------------------------------- | +| FITS_MAX_IN_MEMORY_FILE_SIZE | /fits/max/in/memory/file/size | 4 | Maximum size of an uploaded size kept in memory in MiB. Otherwise temporarily persisted to disk. | +| FITS_MAX_OBJECTS_IN_POOL | /fits/max/objects/in/pool | 5 | Number of objects in FITSServlet object pool. | +| FITS_MAX_REQUEST_SIZE | /fits/max/request/size | 2000 | Maximum size of HTTP Request object in MiB. Must be equal to or larger than the value for /fits/max/upload/file/size | +| FITS_MAX_UPLOAD_FILE_SIZE | /fits/max/upload/file/size | 2000 | Maximum allowable size of uploaded file in MiB. | + +## Logs + +| Path | Description | +| :-------------------------------- | :---------- | +| /opt/tomcat/logs/fits-service.log | | + +[Fits Documentation]: https://wiki.lyrasis.org/display/FF +[Fits]: https://github.com/fits4/fits4 diff --git a/fits/rootfs/etc/confd/conf.d/catalina.properties.toml b/fits/rootfs/etc/confd/conf.d/catalina.properties.toml new file mode 100644 index 00000000..cc6b2a90 --- /dev/null +++ b/fits/rootfs/etc/confd/conf.d/catalina.properties.toml @@ -0,0 +1,6 @@ +[template] +src = "catalina.properties.tmpl" +dest = "/opt/tomcat/conf/catalina.properties" +uid = 100 +gid = 1000 +mode = "0644" diff --git a/fits/rootfs/etc/confd/conf.d/fits-service.properties.toml b/fits/rootfs/etc/confd/conf.d/fits-service.properties.toml new file mode 100644 index 00000000..8a27f595 --- /dev/null +++ b/fits/rootfs/etc/confd/conf.d/fits-service.properties.toml @@ -0,0 +1,7 @@ +[template] +src = "fits-service.properties.tmpl" +dest = "/opt/tomcat/conf/fit-service.properties" +uid = 100 +gid = 1000 +mode = "0644" +keys = ["/"] diff --git a/fits/rootfs/etc/confd/confd.toml b/fits/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..331ea474 --- /dev/null +++ b/fits/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/fits" diff --git a/fits/rootfs/etc/confd/templates/catalina.properties.tmpl b/fits/rootfs/etc/confd/templates/catalina.properties.tmpl new file mode 100644 index 00000000..eeb706b0 --- /dev/null +++ b/fits/rootfs/etc/confd/templates/catalina.properties.tmpl @@ -0,0 +1,147 @@ +# 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. + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageAccess unless the +# corresponding RuntimePermission ("accessClassInPackage."+package) has +# been granted. +package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.jasper.,org.apache.tomcat. +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageDefinition unless the +# corresponding RuntimePermission ("defineClassInPackage."+package) has +# been granted. +# +# by default, no packages are restricted for definition, and none of +# the class loaders supplied with the JDK call checkPackageDefinition. +# +package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,\ +org.apache.jasper.,org.apache.naming.,org.apache.tomcat. + +# +# +# List of comma-separated paths defining the contents of the "common" +# classloader. Prefixes should be used to define what is the repository type. +# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute. +# If left as blank,the JVM system loader will be used as Catalina's "common" +# loader. +# Examples: +# "foo": Add this folder as a class repository +# "foo/*.jar": Add all the JARs of the specified folder as class +# repositories +# "foo/bar.jar": Add bar.jar as a class repository +# +# Note: Values are enclosed in double quotes ("...") in case either the +# ${catalina.base} path or the ${catalina.home} path contains a comma. +# Because double quotes are used for quoting, the double quote character +# may not appear in a path. +common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar" + +# +# List of comma-separated paths defining the contents of the "server" +# classloader. Prefixes should be used to define what is the repository type. +# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute. +# If left as blank, the "common" loader will be used as Catalina's "server" +# loader. +# Examples: +# "foo": Add this folder as a class repository +# "foo/*.jar": Add all the JARs of the specified folder as class +# repositories +# "foo/bar.jar": Add bar.jar as a class repository +# +# Note: Values may be enclosed in double quotes ("...") in case either the +# ${catalina.base} path or the ${catalina.home} path contains a comma. +# Because double quotes are used for quoting, the double quote character +# may not appear in a path. +server.loader= + +fits.home=/opt/fits +# +# List of comma-separated paths defining the contents of the "shared" +# classloader. Prefixes should be used to define what is the repository type. +# Path may be relative to the CATALINA_BASE path or absolute. If left as blank, +# the "common" loader will be used as Catalina's "shared" loader. +# Examples: +# "foo": Add this folder as a class repository +# "foo/*.jar": Add all the JARs of the specified folder as class +# repositories +# "foo/bar.jar": Add bar.jar as a class repository +# Please note that for single jars, e.g. bar.jar, you need the URL form +# starting with file:. +# +# Note: Values may be enclosed in double quotes ("...") in case either the +# ${catalina.base} path or the ${catalina.home} path contains a comma. +# Because double quotes are used for quoting, the double quote character +# may not appear in a path. +shared.loader=${fits.home}/lib/*.jar + +# Default list of JAR files that should not be scanned using the JarScanner +# functionality. This is typically used to scan JARs for configuration +# information. JARs that do not contain such information may be excluded from +# the scan to speed up the scanning process. This is the default list. JARs on +# this list are excluded from all scans. The list must be a comma separated list +# of JAR file names. +# The list of JARs to skip may be over-ridden at a Context level for individual +# scan types by configuring a JarScanner with a nested JarScanFilter. +# The JARs listed below include: +# - Tomcat Bootstrap JARs +# - Tomcat API JARs +# - Catalina JARs +# - Jasper JARs +# - Tomcat JARs +# - Common non-Tomcat JARs +# - Test JARs (JUnit, Cobertura and dependencies) +tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\ +bootstrap.jar,commons-daemon.jar,tomcat-juli.jar,\ +annotations-api.jar,el-api.jar,jsp-api.jar,servlet-api.jar,websocket-api.jar,\ +catalina.jar,catalina-ant.jar,catalina-ha.jar,catalina-storeconfig.jar,\ +catalina-tribes.jar,\ +jasper.jar,jasper-el.jar,ecj-*.jar,\ +tomcat-api.jar,tomcat-util.jar,tomcat-util-scan.jar,tomcat-coyote.jar,\ +tomcat-dbcp.jar,tomcat-jni.jar,tomcat-websocket.jar,\ +tomcat-i18n-en.jar,tomcat-i18n-es.jar,tomcat-i18n-fr.jar,tomcat-i18n-ja.jar,\ +tomcat-juli-adapters.jar,catalina-jmx-remote.jar,catalina-ws.jar,\ +tomcat-jdbc.jar,\ +tools.jar,\ +commons-beanutils*.jar,commons-codec*.jar,commons-collections*.jar,\ +commons-dbcp*.jar,commons-digester*.jar,commons-fileupload*.jar,\ +commons-httpclient*.jar,commons-io*.jar,commons-lang*.jar,commons-logging*.jar,\ +commons-math*.jar,commons-pool*.jar,\ +jstl.jar,taglibs-standard-spec-*.jar,\ +geronimo-spec-jaxrpc*.jar,wsdl4j*.jar,\ +ant.jar,ant-junit*.jar,aspectj*.jar,jmx.jar,h2*.jar,hibernate*.jar,httpclient*.jar,\ +jmx-tools.jar,jta*.jar,log4j*.jar,mail*.jar,slf4j*.jar,\ +xercesImpl.jar,xmlParserAPIs.jar,xml-apis.jar,\ +junit.jar,junit-*.jar,ant-launcher.jar,\ +cobertura-*.jar,asm-*.jar,dom4j-*.jar,icu4j-*.jar,jaxen-*.jar,jdom-*.jar,\ +jetty-*.jar,oro-*.jar,servlet-api-*.jar,tagsoup-*.jar,xmlParserAPIs-*.jar,\ +xom-*.jar + +# Default list of JAR files that should be scanned that overrides the default +# jarsToSkip list above. This is typically used to include a specific JAR that +# has been excluded by a broad file name pattern in the jarsToSkip list. +# The list of JARs to scan may be over-ridden at a Context level for individual +# scan types by configuring a JarScanner with a nested JarScanFilter. +tomcat.util.scan.StandardJarScanFilter.jarsToScan=log4j-core*.jar,log4j-taglib*.jar,log4javascript*.jar + +# String cache configuration. +tomcat.util.buf.StringCache.byte.enabled=true +#tomcat.util.buf.StringCache.char.enabled=true +#tomcat.util.buf.StringCache.trainThreshold=500000 +#tomcat.util.buf.StringCache.cacheSize=5000 +FITS_SERVICE_PROPS=/opt/tomcat/conf/fits-service.properties diff --git a/fits/rootfs/etc/confd/templates/fits-service.properties.tmpl b/fits/rootfs/etc/confd/templates/fits-service.properties.tmpl new file mode 100644 index 00000000..6edb5e70 --- /dev/null +++ b/fits/rootfs/etc/confd/templates/fits-service.properties.tmpl @@ -0,0 +1,8 @@ +# Number of objects in FITSServlet object pool +max.objects.in.pool={{ getv "/max/objects/in/pool" "5" }} +# Maximum allowable size of uploaded file +max.upload.file.size.MB={{ getv "/max/upload/file/size" "2000" }} +# Maximum size of HTTP Request object. Must be equal to or larger than the value for max.upload.file.size.MB +max.request.size.MB={{ getv "/max/request/size" "2000" }} +# Maximum size of an uploaded size kept in memory. Otherwise temporarily persisted to disk. +max.in.memory.file.size.MB={{ getv "/max/in/memory/file/size" "4" }} diff --git a/gemini/.dockerignore b/gemini/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/gemini/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/gemini/Dockerfile b/gemini/Dockerfile new file mode 100644 index 00000000..562a6573 --- /dev/null +++ b/gemini/Dockerfile @@ -0,0 +1,11 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/crayfish:latest + +RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + composer install -d /var/www/crayfish/Gemini && \ + ln -s /var/www/crayfish/Gemini/src /var/www/html && \ + cleanup.sh + +COPY /rootfs / + +WORKDIR /var/www/crayfish/Gemini/ diff --git a/gemini/README.md b/gemini/README.md new file mode 100644 index 00000000..38a79cff --- /dev/null +++ b/gemini/README.md @@ -0,0 +1,32 @@ +# Gemini + +Docker image for [Gemini]. + +## Dependencies + +Requires `islandora/crayfish` docker image to build. Please refer to the +[Crayfish Image README](../crayfish/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :---------------------- | :----------------------- | :---------------------- | :---------------------------------------------------------- | +| GEMINI_DB_DRIVER | /gemini/db/driver | pdo_mysql | The database driver to use | +| GEMINI_DB_HOST | /gemini/db/host | database | The database host | +| GEMINI_DB_NAME | /gemini/db/name | gemini | The database name | +| GEMINI_DB_PASSWORD | /gemini/db/password | password | The database user password | +| GEMINI_DB_PORT | /gemini/db/port | 3306 | The database port | +| GEMINI_DB_ROOT_PASSWORD | /gemini/db/root/password | password | The root user password (used to create the database / user) | +| GEMINI_DB_ROOT_USER | /gemini/db/root/user | root | The root user (used to create the database / user) | +| GEMINI_DB_USER | /gemini/db/user | gemini | The user to create / use when interacting with the database | +| GEMINI_FCREPO_URL | /gemini/fcrepo/url | fcrepo/fcrepo/rest | Fcrepo Rest API URL | +| GEMINI_LOG_LEVEL | /gemini/log/level | WARNING | The log level for Gemini micro-service | + +## Logs + +| Path | Description | +| :---------------------------- | :---------- | +| /var/log/islandora/gemini.log | Gemini Log | + +[Gemini]: https://github.com/Islandora/Crayfish/tree/master/Gemini diff --git a/gemini/rootfs/etc/confd/conf.d/config.yaml.toml b/gemini/rootfs/etc/confd/conf.d/config.yaml.toml new file mode 100644 index 00000000..d6d186a0 --- /dev/null +++ b/gemini/rootfs/etc/confd/conf.d/config.yaml.toml @@ -0,0 +1,7 @@ +[template] +src = "config.yaml.tmpl" +dest = "/var/www/crayfish/Gemini/cfg/config.yaml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/log/level", "/db", "/fcrepo" ] diff --git a/gemini/rootfs/etc/confd/conf.d/create-gemini-database.sh.toml b/gemini/rootfs/etc/confd/conf.d/create-gemini-database.sh.toml new file mode 100644 index 00000000..a98c7fed --- /dev/null +++ b/gemini/rootfs/etc/confd/conf.d/create-gemini-database.sh.toml @@ -0,0 +1,7 @@ +[template] +src = "create-gemini-database.sh.tmpl" +dest = "/var/run/islandora/create-gemini-database.sh" +uid = 0 +gid = 0 +mode = "0700" +keys = [ "/db" ] diff --git a/gemini/rootfs/etc/confd/conf.d/create-gemini-database.sql.toml b/gemini/rootfs/etc/confd/conf.d/create-gemini-database.sql.toml new file mode 100644 index 00000000..5c36f8af --- /dev/null +++ b/gemini/rootfs/etc/confd/conf.d/create-gemini-database.sql.toml @@ -0,0 +1,7 @@ +[template] +src = "create-gemini-database.sql.tmpl" +dest = "/var/run/islandora/create-gemini-database.sql" +uid = 0 +gid = 0 +mode = "0600" +keys = [ "/db" ] diff --git a/gemini/rootfs/etc/confd/confd.toml b/gemini/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..fa30a27d --- /dev/null +++ b/gemini/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/gemini" diff --git a/gemini/rootfs/etc/confd/templates/config.yaml.tmpl b/gemini/rootfs/etc/confd/templates/config.yaml.tmpl new file mode 100644 index 00000000..3693d4c7 --- /dev/null +++ b/gemini/rootfs/etc/confd/templates/config.yaml.tmpl @@ -0,0 +1,24 @@ +--- +debug: True +fedora_resource: + base_url: {{ getv "/fcrepo/url" "http://fcrepo/fcrepo/rest" }} +log: + # Valid log levels are: + # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY, NONE + # log level none won't open logfile + level: {{ getv "/log/level" "DEBUG" }} + file: /var/log/islandora/gemini.log +syn: + # toggles JWT security for service + enable: true + # Path to the syn config file for authentication. + # example can be found here: + # https://github.com/Islandora/Syn/blob/master/conf/syn-settings.example.xml + config: /var/www/crayfish/syn-settings.xml +db.options: + driver: {{ getv "/db/driver" "pdo_mysql" }} + host: {{ getv "/db/host" "database" }} + port: {{ getv "/db/port" "3306" }} + dbname: {{ getv "/db/name" "gemini" }} + user: {{ getv "/db/user" "gemini" }} + password: {{ getv "/db/password" "password" }} diff --git a/gemini/rootfs/etc/confd/templates/create-gemini-database.sh.tmpl b/gemini/rootfs/etc/confd/templates/create-gemini-database.sh.tmpl new file mode 100644 index 00000000..3749c12e --- /dev/null +++ b/gemini/rootfs/etc/confd/templates/create-gemini-database.sh.tmpl @@ -0,0 +1,33 @@ +#!/usr/bin/with-contenv bash + +set -e + +function main { + local driver="{{ getv "/db/driver" "pdo_mysql" }}" + local host="{{ getv "/db/host" "database" }}" + local port="{{ getv "/db/port" "3306" }}" + local user="{{ getv "/db/root/user" "root" }}" + local password="{{ getv "/db/root/password" "password" }}" + + if [[ "${driver}" == "pdo_mysql" ]]; then + echo "Waiting for connection to database." + wait-for-mysql.sh \ + --host "${host}" \ + --port "${port}" \ + --user "${user}" \ + --password "${password}" + + echo "Create database / user if it does not exist." + mysql \ + --user="${user}" \ + --password="${password}" \ + --host="${host}" \ + --port="${port}" \ + --protocol=tcp \ + < /var/run/islandora/create-gemini-database.sql + else + echo "Only MySQL databases are supported for now." + exit 1 + fi +} +main diff --git a/gemini/rootfs/etc/confd/templates/create-gemini-database.sql.tmpl b/gemini/rootfs/etc/confd/templates/create-gemini-database.sql.tmpl new file mode 100644 index 00000000..87f6fce2 --- /dev/null +++ b/gemini/rootfs/etc/confd/templates/create-gemini-database.sql.tmpl @@ -0,0 +1,13 @@ +-- Create gemini database in mariadb or mysql. +CREATE DATABASE IF NOT EXISTS {{ getv "/db/name" "gemini" }} CHARACTER SET utf8 COLLATE utf8_general_ci; + +CREATE TABLE IF NOT EXISTS {{ getv "/db/name" "gemini" }}.Gemini ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + drupal VARCHAR(2048) NOT NULL UNIQUE, + fedora VARCHAR(2048) NOT NULL UNIQUE +) ENGINE=InnoDB; + +-- Create gemini_user and grant rights. +CREATE USER IF NOT EXISTS '{{ getv "/db/user" "gemini" }}'@'%' IDENTIFIED BY '{{ getv "/db/password" "password" }}'; +GRANT ALL PRIVILEGES ON {{ getv "/db/name" "gemini" }}.* to '{{ getv "/db/user" "gemini" }}'@'%'; +FLUSH PRIVILEGES; diff --git a/gemini/rootfs/etc/cont-init.d/03-gemini-setup.sh b/gemini/rootfs/etc/cont-init.d/03-gemini-setup.sh new file mode 100644 index 00000000..2fa3ee8a --- /dev/null +++ b/gemini/rootfs/etc/cont-init.d/03-gemini-setup.sh @@ -0,0 +1,4 @@ +#!/usr/bin/with-contenv bash +set -e +/var/run/islandora/create-gemini-database.sh +php bin/console --no-interaction migrations:migrate diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..00257aa5 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,9 @@ +org.gradle.parallel=true +# Will be used as a tag to denote the images release. +version=0.0.1 +# The repository path to use for all images. +repository=islandora +# Tags to add to all images, can be overridden on a project by project basis. +tags= +# By default all images have a 'prod' environment, even if they ignore it in the build, although some also have 'dev'. +environments=prod diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 GIT binary patch literal 58694 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..a4b44297 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..2fe81a7d --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +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 +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..62bd9b9c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/homarus/.dockerignore b/homarus/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/homarus/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/homarus/Dockerfile b/homarus/Dockerfile new file mode 100644 index 00000000..3905fce6 --- /dev/null +++ b/homarus/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/crayfish:latest + +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + apk-install.sh \ + ffmpeg \ + && \ + cleanup.sh + +RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + composer install -d /var/www/crayfish/Homarus && \ + ln -s /var/www/crayfish/Homarus/src /var/www/html && \ + cleanup.sh + +COPY /rootfs / + +RUN chown -R nginx:nginx /var/www + +WORKDIR /var/www/crayfish/Homarus/ diff --git a/homarus/README.md b/homarus/README.md new file mode 100644 index 00000000..8d38ca37 --- /dev/null +++ b/homarus/README.md @@ -0,0 +1,23 @@ +# Homarus + +Docker image for [Homarus]. + +## Dependencies + +Requires `islandora/crayfish` docker image to build. Please refer to the +[Crayfish Image README](../crayfish/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------- | :----------------- | :------ | :-------------------------------------- | +| HOMARUS_LOG_LEVEL | /homarus/log/level | WARNING | The log level for Homarus micro-service | + +## Logs + +| Path | Description | +| :----------------------------- | :---------- | +| /var/log/islandora/homarus.log | Homarus Log | + +[Homarus]: https://github.com/Islandora/Crayfish/tree/master/Homarus diff --git a/homarus/rootfs/etc/confd/conf.d/config.yaml.toml b/homarus/rootfs/etc/confd/conf.d/config.yaml.toml new file mode 100644 index 00000000..73251e38 --- /dev/null +++ b/homarus/rootfs/etc/confd/conf.d/config.yaml.toml @@ -0,0 +1,7 @@ +[template] +src = "config.yaml.tmpl" +dest = "/var/www/crayfish/Homarus/cfg/config.yaml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/log/level" ] diff --git a/homarus/rootfs/etc/confd/confd.toml b/homarus/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..a1478f64 --- /dev/null +++ b/homarus/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/homarus" diff --git a/homarus/rootfs/etc/confd/templates/config.yaml.tmpl b/homarus/rootfs/etc/confd/templates/config.yaml.tmpl new file mode 100644 index 00000000..09261879 --- /dev/null +++ b/homarus/rootfs/etc/confd/templates/config.yaml.tmpl @@ -0,0 +1,41 @@ +--- +homarus: + # path to the ffmpeg executable + executable: ffmpeg + mime_types: + valid: + - video/mp4 + - video/x-msvideo + - video/ogg + - audio/x-wav + - audio/mpeg + - audio/aac + - image/jpeg + - image/png + default: video/mp4 + mime_to_format: + valid: + - video/mp4_mp4 + - video/x-msvideo_avi + - video/ogg_ogg + - audio/x-wav_wav + - audio/mpeg_mp3 + - audio/aac_m4a + - image/jpeg_image2pipe + - image/png_image2pipe + default: mp4 + +log: + # Valid log levels are: + # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY, NONE + # log level none won't open logfile + level: {{ getv "/log/level" "WARNING" }} + file: /var/log/islandora/homarus.log + +syn: + # toggles JWT security for service + enable: True + # Path to the syn config file for authentication. + # example can be found here: + # https://github.com/Islandora/Syn/blob/master/conf/syn-settings.example$ + config: /var/www/crayfish/syn-settings.xml diff --git a/houdini/.dockerignore b/houdini/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/houdini/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/houdini/Dockerfile b/houdini/Dockerfile new file mode 100644 index 00000000..1bce19af --- /dev/null +++ b/houdini/Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/imagemagick:latest as imagemagick + +FROM islandora/crayfish:latest + +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + --mount=type=bind,from=imagemagick,source=/home/builder/packages/x86_64,target=/packages \ + --mount=type=bind,from=imagemagick,source=/etc/apk/keys,target=/etc/apk/keys \ + --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + apk add /packages/imagemagick-*.apk && \ + composer install -d /var/www/crayfish/Houdini && \ + php /var/www/crayfish/Houdini/bin/console cache:clear && \ + ln -s /var/www/crayfish/Houdini/public /var/www/html && \ + cleanup.sh + +COPY /rootfs / + +RUN chown -R nginx:nginx /var/www + +WORKDIR /var/www/crayfish/Houdini/ diff --git a/houdini/README.md b/houdini/README.md new file mode 100644 index 00000000..a345ecb8 --- /dev/null +++ b/houdini/README.md @@ -0,0 +1,23 @@ +# Houdini + +Docker image for [Houdini]. + +## Dependencies + +Requires `islandora/crayfish` docker image to build. Please refer to the +[Crayfish Image README](../crayfish/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------- | :----------------- | :------ | :-------------------------------------- | +| HOUDINI_LOG_LEVEL | /houdini/log/level | WARNING | The log level for Houdini micro-service | + +## Logs + +| Path | Description | +| :----------------------------- | :---------- | +| /var/log/islandora/houdini.log | Houdini Log | + +[Houdini]: https://github.com/Islandora/Crayfish/tree/master/Houdini diff --git a/houdini/rootfs/etc/confd/conf.d/monolog.yaml.toml b/houdini/rootfs/etc/confd/conf.d/monolog.yaml.toml new file mode 100644 index 00000000..3ceebd7d --- /dev/null +++ b/houdini/rootfs/etc/confd/conf.d/monolog.yaml.toml @@ -0,0 +1,7 @@ +[template] +src = "monolog.yaml.tmpl" +dest = "/var/www/crayfish/Houdini/config/packages/monolog.yaml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/log/level" ] diff --git a/houdini/rootfs/etc/confd/confd.toml b/houdini/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..b23e63e9 --- /dev/null +++ b/houdini/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/houdini" diff --git a/houdini/rootfs/etc/confd/templates/monolog.yaml.tmpl b/houdini/rootfs/etc/confd/templates/monolog.yaml.tmpl new file mode 100644 index 00000000..1de34b03 --- /dev/null +++ b/houdini/rootfs/etc/confd/templates/monolog.yaml.tmpl @@ -0,0 +1,7 @@ +monolog: + handlers: + houdini: + type: rotating_file + path: /var/log/islandora/houdini.log + level: {{ getv "/log/level" "WARNING" }} + max_files: 1 diff --git a/houdini/rootfs/var/www/crayfish/Houdini/config/packages/crayfish_commons.yaml b/houdini/rootfs/var/www/crayfish/Houdini/config/packages/crayfish_commons.yaml new file mode 100644 index 00000000..67e492b1 --- /dev/null +++ b/houdini/rootfs/var/www/crayfish/Houdini/config/packages/crayfish_commons.yaml @@ -0,0 +1,3 @@ +crayfish_commons: + syn_config: /var/www/crayfish/syn-settings.xml + syn_enabled: True diff --git a/houdini/rootfs/var/www/crayfish/Houdini/config/services.yaml b/houdini/rootfs/var/www/crayfish/Houdini/config/services.yaml new file mode 100644 index 00000000..12eb951d --- /dev/null +++ b/houdini/rootfs/var/www/crayfish/Houdini/config/services.yaml @@ -0,0 +1,33 @@ +parameters: + app.executable: convert + app.formats.valid: + - image/jpeg + - image/png + - image/tiff + - image/jp2 + app.formats.default: image/jpeg + +services: + # default configuration for services in *this* file + _defaults: + autowire: true # Automatically injects dependencies in your services. + autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + + # makes classes in src/ available to be used as services + # this creates a service per class whose id is the fully-qualified class name + App\Islandora\Houdini\: + resource: '../src/*' + exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' + + # controllers are imported separately to make sure services can be injected + # as action arguments even if you don't extend any base controller class + App\Islandora\Houdini\Controller\HoudiniController: + public: false + bind: + $formats: '%app.formats.valid%' + $default_format: '%app.formats.default%' + $executable: '%app.executable%' + tags: ['controller.service_arguments'] + + # add more service definitions when explicit configuration is needed + # please note that last definitions always *replace* previous ones diff --git a/hypercube/.dockerignore b/hypercube/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/hypercube/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/hypercube/Dockerfile b/hypercube/Dockerfile new file mode 100644 index 00000000..8b85ae4e --- /dev/null +++ b/hypercube/Dockerfile @@ -0,0 +1,25 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/crayfish:latest + +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + apk-install.sh \ + poppler-utils \ + tesseract-ocr \ + tesseract-ocr-data-fra \ + tesseract-ocr-data-spa \ + tesseract-ocr-data-ita \ + tesseract-ocr-data-por \ + tesseract-ocr-data-hin \ + tesseract-ocr-data-deu \ + tesseract-ocr-data-jpn \ + tesseract-ocr-data-rus \ + && \ + composer install -d /var/www/crayfish/Hypercube && \ + ln -s /var/www/crayfish/Hypercube/src /var/www/html && \ + cleanup.sh + +COPY /rootfs / + +WORKDIR /var/www/crayfish/Hypercube/ diff --git a/hypercube/README.md b/hypercube/README.md new file mode 100644 index 00000000..54ef6778 --- /dev/null +++ b/hypercube/README.md @@ -0,0 +1,24 @@ +# Hypercube + +Docker image for [Hypercube]. + +## Dependencies + +Requires `islandora/crayfish` docker image to build. Please refer to the +[Crayfish Image README](../crayfish/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------- | :-------------------- | :---------------------- | :---------------------------------------- | +| HYPERCUBE_FCREPO_URL | /hypercube/fcrepo/url | fcrepo/fcrepo/rest | Fcrepo Rest API URL | +| HYPERCUBE_LOG_LEVEL | /hypercube/log/level | WARNING | The log level for Hypercube micro-service | + +## Logs + +| Path | Description | +| :------------------------------- | :------------ | +| /var/log/islandora/hypercube.log | Hypercube Log | + +[Hypercube]: https://github.com/Islandora/Crayfish/tree/master/Hypercube diff --git a/hypercube/rootfs/etc/confd/conf.d/config.yaml.toml b/hypercube/rootfs/etc/confd/conf.d/config.yaml.toml new file mode 100644 index 00000000..82bc8a80 --- /dev/null +++ b/hypercube/rootfs/etc/confd/conf.d/config.yaml.toml @@ -0,0 +1,7 @@ +[template] +src = "config.yaml.tmpl" +dest = "/var/www/crayfish/Hypercube/cfg/config.yaml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/log/level", "/fcrepo/url" ] diff --git a/hypercube/rootfs/etc/confd/confd.toml b/hypercube/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..54a65ee1 --- /dev/null +++ b/hypercube/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/hypercube" diff --git a/hypercube/rootfs/etc/confd/templates/config.yaml.tmpl b/hypercube/rootfs/etc/confd/templates/config.yaml.tmpl new file mode 100644 index 00000000..d88a913b --- /dev/null +++ b/hypercube/rootfs/etc/confd/templates/config.yaml.tmpl @@ -0,0 +1,20 @@ +--- +hypercube: + # path to the convert executable + tesseract_executable: tesseract + pdftotext_executable: pdftotext +fedora_resource: + base_url: {{ getv "/fcrepo/url" "fcrepo/fcrepo/rest" }} +log: + # Valid log levels are: + # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY, NONE + # log level none won't open logfile + level: {{ getv "/log/level" "WARNING" }} + file: /var/log/islandora/hypercube.log +syn: + # toggles JWT security for service + enable: true + # Path to the syn config file for authentication. + # example can be found here: + # https://github.com/Islandora/Syn/blob/master/conf/syn-settings.example.xml + config: /var/www/crayfish/syn-settings.xml diff --git a/imagemagick/.dockerignore b/imagemagick/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/imagemagick/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/imagemagick/Dockerfile b/imagemagick/Dockerfile new file mode 100644 index 00000000..8bb93f1d --- /dev/null +++ b/imagemagick/Dockerfile @@ -0,0 +1,45 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/abuild:latest + +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + apk --update add \ + libheif-dev \ + libwebp-dev \ + chrpath \ + fftw-dev \ + fontconfig-dev \ + freetype-dev \ + ghostscript-dev \ + ghostscript-fonts \ + graphviz \ + lcms2-dev \ + libjpeg-turbo-dev \ + libpng-dev \ + librsvg-dev \ + libtool \ + libwmf-dev \ + libx11-dev \ + libxext-dev \ + libxml2-dev \ + openexr-dev \ + openjpeg-dev \ + pango-dev \ + perl-dev \ + tiff-dev \ + zlib-dev + +COPY /build /build + +WORKDIR /build + +RUN chown -R builder /build + +ARG PACKAGER="Nigel Banks " + +USER builder + +RUN export PACKAGER="${PACKAGER}" && \ + abuild-keygen -ain && \ + abuild-apk update && \ + abuild diff --git a/imagemagick/README.md b/imagemagick/README.md new file mode 100644 index 00000000..4ed14157 --- /dev/null +++ b/imagemagick/README.md @@ -0,0 +1,25 @@ +# Imagemagick + +Docker image for `imagemagick` package. + +It is not meant to be deployed as a service, but rather as base to import our +custom imagemagick build into containers like `islandora/houdini`. + +Consumers are expected to follow this pattern: + +```dockerfile +FROM islandora/imagemagick:latest as imagemagick + +FROM some_image:latest + +RUN --mount=type=bind,from=imagemagick,source=/home/builder/packages/x86_64,target=/packages \ + --mount=type=bind,from=imagemagick,source=/etc/apk/keys,target=/etc/apk/keys \ + apk add /packages/imagemagick-*.apk && \ + ... other build steps ... && \ + cleanup.sh +``` + +## Dependencies + +Requires `islandora/abuild` docker image to build. Please refer to the +[ABuild Image README](../abuild/README.md) for additional information. diff --git a/imagemagick/build/APKBUILD b/imagemagick/build/APKBUILD new file mode 100644 index 00000000..99c1e70a --- /dev/null +++ b/imagemagick/build/APKBUILD @@ -0,0 +1,200 @@ +# Adapted from: https://git.alpinelinux.org/aports/commit/?id=f0a480dcf122a955d24fffe94517875a4e32061e +# Contributor: Nigel Banks +# Contributor: Łukasz Jendrysik +# Contributor: Carlo Landmeter +# Maintainer: Natanael Copa +pkgname=imagemagick +_pkgname=ImageMagick +pkgver=7.0.10.10 +pkgrel=0 +_pkgver=${pkgver%.*}-${pkgver##*.} +_abiver=7 +pkgdesc="Collection of tools and libraries for many image formats" +url="https://www.imagemagick.org/" +arch="all" +license="ImageMagick" +options="libtool" +makedepends=" + chrpath + fftw-dev + fontconfig-dev + freetype-dev + ghostscript-dev + lcms2-dev + libheif-dev + libjpeg-turbo-dev + libpng-dev + libtool + libwebp-dev + libwmf-dev + libx11-dev + libxext-dev + libxml2-dev + openexr-dev + openjpeg-dev + pango-dev + perl-dev + tiff-dev + zlib-dev" + +case "$CARCH" in + s390x) ;; + mips*) options="!check" ;; + *) makedepends="$makedepends librsvg-dev" ;; +esac + +checkdepends="freetype fontconfig ghostscript ghostscript-fonts lcms2 graphviz" +subpackages=" + $pkgname-doc + $pkgname-static + $pkgname-dev + $pkgname-c++:_cxx + $pkgname-libs + $pkgname-perlmagick:_perlmagick + $pkgname-perlmagick-doc:_perlmagick_doc + " +source="$_pkgname-$_pkgver.tar.gz::https://github.com/ImageMagick/ImageMagick/archive/$_pkgver.tar.gz + disable-avaraging-tests.patch" +builddir="$srcdir/$_pkgname-$_pkgver" + +# secfixes: +# 7.0.9.7-r0: +# - CVE-2019-19952 +# 7.0.8.62-r0: +# - CVE-2019-17547 +# 7.0.8.56-r0: +# - CVE-2019-17541 +# - CVE-2019-17540 +# - CVE-2019-14981 +# - CVE-2019-13454 +# 7.0.8.53-r0: +# - CVE-2019-13391 +# - CVE-2019-13311 +# - CVE-2019-13310 +# - CVE-2019-13309 +# - CVE-2019-13308 +# - CVE-2019-13307 +# - CVE-2019-13306 +# - CVE-2019-13305 +# - CVE-2019-13304 +# - CVE-2019-13303 +# - CVE-2019-13302 +# - CVE-2019-13301 +# - CVE-2019-13300 +# - CVE-2019-13299 +# - CVE-2019-13298 +# - CVE-2019-13297 +# - CVE-2019-13296 +# - CVE-2019-13295 +# - CVE-2019-13137 +# - CVE-2019-13136 +# - CVE-2019-13135 +# - CVE-2019-13134 +# - CVE-2019-13133 +# 7.0.8.44-r0: +# - CVE-2019-19949 +# - CVE-2019-19948 +# - CVE-2019-16713 +# - CVE-2019-16712 +# - CVE-2019-16711 +# - CVE-2019-15141 +# - CVE-2019-15140 +# - CVE-2019-15139 +# - CVE-2019-14980 +# - CVE-2019-11598 +# - CVE-2019-11597 +# - CVE-2019-11472 +# 7.0.8.38-r0: +# - CVE-2019-9956 +# - CVE-2019-16710 +# - CVE-2019-16709 +# - CVE-2019-16708 +# - CVE-2019-10650 +# - CVE-2019-10649 + +build() { + case "$CARCH" in + s390x) ;; + *) _conf_args="--with-rsvg" ;; + esac + + # fix doc dir, Gentoo bug 91911 + sed -i -e \ + 's:DOCUMENTATION_PATH="$DATA_DIR/doc/$DOCUMENTATION_RELATIVE_PATH":DOCUMENTATION_PATH="/usr/share/doc/imagemagick":g' \ + configure + ./configure -C \ + --build=$CBUILD \ + --host=$CHOST \ + --prefix=/usr \ + --sysconfdir=/etc \ + --mandir=/usr/share/man \ + --infodir=/usr/share/info \ + --enable-static \ + --disable-openmp \ + --with-threads \ + --with-x \ + --with-tiff \ + --with-png \ + --with-webp \ + --with-gslib \ + --with-gs-font-dir=/usr/share/fonts/Type1 \ + --with-heic \ + --with-modules \ + --with-xml \ + --with-perl \ + --with-perl-options="PREFIX=/usr INSTALLDIRS=vendor" \ + --with-dps=no \ + --with-fpx=no \ + --with-gslib=no \ + --with-gvc=no \ + --with-rsvg=no \ + $_conf_args + make -j $(nproc) +} + +check() { + # Test disabled to reduce build time, manually check if you are modifing this package script. + # make check -j $(nproc) + return 0 +} + +package() { + make -j1 DESTDIR="$pkgdir" install + if ! [ -e "$pkgdir"/usr/lib/libMagickCore-$_abiver.Q16HDRI.so ]; then + error "Has ABI verision changed? (current is $_abiver)" + return 1 + fi + + # we cannot let abuild delete the *.la files due to we need *.la + # for the modules + rm "$pkgdir"/usr/lib/*.la + + find "$pkgdir" \( -name '.packlist' -o -name 'perllocal.pod' \ + -o -name '*.bs' \) -delete +} + +_cxx() { + pkgdesc="ImageMagick Magick++ library (C++ bindings)" + mkdir -p "$subpkgdir"/usr/lib + mv "$pkgdir"/usr/lib/libMagick++*.so.* "$subpkgdir"/usr/lib/ +} + +_perlmagick() { + pkgdesc="PerlMagick Perl Modules for ImageMagick" + mkdir -p "$subpkgdir"/usr/lib + mv "$pkgdir"/usr/lib/perl5 "$subpkgdir"/usr/lib/ + # Strip all the rpath that include /home + scanelf --recursive --rpath "$subpkgdir" | awk '/home/{print $3;}' | xargs chrpath -d +# chrpath -d "$subpkgdir"/usr/lib/perl5/vendor_perl/auto/Image/Magick/Q16HDRI/Q16HDRI.so +# chrpath -d "$subpkgdir"/usr/lib/perl5/vendor_perl/auto/Image/Magick/Magick.so +} + +_perlmagick_doc() { + pkgdesc="PerlMagick Perl Module Documentation for ImageMagick" + mkdir -p "$subpkgdir" + cd "$builddir"/PerlMagick + make -j1 DESTDIR="$subpkgdir" doc_vendor_install +} + +sha512sums="2c9e0f0f172572473078651d11ee1a173ec8a0965310c04c9e96f2bfa1249362fb775f23681d5d866ec6b8847125a001814ddd91dda44ae613602127819b2894 ImageMagick-7.0.10-10.tar.gz +58afb2da075a6208b6a990ff297b3a827d260687c3355198a8b4d987e1596c0b0cd78aff6f0be0e1896e537fbe44a3d467473183f5f149664ea6e6fb3d3291a9 disable-avaraging-tests.patch" \ No newline at end of file diff --git a/imagemagick/build/disable-avaraging-tests.patch b/imagemagick/build/disable-avaraging-tests.patch new file mode 100644 index 00000000..8e715f81 --- /dev/null +++ b/imagemagick/build/disable-avaraging-tests.patch @@ -0,0 +1,26 @@ +The avaraging tests seems to be flaky due to rounding errors. Test fails on +x86 and s390x + +https://github.com/ImageMagick/ImageMagick/issues/1576#issuecomment-494595404 + +diff --git a/Magick++/tests/tests.tap b/Magick++/tests/tests.tap +index b5c15ff..bb83980 100755 +--- a/Magick++/tests/tests.tap ++++ b/Magick++/tests/tests.tap +@@ -8,14 +8,14 @@ + # + subdir=Magick++/tests + . ./common.shi +-echo "1..13" ++echo "1..12" + + SRCDIR=${top_srcdir}/${subdir}/ + export SRCDIR + + cd ${subdir} || exit 1 + +-for mytest in appendImages attributes averageImages coalesceImages coderInfo color colorHistogram exceptions geometry montageImages morphImages readWriteBlob readWriteImages ++for mytest in appendImages attributes coalesceImages coderInfo color colorHistogram exceptions geometry montageImages morphImages readWriteBlob readWriteImages + do + ./${mytest} && echo "ok" || echo "not ok" + done diff --git a/java/.dockerignore b/java/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/java/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/java/Dockerfile b/java/Dockerfile new file mode 100644 index 00000000..38d192c3 --- /dev/null +++ b/java/Dockerfile @@ -0,0 +1,15 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/base:latest + +# Install packages and tools required by all downstream images. +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + apk-install.sh \ + openjdk8 \ + maven \ + && \ + cleanup.sh + +ENV JAVA_HOME=/usr/lib/jvm/default-jvm + +COPY rootfs / diff --git a/java/README.md b/java/README.md new file mode 100644 index 00000000..0816133a --- /dev/null +++ b/java/README.md @@ -0,0 +1,15 @@ +# Java + +Docker image for [Java] OpenJDK version 8. + +Please refer to the [Java Documentation] for more in-depth information. + +Acts as base Docker image for all Java based services. + +## Dependencies + +Requires `islandora/base` docker image to build. Please refer to the +[Base Image README](../base/README.md) for additional information. + +[Java Documentation]: https://docs.oracle.com/en/java/ +[Java]: https://www.java.com/ diff --git a/java/rootfs/usr/local/bin/install-apache-service.sh b/java/rootfs/usr/local/bin/install-apache-service.sh new file mode 100755 index 00000000..6c692766 --- /dev/null +++ b/java/rootfs/usr/local/bin/install-apache-service.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash + +# Exit non-zero if any command fails. +set -e + +readonly PROGNAME=$(basename $0) +readonly ARGS="$@" + +readonly DOWNLOAD_CACHE_DIRECTORY=/opt/downloads + +function usage() { + cat <<- EOF + usage: $PROGNAME options [FILE]... + + Installs the given apache service in /opt. Creates a user/group for the + service and ensuring that all files are owned by that user/group. + + Additional parameters are files to be removed from the installation to save + on space. Things like "examples", and "docs". + + OPTIONS: + -n --name The name of the services to install (used to create user/group and install directory). + -v --version Version of apache service to install. + -k --key GPG Key used to verify the downloaded file. + -m --mirror The URL where the package is downloaded from. + -f --file The name of the file to download. + -h --help Show this help. + -x --debug Debug this script. + + Examples: + Install ActiveMQ: + $PROGNAME \\ + --name "activemq" \\ + --version "5.14.5" \\ + --key "62ED4DF0BACB8793" \\ + --mirror "https://archive.apache.org/dist/activemq/5.14.5" \\ + --file "apache-activemq-5.14.5-bin.tar.gz" \\ + examples webapps-demo docs +EOF +} + +function cmdline() { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --name) args="${args}-n ";; + --version) args="${args}-v ";; + --key) args="${args}-k ";; + --mirror) args="${args}-m ";; + --file) args="${args}-f ";; + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + while getopts "n:v:k:m:f:hx" OPTION + do + case $OPTION in + n) + readonly NAME=${OPTARG} + ;; + v) + readonly VERSION=${OPTARG} + ;; + k) + readonly KEY=${OPTARG} + ;; + m) + readonly MIRROR=${OPTARG} + ;; + f) + readonly FILE=${OPTARG} + ;; + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + if [[ -z $NAME || -z $VERSION || -z $KEY || -z $MIRROR || -z $FILE ]]; then + echo "Missing one or more required options: --name --version --key --mirror --file" + exit 1 + fi + + # All remaning parameters are files to be removed from the installation. + shift $((OPTIND-1)) + readonly REMOVE=("$@") + + return 0 +} + +function main { + cmdline ${ARGS} + local install_directory=/opt/${NAME} + local user=${NAME} + local group=${NAME} + # Expects that the RUN uses ${DOWNLOAD_CACHE_DIRECTORY} as a cache + # https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md#run---mounttypecache + wget -N -P ${DOWNLOAD_CACHE_DIRECTORY} ${MIRROR}/${FILE} + wget -N -P ${DOWNLOAD_CACHE_DIRECTORY} ${MIRROR}/${FILE}.asc + gpg --keyserver hkp://keys.gnupg.net:80 --recv-key ${KEY} + gpg --verify ${DOWNLOAD_CACHE_DIRECTORY}/${FILE}.asc ${DOWNLOAD_CACHE_DIRECTORY}/${FILE} + mkdir ${install_directory} + addgroup ${group} && \ + adduser --system --disabled-password --no-create-home --ingroup ${group} --shell /sbin/nologin --home ${install_directory} ${user} + chown ${user}:${group} ${install_directory} + s6-setuidgid ${user} tar -xzf /opt/downloads/${FILE} -C ${install_directory} --strip-components 1 + for i in "${REMOVE[@]}"; do + rm -fr "${install_directory}/${i}" + done + cleanup.sh +} +main diff --git a/karaf/.dockerignore b/karaf/.dockerignore new file mode 100644 index 00000000..42061c01 --- /dev/null +++ b/karaf/.dockerignore @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/karaf/Dockerfile b/karaf/Dockerfile new file mode 100644 index 00000000..36a2a374 --- /dev/null +++ b/karaf/Dockerfile @@ -0,0 +1,25 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/java:latest + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + KARAF_VERSION="4.0.8" && \ + install-apache-service.sh \ + --name karaf \ + --version "${KARAF_VERSION}" \ + --key "BFF2EE42C8282E76" \ + --mirror "https://archive.apache.org/dist/karaf/${KARAF_VERSION}" \ + --file "apache-karaf-${KARAF_VERSION}.tar.gz" \ + demos \ + && \ + sed -i 's@http://repo1@https://repo1@' /opt/karaf/etc/org.ops4j.pax.url.mvn.cfg && \ + chown -R karaf:karaf /opt/karaf && \ + rm -rf /opt/karaf/instances/* && \ + cleanup.sh + +WORKDIR /opt/karaf + +EXPOSE 8101 1099 44444 8181 + +VOLUME [ "/opt/karaf/data/" ] + +COPY rootfs / diff --git a/karaf/README.md b/karaf/README.md new file mode 100644 index 00000000..509bf575 --- /dev/null +++ b/karaf/README.md @@ -0,0 +1,72 @@ +# Karaf + +Docker image for [Karaf] version 4.0.8 + +Please refer to the [Karaf Documentation] for more in-depth information. + +As a quick example this will bring up an instance of karaf, and allow you to +log view the [WebConsole] on as the user `admin` with +the password `password`. + +```bash +docker run --rm -ti \ + -p 8181:8181 \ + -e "KARAF_ADMIN_NAME=admin" \ + -e "KARAF_ADMIN_PASSWORD=password" \ + islandora/karaf +``` + +## Dependencies + +Requires `islandora/karaf` docker image to build. + +## Ports + +| Port | Description | +| :---- | :----------- | +| 8101 | [SSH] | +| 1099 | [RMI] | +| 44444 | [JMX] | +| 8181 | [WebConsole] | + +## Volumes + +| Path | Description | +| :-------------- | :-------------------------- | +| /opt/karaf/data | [Karaf Directory Structure] | + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------- | :-------------------- | :------- | :------------------ | +| KARAF_ADMIN_NAME | /karaf/admin/name | admin | Admin user name | +| KARAF_ADMIN_PASSWORD | /karaf/admin/password | password | Admin user password | + +Additional users/groups/etc can be defined by adding more environment variables, +following the above conventions: + +| Environment Variable | Etcd Key | Description | +| :------------------------- | :-------------------------- | :------------------------------- | +| KARAF_USER_{USER}_NAME | /karaf/user/{USER}/name | See [Security]: users.properties | +| KARAF_USER_{USER}_PASSWORD | /karaf/user/{USER}/password | See [Security]: users.properties | +| KARAF_USER_{USER}_ROLES | /karaf/user/{USER}/roles | See [Security]: users.properties | +| KARAF_GROUP_{GROUP}_NAME | /karaf/group/{GROUP}/name | See [Security]: users.properties | +| KARAF_GROUP_{GROUP}_ROLES | /karaf/group/{GROUP}/roles | See [Security]: users.properties | + +*N.B. These do not have defaults.* + +## Logs + +| Path | Description | +| :---------------------------- | :---------- | +| /opt/karaf/data/log/karaf.log | [Karaf Log] | + +[JMX]: https://karaf.apache.org/manual/latest/#_monitoring_and_management_using_jmx +[Karaf Directory Structure]: https://karaf.apache.org/manual/latest/#_directory_structure +[Karaf Documentation]: https://islandora.github.io/documentation/ +[Karaf Log]: https://karaf.apache.org/manual/latest/#_log +[Karaf]: https://github.com/Islandora/karaf +[RMI]: https://karaf.apache.org/manual/latest/monitoring +[Security]: https://karaf.apache.org/manual/latest/security +[SSH]: https://karaf.apache.org/manual/latest/remote +[WebConsole]: https://karaf.apache.org/manual/latest/webconsole diff --git a/karaf/rootfs/etc/confd/conf.d/users.properties.toml b/karaf/rootfs/etc/confd/conf.d/users.properties.toml new file mode 100644 index 00000000..267cd2a2 --- /dev/null +++ b/karaf/rootfs/etc/confd/conf.d/users.properties.toml @@ -0,0 +1,7 @@ +[template] +src = "users.properties.tmpl" +dest = "/opt/karaf/etc/users.properties" +uid = 100 +gid = 1000 +mode = "0640" +keys = [ "/karaf" ] diff --git a/karaf/rootfs/etc/confd/templates/users.properties.tmpl b/karaf/rootfs/etc/confd/templates/users.properties.tmpl new file mode 100644 index 00000000..a97612e9 --- /dev/null +++ b/karaf/rootfs/etc/confd/templates/users.properties.tmpl @@ -0,0 +1,18 @@ +# +# This file contains the users, groups, and roles. +# Each line has to be of the format: +# +# USER=PASSWORD,ROLE1,ROLE2,... +# USER=PASSWORD,_g_:GROUP,... +# _g_\:GROUP=ROLE1,ROLE2,... +# +# All users, groups, and roles entered in this file are available after Karaf startup +# and modifiable via the JAAS command group. These users reside in a JAAS domain +# with the name "karaf". +# +{{ getv "/karaf/admin/name" "admin" }} = {{ getv "/karaf/admin/password" "password" }}, _g_:admingroup +_g_\:admingroup = group,admin,manager,viewer,systembundles +{{ range $dir := lsdir "/karaf/user" }}{{ getv (printf "/karaf/user/%s/name" $dir) }} = {{ getv (printf "/karaf/user/%s/password" $dir) }} {{ getv (printf "/karaf/user/%s/roles" $dir) }} +{{ end }} +{{ range $dir := lsdir "/karaf/group" }}{{ getv (printf "/karaf/group/%s/name" $dir) }} = {{ getv (printf "/karaf/group/%s/roles" $dir) }} +{{ end }} diff --git a/karaf/rootfs/etc/cont-init.d/04-karaf-startup.sh b/karaf/rootfs/etc/cont-init.d/04-karaf-startup.sh new file mode 100644 index 00000000..a09a8c3c --- /dev/null +++ b/karaf/rootfs/etc/cont-init.d/04-karaf-startup.sh @@ -0,0 +1,4 @@ +#!/usr/bin/with-contenv bash +set -e +# On startup if we exit with sigterm the pid file is left behind sometimes. +rm /opt/karaf/instances/instance.properties &> /dev/null || true \ No newline at end of file diff --git a/karaf/rootfs/etc/services.d/karaf/finish b/karaf/rootfs/etc/services.d/karaf/finish new file mode 100644 index 00000000..f8984dd3 --- /dev/null +++ b/karaf/rootfs/etc/services.d/karaf/finish @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -S1 +# -*- mode: sh -*- +# vi: set ft=sh : +s6-svscanctl -t /var/run/s6/services diff --git a/karaf/rootfs/etc/services.d/karaf/run b/karaf/rootfs/etc/services.d/karaf/run new file mode 100644 index 00000000..d1607dc7 --- /dev/null +++ b/karaf/rootfs/etc/services.d/karaf/run @@ -0,0 +1,6 @@ +#!/usr/bin/execlineb -P +# -*- mode: sh -*- +# vi: set ft=sh : +with-contenv +s6-setuidgid karaf +/opt/karaf/bin/karaf server diff --git a/mariadb/.dockerignore b/mariadb/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/mariadb/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/mariadb/Dockerfile b/mariadb/Dockerfile new file mode 100644 index 00000000..dd867c90 --- /dev/null +++ b/mariadb/Dockerfile @@ -0,0 +1,19 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/base:latest + +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + apk-install.sh \ + mariadb \ + mysql-client \ + && \ + mkdir -p /var/lib/mysql-files && \ + chown -R mysql:mysql /var/lib/mysql && \ + chown -R mysql:mysql /var/lib/mysql-files && \ + cleanup.sh + +EXPOSE 3306 + +VOLUME [ "/var/lib/mysql", "/var/lib/mysql-files" ] + +COPY rootfs / diff --git a/mariadb/README.md b/mariadb/README.md new file mode 100644 index 00000000..d510eea7 --- /dev/null +++ b/mariadb/README.md @@ -0,0 +1,46 @@ +# MariaDB + +Docker image for [MariaDB] version 10.4.12 + +Please refer to the [MariaDB Documentation] for more in-depth information. + +As a quick example this will bring up an instance of MariaDB, and allow you to +log in with client as the user `root` with the password `password`. + +```bash +docker run --rm -d -name mariadb islandora/mariadb +docker exec -ti mariadb mysql -u root --password='password' +``` + +## Dependencies + +Requires `islandora/base` docker image to build. Please refer to the +[Base Image README](../base/README.md) for additional information. + +## Ports + +| Port | Description | +| :--- | :---------------- | +| 3306 | MySQL Client Port | + +## Volumes + +| Path | Description | +| :------------------- | :--------------------------------------------- | +| /var/lib/mysql | Database files | +| /var/lib/mysql-files | Location to import databases via CSV/SQL files | + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------- | :------------------- | :------- | :------------------------------------- | +| MYSQL_ROOT_PASSWORD | /mysql/root/password | password | The password for the root user account | + +## Logs + +| Path | Description | +| :----- | :------------ | +| STDOUT | [MariaDB Log] | + +[MariaDB Documentation]: https://mariadb.org/documentation/ +[MariaDB]: https://mariadb.org/ diff --git a/mariadb/rootfs/etc/confd/conf.d/mariadb-server.cnf.toml b/mariadb/rootfs/etc/confd/conf.d/mariadb-server.cnf.toml new file mode 100644 index 00000000..537f353e --- /dev/null +++ b/mariadb/rootfs/etc/confd/conf.d/mariadb-server.cnf.toml @@ -0,0 +1,5 @@ +[template] +src = "mariadb-server.cnf.tmpl" +dest = "/etc/my.cnf.d/mariadb-server.cnf" +mode="0644" +keys = [ "/root" ] diff --git a/mariadb/rootfs/etc/confd/conf.d/my.cnf.toml b/mariadb/rootfs/etc/confd/conf.d/my.cnf.toml new file mode 100644 index 00000000..d74e5e43 --- /dev/null +++ b/mariadb/rootfs/etc/confd/conf.d/my.cnf.toml @@ -0,0 +1,5 @@ +[template] +src = "my.cnf.tmpl" +dest = "/etc/my.cnf" +mode="0644" +keys = ["/root" ] diff --git a/mariadb/rootfs/etc/confd/conf.d/set-root-user-password.sql.toml b/mariadb/rootfs/etc/confd/conf.d/set-root-user-password.sql.toml new file mode 100644 index 00000000..e4d261f7 --- /dev/null +++ b/mariadb/rootfs/etc/confd/conf.d/set-root-user-password.sql.toml @@ -0,0 +1,7 @@ +[template] +src = "set-root-user-password.sql.tmpl" +dest = "/var/run/islandora/set-root-user-password.sql" +uid = 0 +gid = 0 +mode = "0700" +keys = [ "/root/password" ] diff --git a/mariadb/rootfs/etc/confd/confd.toml b/mariadb/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..a5717209 --- /dev/null +++ b/mariadb/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/mysql" diff --git a/mariadb/rootfs/etc/confd/templates/mariadb-server.cnf.tmpl b/mariadb/rootfs/etc/confd/templates/mariadb-server.cnf.tmpl new file mode 100644 index 00000000..cbcad522 --- /dev/null +++ b/mariadb/rootfs/etc/confd/templates/mariadb-server.cnf.tmpl @@ -0,0 +1,42 @@ +# +# These groups are read by MariaDB server. +# Use it for options that only the server (but not clients) should see + +# this is read by the standalone daemon and embedded servers +[server] + +# this is only for the mysqld standalone daemon +[mysqld] +# skip-networking + +# Galera-related settings +[galera] +# Mandatory settings +#wsrep_on=ON +#wsrep_provider= +#wsrep_cluster_address= +#binlog_format=row +#default_storage_engine=InnoDB +#innodb_autoinc_lock_mode=2 +# +# Allow server to accept connections on all interfaces. +# +#bind-address=0.0.0.0 +# +# Optional setting +#wsrep_slave_threads=1 +#innodb_flush_log_at_trx_commit=0 + +# this is only for embedded server +[embedded] + +# This group is only read by MariaDB servers, not by MySQL. +# If you use the same .cnf file for MySQL and MariaDB, +# you can put MariaDB-only options here +[mariadb] + +# This group is only read by MariaDB-10.3 servers. +# If you use the same .cnf file for MariaDB of different versions, +# use this group for options that older servers don't understand +[mariadb-10.3] + diff --git a/mariadb/rootfs/etc/confd/templates/my.cnf.tmpl b/mariadb/rootfs/etc/confd/templates/my.cnf.tmpl new file mode 100644 index 00000000..cec4bd05 --- /dev/null +++ b/mariadb/rootfs/etc/confd/templates/my.cnf.tmpl @@ -0,0 +1,12 @@ +# This group is read both both by the client and the server +# use it for options that affect everything +[client-server] + +# This group is read by the server +[mysqld] + +# Disabling symbolic-links is recommended to prevent assorted security risks +symbolic-links=0 + +# include all files from the config directory +!includedir /etc/my.cnf.d diff --git a/mariadb/rootfs/etc/confd/templates/set-root-user-password.sql.tmpl b/mariadb/rootfs/etc/confd/templates/set-root-user-password.sql.tmpl new file mode 100644 index 00000000..51c1b823 --- /dev/null +++ b/mariadb/rootfs/etc/confd/templates/set-root-user-password.sql.tmpl @@ -0,0 +1,5 @@ +CREATE USER IF NOT EXISTS 'root'@'%'; +SET PASSWORD FOR 'root'@'localhost' = PASSWORD('{{ getv "/root/password" "password" }}'); +SET PASSWORD FOR 'root'@'%' = PASSWORD('{{ getv "/root/password" "password" }}'); +GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; +FLUSH PRIVILEGES; diff --git a/mariadb/rootfs/etc/cont-init.d/03-mysql-setup.sh b/mariadb/rootfs/etc/cont-init.d/03-mysql-setup.sh new file mode 100755 index 00000000..e9613451 --- /dev/null +++ b/mariadb/rootfs/etc/cont-init.d/03-mysql-setup.sh @@ -0,0 +1,32 @@ +#!/usr/bin/with-contenv bash + +set -e + +# Make run directory if it does not exist. +mkdir /run/mysqld &> /dev/null || true +chown mysql:mysql /run/mysqld + +# Create the database if it does not exist. +if [[ ! -d "/var/lib/mysql/mysql" ]]; then + s6-setuidgid mysql mysql_install_db --basedir=/usr --datadir=/var/lib/mysql --skip-test-db --user mysql +fi + +# Startup the database so we can change the root users password. +s6-setuidgid mysql mysqld --skip-networking & +MYSQLD_PID=$! + +# Wait for it to startup. +until mysql --no-defaults --protocol=socket --user=root -e "SELECT 1" &> /dev/null; +do +sleep 1 +done + +# Change the root users password. +echo "Changing the root users password." +mysql --no-defaults --protocol=socket --user=root < /var/run/islandora/set-root-user-password.sql + +# Stop the database. +kill -s TERM ${MYSQLD_PID} + +# Allow database to stop. +wait diff --git a/mariadb/rootfs/etc/services.d/mysqld/finish b/mariadb/rootfs/etc/services.d/mysqld/finish new file mode 100644 index 00000000..32f06197 --- /dev/null +++ b/mariadb/rootfs/etc/services.d/mysqld/finish @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -S0 +# -*- mode: sh -*- +# vi: set ft=sh: +s6-svscanctl -t /var/run/s6/services diff --git a/mariadb/rootfs/etc/services.d/mysqld/run b/mariadb/rootfs/etc/services.d/mysqld/run new file mode 100644 index 00000000..0547bbee --- /dev/null +++ b/mariadb/rootfs/etc/services.d/mysqld/run @@ -0,0 +1,5 @@ +#!/usr/bin/execlineb -P +# -*- mode: sh -*- +# vi: set ft=sh: +s6-setuidgid mysql +mysqld --user mysql diff --git a/matomo/.dockerignore b/matomo/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/matomo/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/matomo/Dockerfile b/matomo/Dockerfile new file mode 100644 index 00000000..9bc3f4ac --- /dev/null +++ b/matomo/Dockerfile @@ -0,0 +1,21 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/nginx:latest + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + MATOMO_VERSION=3.13.5 && \ + MATOMO_FILE=matomo-${MATOMO_VERSION}.tar.gz && \ + wget -N -P /opt/downloads https://builds.matomo.org/${MATOMO_FILE} && \ + wget -N -P /opt/downloads https://builds.matomo.org/${MATOMO_FILE}.asc && \ + gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 814E346FA01A20DBB04B6807B5DBD5925590A237 && \ + gpg --verify /opt/downloads/${MATOMO_FILE}.asc /opt/downloads/${MATOMO_FILE} && \ + tar -xzf /opt/downloads/${MATOMO_FILE} -C /opt && \ + chown -R nginx:nginx /opt/matomo && \ + cleanup.sh + +WORKDIR /opt/matomo + +VOLUME [ "/opt/matomo/config" ] + +COPY rootfs / + +EXPOSE 8000 diff --git a/matomo/README.md b/matomo/README.md new file mode 100644 index 00000000..4b331668 --- /dev/null +++ b/matomo/README.md @@ -0,0 +1,31 @@ +# Matomo + +Docker image for [Crayfish] version 1.1.1. + +Acts as base Docker image for Crayfish based micro-services. It is not meant to +be run on its own. + +## Dependencies + +Requires `islandora/nginx` docker image to build. Please refer to the +[Nginx Image README](../nginx/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Ports + +| Port | Description | +| :--- | :---------- | +| 8000 | HTTP | + +## Settings + +> N.B. For all of the settings below images that descend from +> ``islandora/crayfish`` will apply prefix to every setting. So for example +> `JWT_ADMIN_TOKEN` would become `GEMINI_JWT_ADMIN_TOKEN` this is to allow for +> different settings on a per-service basis. + +| Environment Variable | Etcd Key | Default | Description | +| :------------------- | :--------------- | :-------- | :---------- | +| JWT_ADMIN_TOKEN | /jwt/admin/token | islandora | JWT Token | + +[Crayfish]: https://github.com/Islandora/Crayfish/tree/master diff --git a/matomo/rootfs/etc/confd/conf.d/create-matomo-database.sh.toml b/matomo/rootfs/etc/confd/conf.d/create-matomo-database.sh.toml new file mode 100644 index 00000000..d7511c40 --- /dev/null +++ b/matomo/rootfs/etc/confd/conf.d/create-matomo-database.sh.toml @@ -0,0 +1,7 @@ +[template] +src = "create-matomo-database.sh.tmpl" +dest = "/var/run/islandora/create-matomo-database.sh" +uid = 0 +gid = 0 +mode = "0700" +keys = [ "/db" ] diff --git a/matomo/rootfs/etc/confd/conf.d/create-matomo-database.sql.toml b/matomo/rootfs/etc/confd/conf.d/create-matomo-database.sql.toml new file mode 100644 index 00000000..37a87919 --- /dev/null +++ b/matomo/rootfs/etc/confd/conf.d/create-matomo-database.sql.toml @@ -0,0 +1,7 @@ +[template] +src = "create-matomo-database.sql.tmpl" +dest = "/var/run/islandora/create-matomo-database.sql" +uid = 0 +gid = 0 +mode = "0600" +keys = [ "/db" ] diff --git a/matomo/rootfs/etc/confd/conf.d/default.conf.toml b/matomo/rootfs/etc/confd/conf.d/default.conf.toml new file mode 100644 index 00000000..1df8dbe5 --- /dev/null +++ b/matomo/rootfs/etc/confd/conf.d/default.conf.toml @@ -0,0 +1,7 @@ +[template] +src = "default.conf.tmpl" +dest = "/etc/nginx/conf.d/default.conf" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/" ] diff --git a/matomo/rootfs/etc/confd/confd.toml b/matomo/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..eda2d410 --- /dev/null +++ b/matomo/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/matomo" diff --git a/matomo/rootfs/etc/confd/templates/create-matomo-database.sh.tmpl b/matomo/rootfs/etc/confd/templates/create-matomo-database.sh.tmpl new file mode 100644 index 00000000..3be46856 --- /dev/null +++ b/matomo/rootfs/etc/confd/templates/create-matomo-database.sh.tmpl @@ -0,0 +1,33 @@ +#!/usr/bin/with-contenv bash + +set -e + +function main { + local driver="{{ getv "/db/driver" "pdo_mysql" }}" + local host="{{ getv "/db/host" "database" }}" + local port="{{ getv "/db/port" "3306" }}" + local user="{{ getv "/db/root/user" "root" }}" + local password="{{ getv "/db/root/password" "password" }}" + + if [[ "${driver}" == "pdo_mysql" ]]; then + echo "Waiting for connection to database." + wait-for-mysql.sh \ + --host "${host}" \ + --port "${port}" \ + --user "${user}" \ + --password "${password}" + + echo "Create database / user if it does not exist." + mysql \ + --user="${user}" \ + --password="${password}" \ + --host="${host}" \ + --port="${port}" \ + --protocol=tcp \ + < /var/run/islandora/create-matomo-database.sql + else + echo "Only MySQL databases are supported for now." + exit 1 + fi +} +main diff --git a/matomo/rootfs/etc/confd/templates/create-matomo-database.sql.tmpl b/matomo/rootfs/etc/confd/templates/create-matomo-database.sql.tmpl new file mode 100644 index 00000000..981ac7d2 --- /dev/null +++ b/matomo/rootfs/etc/confd/templates/create-matomo-database.sql.tmpl @@ -0,0 +1,7 @@ +-- Create matomo database in mariadb or mysql. +CREATE DATABASE IF NOT EXISTS {{ getv "/db/name" "matomo" }} CHARACTER SET utf8 COLLATE utf8_general_ci; + +-- Create matomo_user and grant rights. +CREATE USER IF NOT EXISTS '{{ getv "/db/user" "matomo" }}'@'%' IDENTIFIED BY '{{ getv "/db/password" "password" }}'; +GRANT ALL PRIVILEGES ON {{ getv "/db/name" "matomo" }}.* to '{{ getv "/db/user" "matomo" }}'@'%'; +FLUSH PRIVILEGES; diff --git a/matomo/rootfs/etc/confd/templates/default.conf.tmpl b/matomo/rootfs/etc/confd/templates/default.conf.tmpl new file mode 100644 index 00000000..f7a9fc11 --- /dev/null +++ b/matomo/rootfs/etc/confd/templates/default.conf.tmpl @@ -0,0 +1,64 @@ +server { + listen 80; + + add_header Referrer-Policy origin; # make sure outgoing links don't show the URL to the Matomo instance + root /opt/matomo; + index index.php; + try_files $uri $uri/ =404; + + ## only allow accessing the following php files + location ~ ^/(index|matomo|piwik|js/index|plugins/HeatmapSessionRecording/configs).php { + # regex to split $uri to $fastcgi_script_name and $fastcgi_path + fastcgi_split_path_info ^(.+\.php)(/.+)$; + + # Check that the PHP script exists before passing it + try_files $fastcgi_script_name =404; + + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param HTTP_PROXY ""; # prohibit httpoxy: https://httpoxy.org/ + fastcgi_pass unix:/var/run/php-fpm7/php-fpm7.sock; + } + + ## deny access to all other .php files + location ~* ^.+\.php$ { + deny all; + + return 403; + } + + ## disable all access to the following directories + location ~ /(config|tmp|core|lang) { + deny all; + return 403; # replace with 404 to not show these directories exist + } + location ~ /\.ht { + deny all; + return 403; + } + + location ~ js/container_.*_preview\.js$ { + expires off; + add_header Cache-Control 'private, no-cache, no-store'; + } + + location ~ \.(gif|ico|jpg|png|svg|js|css|htm|html|mp3|mp4|wav|ogg|avi|ttf|eot|woff|woff2|json)$ { + allow all; + ## Cache images,CSS,JS and webfonts for an hour + ## Increasing the duration may improve the load-time, but may cause old files to show after an Matomo upgrade + expires 1h; + add_header Pragma public; + add_header Cache-Control "public"; + } + + location ~ /(libs|vendor|plugins|misc/user) { + deny all; + return 403; + } + + ## properly display textfiles in root directory + location ~/(.*\.md|LEGALNOTICE|LICENSE) { + default_type text/plain; + } +} diff --git a/matomo/rootfs/etc/cont-init.d/03-matomo-setup.sh b/matomo/rootfs/etc/cont-init.d/03-matomo-setup.sh new file mode 100644 index 00000000..38490cf0 --- /dev/null +++ b/matomo/rootfs/etc/cont-init.d/03-matomo-setup.sh @@ -0,0 +1,3 @@ +#!/usr/bin/with-contenv bash +set -e +/var/run/islandora/create-matomo-database.sh \ No newline at end of file diff --git a/milliner/.dockerignore b/milliner/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/milliner/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/milliner/Dockerfile b/milliner/Dockerfile new file mode 100644 index 00000000..e54d4e29 --- /dev/null +++ b/milliner/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/crayfish:latest + +RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + composer install -d /var/www/crayfish/Milliner && \ + ln -s /var/www/crayfish/Milliner/src /var/www/html && \ + cleanup.sh + +COPY /rootfs / + +RUN chown -R nginx:nginx /var/www + +WORKDIR /var/www/crayfish/Milliner/ diff --git a/milliner/README.md b/milliner/README.md new file mode 100644 index 00000000..47bc31cc --- /dev/null +++ b/milliner/README.md @@ -0,0 +1,26 @@ +# Milliner + +Docker image for [Milliner]. + +## Dependencies + +Requires `islandora/crayfish` docker image to build. Please refer to the +[Crayfish Image README](../crayfish/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------- | :------------------- | :---------------------- | :--------------------------------------- | +| MILLINER_DRUPAL_URL | /milliner/drupal/url | drupal:80 | Drupal URL | +| MILLINER_FCREPO_URL | /milliner/fcrepo/url | fcrepo/fcrepo/rest | Fcrepo Rest API URL | +| MILLINER_GEMINI_URL | /milliner/gemini/url | gemini:8000 | Gemini URL | +| MILLINER_LOG_LEVEL | /milliner/log/level | WARNING | The log level for Milliner micro-service | + +## Logs + +| Path | Description | +| :------------------------------ | :----------- | +| /var/log/islandora/milliner.log | Milliner Log | + +[Milliner]: https://github.com/Islandora/Crayfish/tree/master/Milliner diff --git a/milliner/rootfs/etc/confd/conf.d/config.yaml.toml b/milliner/rootfs/etc/confd/conf.d/config.yaml.toml new file mode 100644 index 00000000..27b906b8 --- /dev/null +++ b/milliner/rootfs/etc/confd/conf.d/config.yaml.toml @@ -0,0 +1,7 @@ +[template] +src = "config.yaml.tmpl" +dest = "/var/www/crayfish/Milliner/cfg/config.yaml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/log/level", "/fcrepo", "/drupal", "/gemini" ] diff --git a/milliner/rootfs/etc/confd/confd.toml b/milliner/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..65d2057c --- /dev/null +++ b/milliner/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/milliner" diff --git a/milliner/rootfs/etc/confd/templates/config.yaml.tmpl b/milliner/rootfs/etc/confd/templates/config.yaml.tmpl new file mode 100644 index 00000000..5d2e4e39 --- /dev/null +++ b/milliner/rootfs/etc/confd/templates/config.yaml.tmpl @@ -0,0 +1,28 @@ +--- + +fedora_base_url: {{ getv "/fcrepo/url" "http://fcrepo:8080/fcrepo/rest" }} +# if drupal_base_url contains a path, be sure to include trailing slash +# or relative paths will not resolve correctly. +drupal_base_url: {{ getv "/drupal/url" "http://drupal:80" }} +gemini_base_url: {{ getv "/gemini/url" "http://gemini:8000" }} + +modified_date_predicate: http://schema.org/dateModified + +strip_format_jsonld: true + +debug: true + +log: + # Valid log levels are: + # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY, NONE + # log level none won't open logfile + level: {{ getv "/log/level" "DEBUG" }} + file: /var/log/islandora/milliner.log + +syn: + # toggles JWT security for service + enable: false + # Path to the syn config file for authentication. + # example can be found here: + # https://github.com/Islandora/Syn/blob/master/conf/syn-settings.example.xml + config: /var/www/crayfish/syn-settings.xml diff --git a/nginx/.dockerignore b/nginx/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/nginx/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 00000000..d9ffe1f6 --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,26 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/base:latest + +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + apk-install.sh \ + composer \ + nginx \ + php7-ctype \ + php7-curl \ + php7-dom \ + php7-fpm \ + php7-gd \ + php7-opcache \ + php7-pdo \ + php7-pdo_mysql \ + php7-pdo_pgsql \ + php7-session \ + php7-simplexml \ + php7-tokenizer \ + php7-xml \ + php7-xmlwriter \ + && \ + cleanup.sh + +COPY rootfs / diff --git a/nginx/README.md b/nginx/README.md new file mode 100644 index 00000000..8881898f --- /dev/null +++ b/nginx/README.md @@ -0,0 +1,57 @@ +# Nginx + +Docker image for [Nginx] version 1.16.1 and [FPM] version 7.3.17. + +Please refer to the [Nginx Documentation] and [FPM Documentation] for more +in-depth information. + +Acts as base Docker image for all PHP based services, such as Crayfish, Docker +etc. It can be used on it's own as well. + +## Dependencies + +Requires `islandora/base` docker image to build. Please refer to the +[Base Image README](../base/README.md) for additional information. + +## Settings + +> N.B. For all of the settings below images that descend from +> ``islandora/nginx`` will apply prefix to every setting. So for example +> `JWT_ADMIN_TOKEN` would become `GEMINI_JWT_ADMIN_TOKEN` this is to allow for +> different settings on a per-service basis. + +### Nginx Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------------- | :-------------------------- | :------ | :------------------------------------------------------------------------------------ | +| NGINX_CLIENT_MAX_BODY_SIZE | /nginx/client/max/body/size | 1m | Specifies the maximum accepted body size of a client request | +| NGINX_ERROR_LOG_LEVEL | /nginx/error/log/level | warn | Log Level of Error log | +| NGINX_KEEPALIVE_TIMEOUT | /nginx/keepalive/timeout | 65 | Timeout for keep-alive connections | +| NGINX_WORKER_CONNECTIONS | /nginx/worker/connections | 1024 | The maximum number of simultaneous connections that can be opened by a worker process | +| NGINX_WORKER_PROCESSES | /nginx/worker/processes | auto | Set number of worker processes automatically based on number of CPU cores | + +### PHP Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------------- | :-------------------------- | :------ | :---------------------------------------------------------------- | +| PHP_DEFAULT_SOCKET_TIMEOUT | /php/default/socket/timeout | 60 | Default timeout for socket based streams (seconds) | +| PHP_MAX_EXECUTION_TIME | /php/max/execution/time | 30 | Maximum execution time of each script, in seconds | +| PHP_MAX_INPUT_TIME | /php/max/input/time | 60 | Maximum amount of time each script may spend parsing request data | +| PHP_MEMORY_LIMIT | /php/memory/limit | 128M | Maximum amount of memory a script may consume | +| PHP_POST_MAX_SIZE | /php/post/max/size | 8M | Maximum size of POST data that PHP will accept | +| PHP_MAX_FILE_UPLOADS | /php/max/file/uploads | 20 | Maximum number of files that can be uploaded via a single request | +| PHP_UPLOAD_MAX_FILESIZE | /php/upload/max/filesize | 2M | Maximum allowed size for uploaded files | + +## Logs + +| Path | Description | +| :-------------- | :-------------- | +| /var/log/nginx/ | [Nginx Logging] | +| /var/log/php7/ | [FPM Logging] | + +[FPM Documentation]: https://www.php.net/manual/en/install.fpm.configuration.php +[FPM Logging]: https://www.php.net/manual/en/install.fpm.configuration.php +[FPM]: https://www.php.net/manual/en/install.fpm.php +[Nginx Documentation]: https://nginx.org/en/docs/ +[Nginx Logging]: https://docs.nginx.com/nginx/admin-guide/monitoring/logging/ +[Nginx]: https://www.nginx.com/ diff --git a/nginx/rootfs/etc/confd/conf.d/nginx.conf.toml b/nginx/rootfs/etc/confd/conf.d/nginx.conf.toml new file mode 100644 index 00000000..70161db0 --- /dev/null +++ b/nginx/rootfs/etc/confd/conf.d/nginx.conf.toml @@ -0,0 +1,7 @@ +[template] +src = "nginx.conf.tmpl" +dest = "/etc/nginx/nginx.conf" +uid = 0 +gid = 0 +mode = "0644" +keys = [ "/nginx" ] diff --git a/nginx/rootfs/etc/confd/conf.d/php-fpm.conf.toml b/nginx/rootfs/etc/confd/conf.d/php-fpm.conf.toml new file mode 100644 index 00000000..b78e4cfa --- /dev/null +++ b/nginx/rootfs/etc/confd/conf.d/php-fpm.conf.toml @@ -0,0 +1,7 @@ +[template] +src = "php-fpm.conf.tmpl" +dest = "/etc/php7/php-fpm.conf" +uid = 0 +gid = 0 +mode = "0644" +keys = [ "/php" ] diff --git a/nginx/rootfs/etc/confd/conf.d/php.ini.toml b/nginx/rootfs/etc/confd/conf.d/php.ini.toml new file mode 100644 index 00000000..0b0bb90c --- /dev/null +++ b/nginx/rootfs/etc/confd/conf.d/php.ini.toml @@ -0,0 +1,7 @@ +[template] +src = "php.ini.tmpl" +dest = "/etc/php7/php.ini" +uid = 0 +gid = 0 +mode = "0644" +keys = [ "/php" ] diff --git a/nginx/rootfs/etc/confd/conf.d/www.conf.toml b/nginx/rootfs/etc/confd/conf.d/www.conf.toml new file mode 100644 index 00000000..60b953ea --- /dev/null +++ b/nginx/rootfs/etc/confd/conf.d/www.conf.toml @@ -0,0 +1,7 @@ +[template] +src = "www.conf.tmpl" +dest = "/etc/php7/php-fpm.d/www.conf" +uid = 0 +gid = 0 +mode = "0644" +keys = [ "/php" ] diff --git a/nginx/rootfs/etc/confd/templates/nginx.conf.tmpl b/nginx/rootfs/etc/confd/templates/nginx.conf.tmpl new file mode 100644 index 00000000..76cec9f3 --- /dev/null +++ b/nginx/rootfs/etc/confd/templates/nginx.conf.tmpl @@ -0,0 +1,85 @@ +user nginx; + +# Set number of worker processes automatically based on number of CPU cores. +worker_processes {{ getv "nginx/worker/processes" "auto" }}; + +# Enables the use of JIT for regular expressions to speed-up their processing. +pcre_jit on; + +# Configures default error logger. +error_log /var/log/nginx/error.log {{ getv "nginx/error/log/level" "warn" }}; + +# Includes files with directives to load dynamic modules. +include /etc/nginx/modules/*.conf; + + +events { + # The maximum number of simultaneous connections that can be opened by + # a worker process. + worker_connections {{ getv "nginx/worker/connections" "1024" }}; +} + +http { + # Includes mapping of file name extensions to MIME types of responses + # and defines the default type. + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Name servers used to resolve names of upstream servers into addresses. + # It's also needed when using tcpsocket and udpsocket in Lua modules. + #resolver 208.67.222.222 208.67.220.220; + + # Don't tell nginx version to clients. + server_tokens off; + + # Specifies the maximum accepted body size of a client request, as + # indicated by the request header Content-Length. If the stated content + # length is greater than this size, then the client receives the HTTP + # error code 413. Set to 0 to disable. + client_max_body_size {{ getv "nginx/client/max/body/size" "1m" }}; + + # Timeout for keep-alive connections. Server will close connections after + # this time. + keepalive_timeout {{ getv "nginx/keepalive/timeout" "65" }}; + + # Sendfile copies data between one FD and other from within the kernel, + # which is more efficient than read() + write(). + sendfile on; + + # Don't buffer data-sends (disable Nagle algorithm). + # Good for sending frequent small bursts of data in real time. + tcp_nodelay on; + + # Causes nginx to attempt to send its HTTP response head in one packet, + # instead of using partial frames. + #tcp_nopush on; + + # Path of the file with Diffie-Hellman parameters for EDH ciphers. + #ssl_dhparam /etc/ssl/nginx/dh2048.pem; + + # Specifies that our cipher suits should be preferred over client ciphers. + ssl_prefer_server_ciphers on; + + # Enables a shared SSL cache with size that can hold around 8000 sessions. + ssl_session_cache shared:SSL:2m; + + # Enable gzipping of responses. + #gzip on; + + # Set the Vary HTTP header as defined in the RFC 2616. + gzip_vary on; + + # Enable checking the existence of precompressed files. + #gzip_static on; + + # Specifies the main log format. + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + # Sets the path, format, and configuration for a buffered log write. + access_log /var/log/nginx/access.log main; + + # Includes virtual hosts configs. + include /etc/nginx/conf.d/*.conf; +} diff --git a/nginx/rootfs/etc/confd/templates/php-fpm.conf.tmpl b/nginx/rootfs/etc/confd/templates/php-fpm.conf.tmpl new file mode 100644 index 00000000..834fd9bd --- /dev/null +++ b/nginx/rootfs/etc/confd/templates/php-fpm.conf.tmpl @@ -0,0 +1,136 @@ +;;;;;;;;;;;;;;;;;;;;; +; FPM Configuration ; +;;;;;;;;;;;;;;;;;;;;; + +; All relative paths in this configuration file are relative to PHP's install +; prefix (/usr). This prefix can be dynamically changed by using the +; '-p' argument from the command line. + +;;;;;;;;;;;;;;;;;; +; Global Options ; +;;;;;;;;;;;;;;;;;; + +[global] +; Pid file +; Note: the default prefix is /var +; Default Value: none +pid = run/php-fpm7.pid + +; Error log file +; If it's set to "syslog", log is sent to syslogd instead of being written +; into a local file. +; Note: the default prefix is /var +; Default Value: log/php7/error.log +;error_log = log/php7/error.log + +; syslog_facility is used to specify what type of program is logging the +; message. This lets syslogd specify that messages from different facilities +; will be handled differently. +; See syslog(3) for possible values (ex daemon equiv LOG_DAEMON) +; Default Value: daemon +;syslog.facility = daemon + +; syslog_ident is prepended to every message. If you have multiple FPM +; instances running on the same server, you can change the default value +; which must suit common needs. +; Default Value: php-fpm7 +;syslog.ident = php-fpm7 + +; Log level +; Possible Values: alert, error, warning, notice, debug +; Default Value: notice +;log_level = notice + +; Log limit on number of characters in the single line (log entry). If the +; line is over the limit, it is wrapped on multiple lines. The limit is for +; all logged characters including message prefix and suffix if present. However +; the new line character does not count into it as it is present only when +; logging to a file descriptor. It means the new line character is not present +; when logging to syslog. +; Default Value: 1024 +;log_limit = 4096 + +; Log buffering specifies if the log line is buffered which means that the +; line is written in a single write operation. If the value is false, then the +; data is written directly into the file descriptor. It is an experimental +; option that can potentionaly improve logging performance and memory usage +; for some heavy logging scenarios. This option is ignored if logging to syslog +; as it has to be always buffered. +; Default value: yes +;log_buffering = no + +; If this number of child processes exit with SIGSEGV or SIGBUS within the time +; interval set by emergency_restart_interval then FPM will restart. A value +; of '0' means 'Off'. +; Default Value: 0 +;emergency_restart_threshold = 0 + +; Interval of time used by emergency_restart_interval to determine when +; a graceful restart will be initiated. This can be useful to work around +; accidental corruptions in an accelerator's shared memory. +; Available Units: s(econds), m(inutes), h(ours), or d(ays) +; Default Unit: seconds +; Default Value: 0 +;emergency_restart_interval = 0 + +; Time limit for child processes to wait for a reaction on signals from master. +; Available units: s(econds), m(inutes), h(ours), or d(ays) +; Default Unit: seconds +; Default Value: 0 +;process_control_timeout = 0 + +; The maximum number of processes FPM will fork. This has been designed to control +; the global number of processes when using dynamic PM within a lot of pools. +; Use it with caution. +; Note: A value of 0 indicates no limit +; Default Value: 0 +; process.max = 128 + +; Specify the nice(2) priority to apply to the master process (only if set) +; The value can vary from -19 (highest priority) to 20 (lowest priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool process will inherit the master process priority +; unless specified otherwise +; Default Value: no set +; process.priority = -19 + +; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging. +; Default Value: yes +daemonize = no + +; Set open file descriptor rlimit for the master process. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit for the master process. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Specify the event mechanism FPM will use. The following is available: +; - select (any POSIX os) +; - poll (any POSIX os) +; - epoll (linux >= 2.5.44) +; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0) +; - /dev/poll (Solaris >= 7) +; - port (Solaris >= 10) +; Default Value: not set (auto detection) +;events.mechanism = epoll + +; When FPM is built with systemd integration, specify the interval, +; in seconds, between health report notification to systemd. +; Set to 0 to disable. +; Available Units: s(econds), m(inutes), h(ours) +; Default Unit: seconds +; Default value: 10 +;systemd_interval = 10 + +;;;;;;;;;;;;;;;;;;;; +; Pool Definitions ; +;;;;;;;;;;;;;;;;;;;; + +; Multiple pools of child processes may be started with different listening +; ports and different management options. The name of the pool will be +; used in logs and stats. There is no limitation on the number of pools which +; FPM can handle. Your system will tell you anyway :) +include=/etc/php7/php-fpm.d/*.conf diff --git a/nginx/rootfs/etc/confd/templates/php.ini.tmpl b/nginx/rootfs/etc/confd/templates/php.ini.tmpl new file mode 100644 index 00000000..50251c26 --- /dev/null +++ b/nginx/rootfs/etc/confd/templates/php.ini.tmpl @@ -0,0 +1,1939 @@ +[PHP] + +;;;;;;;;;;;;;;;;;;; +; About php.ini ; +;;;;;;;;;;;;;;;;;;; +; PHP's initialization file, generally called php.ini, is responsible for +; configuring many of the aspects of PHP's behavior. + +; PHP attempts to find and load this configuration from a number of locations. +; The following is a summary of its search order: +; 1. SAPI module specific location. +; 2. The PHPRC environment variable. (As of PHP 5.2.0) +; 3. A number of predefined registry keys on Windows (As of PHP 5.2.0) +; 4. Current working directory (except CLI) +; 5. The web server's directory (for SAPI modules), or directory of PHP +; (otherwise in Windows) +; 6. The directory from the --with-config-file-path compile time option, or the +; Windows directory (usually C:\windows) +; See the PHP docs for more specific information. +; http://php.net/configuration.file + +; The syntax of the file is extremely simple. Whitespace and lines +; beginning with a semicolon are silently ignored (as you probably guessed). +; Section headers (e.g. [Foo]) are also silently ignored, even though +; they might mean something in the future. + +; Directives following the section heading [PATH=/www/mysite] only +; apply to PHP files in the /www/mysite directory. Directives +; following the section heading [HOST=www.example.com] only apply to +; PHP files served from www.example.com. Directives set in these +; special sections cannot be overridden by user-defined INI files or +; at runtime. Currently, [PATH=] and [HOST=] sections only work under +; CGI/FastCGI. +; http://php.net/ini.sections + +; Directives are specified using the following syntax: +; directive = value +; Directive names are *case sensitive* - foo=bar is different from FOO=bar. +; Directives are variables used to configure PHP or PHP extensions. +; There is no name validation. If PHP can't find an expected +; directive because it is not set or is mistyped, a default value will be used. + +; The value can be a string, a number, a PHP constant (e.g. E_ALL or M_PI), one +; of the INI constants (On, Off, True, False, Yes, No and None) or an expression +; (e.g. E_ALL & ~E_NOTICE), a quoted string ("bar"), or a reference to a +; previously set variable or directive (e.g. ${foo}) + +; Expressions in the INI file are limited to bitwise operators and parentheses: +; | bitwise OR +; ^ bitwise XOR +; & bitwise AND +; ~ bitwise NOT +; ! boolean NOT + +; Boolean flags can be turned on using the values 1, On, True or Yes. +; They can be turned off using the values 0, Off, False or No. + +; An empty string can be denoted by simply not writing anything after the equal +; sign, or by using the None keyword: + +; foo = ; sets foo to an empty string +; foo = None ; sets foo to an empty string +; foo = "None" ; sets foo to the string 'None' + +; If you use constants in your value, and these constants belong to a +; dynamically loaded extension (either a PHP extension or a Zend extension), +; you may only use these constants *after* the line that loads the extension. + +;;;;;;;;;;;;;;;;;;; +; About this file ; +;;;;;;;;;;;;;;;;;;; +; PHP comes packaged with two INI files. One that is recommended to be used +; in production environments and one that is recommended to be used in +; development environments. + +; php.ini-production contains settings which hold security, performance and +; best practices at its core. But please be aware, these settings may break +; compatibility with older or less security conscience applications. We +; recommending using the production ini in production and testing environments. + +; php.ini-development is very similar to its production variant, except it is +; much more verbose when it comes to errors. We recommend using the +; development version only in development environments, as errors shown to +; application users can inadvertently leak otherwise secure information. + +; This is the php.ini-production INI file. + +;;;;;;;;;;;;;;;;;;; +; Quick Reference ; +;;;;;;;;;;;;;;;;;;; +; The following are all the settings which are different in either the production +; or development versions of the INIs with respect to PHP's default behavior. +; Please see the actual settings later in the document for more details as to why +; we recommend these changes in PHP's behavior. + +; display_errors +; Default Value: On +; Development Value: On +; Production Value: Off + +; display_startup_errors +; Default Value: Off +; Development Value: On +; Production Value: Off + +; error_reporting +; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED +; Development Value: E_ALL +; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT + +; html_errors +; Default Value: On +; Development Value: On +; Production value: On + +; log_errors +; Default Value: Off +; Development Value: On +; Production Value: On + +; max_input_time +; Default Value: -1 (Unlimited) +; Development Value: 60 (60 seconds) +; Production Value: 60 (60 seconds) + +; output_buffering +; Default Value: Off +; Development Value: 4096 +; Production Value: 4096 + +; register_argc_argv +; Default Value: On +; Development Value: Off +; Production Value: Off + +; request_order +; Default Value: None +; Development Value: "GP" +; Production Value: "GP" + +; session.gc_divisor +; Default Value: 100 +; Development Value: 1000 +; Production Value: 1000 + +; session.sid_bits_per_character +; Default Value: 4 +; Development Value: 5 +; Production Value: 5 + +; short_open_tag +; Default Value: On +; Development Value: Off +; Production Value: Off + +; variables_order +; Default Value: "EGPCS" +; Development Value: "GPCS" +; Production Value: "GPCS" + +;;;;;;;;;;;;;;;;;;;; +; php.ini Options ; +;;;;;;;;;;;;;;;;;;;; +; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini" +;user_ini.filename = ".user.ini" + +; To disable this feature set this option to an empty value +;user_ini.filename = + +; TTL for user-defined php.ini files (time-to-live) in seconds. Default is 300 seconds (5 minutes) +;user_ini.cache_ttl = 300 + +;;;;;;;;;;;;;;;;;;;; +; Language Options ; +;;;;;;;;;;;;;;;;;;;; + +; Enable the PHP scripting language engine under Apache. +; http://php.net/engine +engine = On + +; This directive determines whether or not PHP will recognize code between +; tags as PHP source which should be processed as such. It is +; generally recommended that should be used and that this feature +; should be disabled, as enabling it may result in issues when generating XML +; documents, however this remains supported for backward compatibility reasons. +; Note that this directive does not control the would work. +; http://php.net/syntax-highlighting +;highlight.string = #DD0000 +;highlight.comment = #FF9900 +;highlight.keyword = #007700 +;highlight.default = #0000BB +;highlight.html = #000000 + +; If enabled, the request will be allowed to complete even if the user aborts +; the request. Consider enabling it if executing long requests, which may end up +; being interrupted by the user or a browser timing out. PHP's default behavior +; is to disable this feature. +; http://php.net/ignore-user-abort +;ignore_user_abort = On + +; Determines the size of the realpath cache to be used by PHP. This value should +; be increased on systems where PHP opens many files to reflect the quantity of +; the file operations performed. +; Note: if open_basedir is set, the cache is disabled +; http://php.net/realpath-cache-size +;realpath_cache_size = 4096k + +; Duration of time, in seconds for which to cache realpath information for a given +; file or directory. For systems with rarely changing files, consider increasing this +; value. +; http://php.net/realpath-cache-ttl +;realpath_cache_ttl = 120 + +; Enables or disables the circular reference collector. +; http://php.net/zend.enable-gc +zend.enable_gc = On + +; If enabled, scripts may be written in encodings that are incompatible with +; the scanner. CP936, Big5, CP949 and Shift_JIS are the examples of such +; encodings. To use this feature, mbstring extension must be enabled. +; Default: Off +;zend.multibyte = Off + +; Allows to set the default encoding for the scripts. This value will be used +; unless "declare(encoding=...)" directive appears at the top of the script. +; Only affects if zend.multibyte is set. +; Default: "" +;zend.script_encoding = + +;;;;;;;;;;;;;;;;; +; Miscellaneous ; +;;;;;;;;;;;;;;;;; + +; Decides whether PHP may expose the fact that it is installed on the server +; (e.g. by adding its signature to the Web server header). It is no security +; threat in any way, but it makes it possible to determine whether you use PHP +; on your server or not. +; http://php.net/expose-php +expose_php = On + +;;;;;;;;;;;;;;;;;;; +; Resource Limits ; +;;;;;;;;;;;;;;;;;;; + +; Maximum execution time of each script, in seconds +; http://php.net/max-execution-time +; Note: This directive is hardcoded to 0 for the CLI SAPI +max_execution_time = {{ getv "/php/max/execution/time" "30" }} + +; Maximum amount of time each script may spend parsing request data. It's a good +; idea to limit this time on productions servers in order to eliminate unexpectedly +; long running scripts. +; Note: This directive is hardcoded to -1 for the CLI SAPI +; Default Value: -1 (Unlimited) +; Development Value: 60 (60 seconds) +; Production Value: 60 (60 seconds) +; http://php.net/max-input-time +max_input_time = {{ getv "/php/max/input/time" "60" }} + +; Maximum input variable nesting level +; http://php.net/max-input-nesting-level +;max_input_nesting_level = 64 + +; How many GET/POST/COOKIE input variables may be accepted +;max_input_vars = 1000 + +; Maximum amount of memory a script may consume (128MB) +; http://php.net/memory-limit +memory_limit = {{ getv "/php/memory/limit" "128M" }} + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Error handling and logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; This directive informs PHP of which errors, warnings and notices you would like +; it to take action for. The recommended way of setting values for this +; directive is through the use of the error level constants and bitwise +; operators. The error level constants are below here for convenience as well as +; some common settings and their meanings. +; By default, PHP is set to take action on all errors, notices and warnings EXCEPT +; those related to E_NOTICE and E_STRICT, which together cover best practices and +; recommended coding standards in PHP. For performance reasons, this is the +; recommend error reporting setting. Your production server shouldn't be wasting +; resources complaining about best practices and coding standards. That's what +; development servers and development settings are for. +; Note: The php.ini-development file has this setting as E_ALL. This +; means it pretty much reports everything which is exactly what you want during +; development and early testing. +; +; Error Level Constants: +; E_ALL - All errors and warnings (includes E_STRICT as of PHP 5.4.0) +; E_ERROR - fatal run-time errors +; E_RECOVERABLE_ERROR - almost fatal run-time errors +; E_WARNING - run-time warnings (non-fatal errors) +; E_PARSE - compile-time parse errors +; E_NOTICE - run-time notices (these are warnings which often result +; from a bug in your code, but it's possible that it was +; intentional (e.g., using an uninitialized variable and +; relying on the fact it is automatically initialized to an +; empty string) +; E_STRICT - run-time notices, enable to have PHP suggest changes +; to your code which will ensure the best interoperability +; and forward compatibility of your code +; E_CORE_ERROR - fatal errors that occur during PHP's initial startup +; E_CORE_WARNING - warnings (non-fatal errors) that occur during PHP's +; initial startup +; E_COMPILE_ERROR - fatal compile-time errors +; E_COMPILE_WARNING - compile-time warnings (non-fatal errors) +; E_USER_ERROR - user-generated error message +; E_USER_WARNING - user-generated warning message +; E_USER_NOTICE - user-generated notice message +; E_DEPRECATED - warn about code that will not work in future versions +; of PHP +; E_USER_DEPRECATED - user-generated deprecation warnings +; +; Common Values: +; E_ALL (Show all errors, warnings and notices including coding standards.) +; E_ALL & ~E_NOTICE (Show all errors, except for notices) +; E_ALL & ~E_NOTICE & ~E_STRICT (Show all errors, except for notices and coding standards warnings.) +; E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR (Show only errors) +; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED +; Development Value: E_ALL +; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT +; http://php.net/error-reporting +error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT + +; This directive controls whether or not and where PHP will output errors, +; notices and warnings too. Error output is very useful during development, but +; it could be very dangerous in production environments. Depending on the code +; which is triggering the error, sensitive information could potentially leak +; out of your application such as database usernames and passwords or worse. +; For production environments, we recommend logging errors rather than +; sending them to STDOUT. +; Possible Values: +; Off = Do not display any errors +; stderr = Display errors to STDERR (affects only CGI/CLI binaries!) +; On or stdout = Display errors to STDOUT +; Default Value: On +; Development Value: On +; Production Value: Off +; http://php.net/display-errors +display_errors = Off + +; The display of errors which occur during PHP's startup sequence are handled +; separately from display_errors. PHP's default behavior is to suppress those +; errors from clients. Turning the display of startup errors on can be useful in +; debugging configuration problems. We strongly recommend you +; set this to 'off' for production servers. +; Default Value: Off +; Development Value: On +; Production Value: Off +; http://php.net/display-startup-errors +display_startup_errors = Off + +; Besides displaying errors, PHP can also log errors to locations such as a +; server-specific log, STDERR, or a location specified by the error_log +; directive found below. While errors should not be displayed on productions +; servers they should still be monitored and logging is a great way to do that. +; Default Value: Off +; Development Value: On +; Production Value: On +; http://php.net/log-errors +log_errors = On + +; Set maximum length of log_errors. In error_log information about the source is +; added. The default is 1024 and 0 allows to not apply any maximum length at all. +; http://php.net/log-errors-max-len +log_errors_max_len = 1024 + +; Do not log repeated messages. Repeated errors must occur in same file on same +; line unless ignore_repeated_source is set true. +; http://php.net/ignore-repeated-errors +ignore_repeated_errors = Off + +; Ignore source of message when ignoring repeated messages. When this setting +; is On you will not log errors with repeated messages from different files or +; source lines. +; http://php.net/ignore-repeated-source +ignore_repeated_source = Off + +; If this parameter is set to Off, then memory leaks will not be shown (on +; stdout or in the log). This has only effect in a debug compile, and if +; error reporting includes E_WARNING in the allowed list +; http://php.net/report-memleaks +report_memleaks = On + +; This setting is on by default. +;report_zend_debug = 0 + +; Store the last error/warning message in $php_errormsg (boolean). Setting this value +; to On can assist in debugging and is appropriate for development servers. It should +; however be disabled on production servers. +; This directive is DEPRECATED. +; Default Value: Off +; Development Value: Off +; Production Value: Off +; http://php.net/track-errors +;track_errors = Off + +; Turn off normal error reporting and emit XML-RPC error XML +; http://php.net/xmlrpc-errors +;xmlrpc_errors = 0 + +; An XML-RPC faultCode +;xmlrpc_error_number = 0 + +; When PHP displays or logs an error, it has the capability of formatting the +; error message as HTML for easier reading. This directive controls whether +; the error message is formatted as HTML or not. +; Note: This directive is hardcoded to Off for the CLI SAPI +; Default Value: On +; Development Value: On +; Production value: On +; http://php.net/html-errors +html_errors = On + +; If html_errors is set to On *and* docref_root is not empty, then PHP +; produces clickable error messages that direct to a page describing the error +; or function causing the error in detail. +; You can download a copy of the PHP manual from http://php.net/docs +; and change docref_root to the base URL of your local copy including the +; leading '/'. You must also specify the file extension being used including +; the dot. PHP's default behavior is to leave these settings empty, in which +; case no links to documentation are generated. +; Note: Never use this feature for production boxes. +; http://php.net/docref-root +; Examples +;docref_root = "/phpmanual/" + +; http://php.net/docref-ext +;docref_ext = .html + +; String to output before an error message. PHP's default behavior is to leave +; this setting blank. +; http://php.net/error-prepend-string +; Example: +;error_prepend_string = "" + +; String to output after an error message. PHP's default behavior is to leave +; this setting blank. +; http://php.net/error-append-string +; Example: +;error_append_string = "" + +; Log errors to specified file. PHP's default behavior is to leave this value +; empty. +; http://php.net/error-log +; Example: +;error_log = php_errors.log +; Log errors to syslog (Event Log on Windows). +;error_log = syslog + +; The syslog ident is a string which is prepended to every message logged +; to syslog. Only used when error_log is set to syslog. +;syslog.ident = php + +; The syslog facility is used to specify what type of program is logging +; the message. Only used when error_log is set to syslog. +;syslog.facility = user + +; Set this to disable filtering control characters (the default). +; Some loggers only accept NVT-ASCII, others accept anything that's not +; control characters. If your logger accepts everything, then no filtering +; is needed at all. +; Allowed values are: +; ascii (all printable ASCII characters and NL) +; no-ctrl (all characters except control characters) +; all (all characters) +; raw (like "all", but messages are not split at newlines) +; http://php.net/syslog.filter +;syslog.filter = ascii + +;windows.show_crt_warning +; Default value: 0 +; Development value: 0 +; Production value: 0 + +;;;;;;;;;;;;;;;;; +; Data Handling ; +;;;;;;;;;;;;;;;;; + +; The separator used in PHP generated URLs to separate arguments. +; PHP's default setting is "&". +; http://php.net/arg-separator.output +; Example: +;arg_separator.output = "&" + +; List of separator(s) used by PHP to parse input URLs into variables. +; PHP's default setting is "&". +; NOTE: Every character in this directive is considered as separator! +; http://php.net/arg-separator.input +; Example: +;arg_separator.input = ";&" + +; This directive determines which super global arrays are registered when PHP +; starts up. G,P,C,E & S are abbreviations for the following respective super +; globals: GET, POST, COOKIE, ENV and SERVER. There is a performance penalty +; paid for the registration of these arrays and because ENV is not as commonly +; used as the others, ENV is not recommended on productions servers. You +; can still get access to the environment variables through getenv() should you +; need to. +; Default Value: "EGPCS" +; Development Value: "GPCS" +; Production Value: "GPCS"; +; http://php.net/variables-order +variables_order = "GPCS" + +; This directive determines which super global data (G,P & C) should be +; registered into the super global array REQUEST. If so, it also determines +; the order in which that data is registered. The values for this directive +; are specified in the same manner as the variables_order directive, +; EXCEPT one. Leaving this value empty will cause PHP to use the value set +; in the variables_order directive. It does not mean it will leave the super +; globals array REQUEST empty. +; Default Value: None +; Development Value: "GP" +; Production Value: "GP" +; http://php.net/request-order +request_order = "GP" + +; This directive determines whether PHP registers $argv & $argc each time it +; runs. $argv contains an array of all the arguments passed to PHP when a script +; is invoked. $argc contains an integer representing the number of arguments +; that were passed when the script was invoked. These arrays are extremely +; useful when running scripts from the command line. When this directive is +; enabled, registering these variables consumes CPU cycles and memory each time +; a script is executed. For performance reasons, this feature should be disabled +; on production servers. +; Note: This directive is hardcoded to On for the CLI SAPI +; Default Value: On +; Development Value: Off +; Production Value: Off +; http://php.net/register-argc-argv +register_argc_argv = Off + +; When enabled, the ENV, REQUEST and SERVER variables are created when they're +; first used (Just In Time) instead of when the script starts. If these +; variables are not used within a script, having this directive on will result +; in a performance gain. The PHP directive register_argc_argv must be disabled +; for this directive to have any affect. +; http://php.net/auto-globals-jit +auto_globals_jit = On + +; Whether PHP will read the POST data. +; This option is enabled by default. +; Most likely, you won't want to disable this option globally. It causes $_POST +; and $_FILES to always be empty; the only way you will be able to read the +; POST data will be through the php://input stream wrapper. This can be useful +; to proxy requests or to process the POST data in a memory efficient fashion. +; http://php.net/enable-post-data-reading +;enable_post_data_reading = Off + +; Maximum size of POST data that PHP will accept. +; Its value may be 0 to disable the limit. It is ignored if POST data reading +; is disabled through enable_post_data_reading. +; http://php.net/post-max-size +post_max_size = {{ getv "/php/post/max/size" "8M" }} + +; Automatically add files before PHP document. +; http://php.net/auto-prepend-file +auto_prepend_file = + +; Automatically add files after PHP document. +; http://php.net/auto-append-file +auto_append_file = + +; By default, PHP will output a media type using the Content-Type header. To +; disable this, simply set it to be empty. +; +; PHP's built-in default media type is set to text/html. +; http://php.net/default-mimetype +default_mimetype = "text/html" + +; PHP's default character set is set to UTF-8. +; http://php.net/default-charset +default_charset = "UTF-8" + +; PHP internal character encoding is set to empty. +; If empty, default_charset is used. +; http://php.net/internal-encoding +;internal_encoding = + +; PHP input character encoding is set to empty. +; If empty, default_charset is used. +; http://php.net/input-encoding +;input_encoding = + +; PHP output character encoding is set to empty. +; If empty, default_charset is used. +; See also output_buffer. +; http://php.net/output-encoding +;output_encoding = + +;;;;;;;;;;;;;;;;;;;;;;;;; +; Paths and Directories ; +;;;;;;;;;;;;;;;;;;;;;;;;; + +; UNIX: "/path1:/path2" +include_path = ".:/usr/share/php7" +; +; Windows: "\path1;\path2" +;include_path = ".;c:\php\includes" +; +; PHP's default setting for include_path is ".;/path/to/php/pear" +; http://php.net/include-path + +; The root of the PHP pages, used only if nonempty. +; if PHP was not compiled with FORCE_REDIRECT, you SHOULD set doc_root +; if you are running php as a CGI under any web server (other than IIS) +; see documentation for security issues. The alternate is to use the +; cgi.force_redirect configuration below +; http://php.net/doc-root +doc_root = + +; The directory under which PHP opens the script using /~username used only +; if nonempty. +; http://php.net/user-dir +user_dir = + +; Directory in which the loadable extensions (modules) reside. +; http://php.net/extension-dir +;extension_dir = "./" +; On windows: +;extension_dir = "ext" + +; Directory where the temporary files should be placed. +; Defaults to the system default (see sys_get_temp_dir) +;sys_temp_dir = "/tmp" + +; Whether or not to enable the dl() function. The dl() function does NOT work +; properly in multithreaded servers, such as IIS or Zeus, and is automatically +; disabled on them. +; http://php.net/enable-dl +enable_dl = Off + +; cgi.force_redirect is necessary to provide security running PHP as a CGI under +; most web servers. Left undefined, PHP turns this on by default. You can +; turn it off here AT YOUR OWN RISK +; **You CAN safely turn this off for IIS, in fact, you MUST.** +; http://php.net/cgi.force-redirect +;cgi.force_redirect = 1 + +; if cgi.nph is enabled it will force cgi to always sent Status: 200 with +; every request. PHP's default behavior is to disable this feature. +;cgi.nph = 1 + +; if cgi.force_redirect is turned on, and you are not running under Apache or Netscape +; (iPlanet) web servers, you MAY need to set an environment variable name that PHP +; will look for to know it is OK to continue execution. Setting this variable MAY +; cause security issues, KNOW WHAT YOU ARE DOING FIRST. +; http://php.net/cgi.redirect-status-env +;cgi.redirect_status_env = + +; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's +; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok +; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting +; this to 1 will cause PHP CGI to fix its paths to conform to the spec. A setting +; of zero causes PHP to behave as before. Default is 1. You should fix your scripts +; to use SCRIPT_FILENAME rather than PATH_TRANSLATED. +; http://php.net/cgi.fix-pathinfo +;cgi.fix_pathinfo=1 + +; if cgi.discard_path is enabled, the PHP CGI binary can safely be placed outside +; of the web tree and people will not be able to circumvent .htaccess security. +;cgi.discard_path=1 + +; FastCGI under IIS supports the ability to impersonate +; security tokens of the calling client. This allows IIS to define the +; security context that the request runs under. mod_fastcgi under Apache +; does not currently support this feature (03/17/2002) +; Set to 1 if running under IIS. Default is zero. +; http://php.net/fastcgi.impersonate +;fastcgi.impersonate = 1 + +; Disable logging through FastCGI connection. PHP's default behavior is to enable +; this feature. +;fastcgi.logging = 0 + +; cgi.rfc2616_headers configuration option tells PHP what type of headers to +; use when sending HTTP response code. If set to 0, PHP sends Status: header that +; is supported by Apache. When this option is set to 1, PHP will send +; RFC2616 compliant header. +; Default is zero. +; http://php.net/cgi.rfc2616-headers +;cgi.rfc2616_headers = 0 + +; cgi.check_shebang_line controls whether CGI PHP checks for line starting with #! +; (shebang) at the top of the running script. This line might be needed if the +; script support running both as stand-alone script and via PHP CGI<. PHP in CGI +; mode skips this line and ignores its content if this directive is turned on. +; http://php.net/cgi.check-shebang-line +;cgi.check_shebang_line=1 + +;;;;;;;;;;;;;;;; +; File Uploads ; +;;;;;;;;;;;;;;;; + +; Whether to allow HTTP file uploads. +; http://php.net/file-uploads +file_uploads = On + +; Temporary directory for HTTP uploaded files (will use system default if not +; specified). +; http://php.net/upload-tmp-dir +;upload_tmp_dir = + +; Maximum allowed size for uploaded files. +; http://php.net/upload-max-filesize +upload_max_filesize = {{ getv "/php/upload/max/filesize" "2M" }} + +; Maximum number of files that can be uploaded via a single request +max_file_uploads = {{ getv "/php/max/file/uploads" "20" }} + +;;;;;;;;;;;;;;;;;; +; Fopen wrappers ; +;;;;;;;;;;;;;;;;;; + +; Whether to allow the treatment of URLs (like http:// or ftp://) as files. +; http://php.net/allow-url-fopen +allow_url_fopen = On + +; Whether to allow include/require to open URLs (like http:// or ftp://) as files. +; http://php.net/allow-url-include +allow_url_include = Off + +; Define the anonymous ftp password (your email address). PHP's default setting +; for this is empty. +; http://php.net/from +;from="john@doe.com" + +; Define the User-Agent string. PHP's default setting for this is empty. +; http://php.net/user-agent +;user_agent="PHP" + +; Default timeout for socket based streams (seconds) +; http://php.net/default-socket-timeout +default_socket_timeout = {{ getv "/php/default/socket/timeout" "60" }} + +; If your scripts have to deal with files from Macintosh systems, +; or you are running on a Mac and need to deal with files from +; unix or win32 systems, setting this flag will cause PHP to +; automatically detect the EOL character in those files so that +; fgets() and file() will work regardless of the source of the file. +; http://php.net/auto-detect-line-endings +;auto_detect_line_endings = Off + +;;;;;;;;;;;;;;;;;;;;;; +; Dynamic Extensions ; +;;;;;;;;;;;;;;;;;;;;;; + +; If you wish to have an extension loaded automatically, use the following +; syntax: +; +; extension=modulename +; +; For example: +; +; extension=mysqli +; +; When the extension library to load is not located in the default extension +; directory, You may specify an absolute path to the library file: +; +; extension=/path/to/extension/mysqli.so +; +; Note : The syntax used in previous PHP versions ('extension=.so' and +; 'extension='php_.dll') is supported for legacy reasons and may be +; deprecated in a future PHP major version. So, when it is possible, please +; move to the new ('extension=) syntax. +; +; Notes for Windows environments : +; +; - Many DLL files are located in the extensions/ (PHP 4) or ext/ (PHP 5+) +; extension folders as well as the separate PECL DLL download (PHP 5+). +; Be sure to appropriately set the extension_dir directive. +; +;extension=bz2 +;extension=curl +;extension=fileinfo +;extension=gd2 +;extension=gettext +;extension=gmp +;extension=intl +;extension=imap +;extension=interbase +;extension=ldap +;extension=mbstring +;extension=exif ; Must be after mbstring as it depends on it +;extension=mysqli +;extension=oci8_12c ; Use with Oracle Database 12c Instant Client +;extension=odbc +;extension=openssl +;extension=pdo_firebird +;extension=pdo_mysql +;extension=pdo_oci +;extension=pdo_odbc +;extension=pdo_pgsql +;extension=pdo_sqlite +;extension=pgsql +;extension=shmop + +; The MIBS data available in the PHP distribution must be installed. +; See http://www.php.net/manual/en/snmp.installation.php +;extension=snmp + +;extension=soap +;extension=sockets +;extension=sodium +;extension=sqlite3 +;extension=tidy +;extension=xmlrpc +;extension=xsl + +;;;;;;;;;;;;;;;;;;; +; Module Settings ; +;;;;;;;;;;;;;;;;;;; + +[CLI Server] +; Whether the CLI web server uses ANSI color coding in its terminal output. +cli_server.color = On + +[Date] +; Defines the default timezone used by the date functions +; http://php.net/date.timezone +;date.timezone = + +; http://php.net/date.default-latitude +;date.default_latitude = 31.7667 + +; http://php.net/date.default-longitude +;date.default_longitude = 35.2333 + +; http://php.net/date.sunrise-zenith +;date.sunrise_zenith = 90.583333 + +; http://php.net/date.sunset-zenith +;date.sunset_zenith = 90.583333 + +[filter] +; http://php.net/filter.default +;filter.default = unsafe_raw + +; http://php.net/filter.default-flags +;filter.default_flags = + +[iconv] +; Use of this INI entry is deprecated, use global input_encoding instead. +; If empty, default_charset or input_encoding or iconv.input_encoding is used. +; The precedence is: default_charset < input_encoding < iconv.input_encoding +;iconv.input_encoding = + +; Use of this INI entry is deprecated, use global internal_encoding instead. +; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. +; The precedence is: default_charset < internal_encoding < iconv.internal_encoding +;iconv.internal_encoding = + +; Use of this INI entry is deprecated, use global output_encoding instead. +; If empty, default_charset or output_encoding or iconv.output_encoding is used. +; The precedence is: default_charset < output_encoding < iconv.output_encoding +; To use an output encoding conversion, iconv's output handler must be set +; otherwise output encoding conversion cannot be performed. +;iconv.output_encoding = + +[imap] +; rsh/ssh logins are disabled by default. Use this INI entry if you want to +; enable them. Note that the IMAP library does not filter mailbox names before +; passing them to rsh/ssh command, thus passing untrusted data to this function +; with rsh/ssh enabled is insecure. +;imap.enable_insecure_rsh=0 + +[intl] +;intl.default_locale = +; This directive allows you to produce PHP errors when some error +; happens within intl functions. The value is the level of the error produced. +; Default is 0, which does not produce any errors. +;intl.error_level = E_WARNING +;intl.use_exceptions = 0 + +[sqlite3] +; Directory pointing to SQLite3 extensions +; http://php.net/sqlite3.extension-dir +;sqlite3.extension_dir = + +; SQLite defensive mode flag (only available from SQLite 3.26+) +; When the defensive flag is enabled, language features that allow ordinary +; SQL to deliberately corrupt the database file are disabled. This forbids +; writing directly to the schema, shadow tables (eg. FTS data tables), or +; the sqlite_dbpage virtual table. +; https://www.sqlite.org/c3ref/c_dbconfig_defensive.html +; (for older SQLite versions, this flag has no use) +;sqlite3.defensive = 1 + +[Pcre] +; PCRE library backtracking limit. +; http://php.net/pcre.backtrack-limit +;pcre.backtrack_limit=100000 + +; PCRE library recursion limit. +; Please note that if you set this value to a high number you may consume all +; the available process stack and eventually crash PHP (due to reaching the +; stack size limit imposed by the Operating System). +; http://php.net/pcre.recursion-limit +;pcre.recursion_limit=100000 + +; Enables or disables JIT compilation of patterns. This requires the PCRE +; library to be compiled with JIT support. +;pcre.jit=1 + +[Pdo] +; Whether to pool ODBC connections. Can be one of "strict", "relaxed" or "off" +; http://php.net/pdo-odbc.connection-pooling +;pdo_odbc.connection_pooling=strict + +;pdo_odbc.db2_instance_name + +[Pdo_mysql] +; Default socket name for local MySQL connects. If empty, uses the built-in +; MySQL defaults. +pdo_mysql.default_socket= + +[Phar] +; http://php.net/phar.readonly +;phar.readonly = On + +; http://php.net/phar.require-hash +;phar.require_hash = On + +;phar.cache_list = + +[mail function] +; For Win32 only. +; http://php.net/smtp +SMTP = localhost +; http://php.net/smtp-port +smtp_port = 25 + +; For Win32 only. +; http://php.net/sendmail-from +;sendmail_from = me@example.com + +; For Unix only. You may supply arguments as well (default: "sendmail -t -i"). +; http://php.net/sendmail-path +;sendmail_path = + +; Force the addition of the specified parameters to be passed as extra parameters +; to the sendmail binary. These parameters will always replace the value of +; the 5th parameter to mail(). +;mail.force_extra_parameters = + +; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename +mail.add_x_header = Off + +; The path to a log file that will log all mail() calls. Log entries include +; the full path of the script, line number, To address and headers. +;mail.log = +; Log mail to syslog (Event Log on Windows). +;mail.log = syslog + +[ODBC] +; http://php.net/odbc.default-db +;odbc.default_db = Not yet implemented + +; http://php.net/odbc.default-user +;odbc.default_user = Not yet implemented + +; http://php.net/odbc.default-pw +;odbc.default_pw = Not yet implemented + +; Controls the ODBC cursor model. +; Default: SQL_CURSOR_STATIC (default). +;odbc.default_cursortype + +; Allow or prevent persistent links. +; http://php.net/odbc.allow-persistent +odbc.allow_persistent = On + +; Check that a connection is still valid before reuse. +; http://php.net/odbc.check-persistent +odbc.check_persistent = On + +; Maximum number of persistent links. -1 means no limit. +; http://php.net/odbc.max-persistent +odbc.max_persistent = -1 + +; Maximum number of links (persistent + non-persistent). -1 means no limit. +; http://php.net/odbc.max-links +odbc.max_links = -1 + +; Handling of LONG fields. Returns number of bytes to variables. 0 means +; passthru. +; http://php.net/odbc.defaultlrl +odbc.defaultlrl = 4096 + +; Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char. +; See the documentation on odbc_binmode and odbc_longreadlen for an explanation +; of odbc.defaultlrl and odbc.defaultbinmode +; http://php.net/odbc.defaultbinmode +odbc.defaultbinmode = 1 + +[Interbase] +; Allow or prevent persistent links. +ibase.allow_persistent = 1 + +; Maximum number of persistent links. -1 means no limit. +ibase.max_persistent = -1 + +; Maximum number of links (persistent + non-persistent). -1 means no limit. +ibase.max_links = -1 + +; Default database name for ibase_connect(). +;ibase.default_db = + +; Default username for ibase_connect(). +;ibase.default_user = + +; Default password for ibase_connect(). +;ibase.default_password = + +; Default charset for ibase_connect(). +;ibase.default_charset = + +; Default timestamp format. +ibase.timestampformat = "%Y-%m-%d %H:%M:%S" + +; Default date format. +ibase.dateformat = "%Y-%m-%d" + +; Default time format. +ibase.timeformat = "%H:%M:%S" + +[MySQLi] + +; Maximum number of persistent links. -1 means no limit. +; http://php.net/mysqli.max-persistent +mysqli.max_persistent = -1 + +; Allow accessing, from PHP's perspective, local files with LOAD DATA statements +; http://php.net/mysqli.allow_local_infile +;mysqli.allow_local_infile = On + +; Allow or prevent persistent links. +; http://php.net/mysqli.allow-persistent +mysqli.allow_persistent = On + +; Maximum number of links. -1 means no limit. +; http://php.net/mysqli.max-links +mysqli.max_links = -1 + +; Default port number for mysqli_connect(). If unset, mysqli_connect() will use +; the $MYSQL_TCP_PORT or the mysql-tcp entry in /etc/services or the +; compile-time value defined MYSQL_PORT (in that order). Win32 will only look +; at MYSQL_PORT. +; http://php.net/mysqli.default-port +mysqli.default_port = 3306 + +; Default socket name for local MySQL connects. If empty, uses the built-in +; MySQL defaults. +; http://php.net/mysqli.default-socket +mysqli.default_socket = + +; Default host for mysql_connect() (doesn't apply in safe mode). +; http://php.net/mysqli.default-host +mysqli.default_host = + +; Default user for mysql_connect() (doesn't apply in safe mode). +; http://php.net/mysqli.default-user +mysqli.default_user = + +; Default password for mysqli_connect() (doesn't apply in safe mode). +; Note that this is generally a *bad* idea to store passwords in this file. +; *Any* user with PHP access can run 'echo get_cfg_var("mysqli.default_pw") +; and reveal this password! And of course, any users with read access to this +; file will be able to reveal the password as well. +; http://php.net/mysqli.default-pw +mysqli.default_pw = + +; Allow or prevent reconnect +mysqli.reconnect = Off + +[mysqlnd] +; Enable / Disable collection of general statistics by mysqlnd which can be +; used to tune and monitor MySQL operations. +mysqlnd.collect_statistics = On + +; Enable / Disable collection of memory usage statistics by mysqlnd which can be +; used to tune and monitor MySQL operations. +mysqlnd.collect_memory_statistics = Off + +; Records communication from all extensions using mysqlnd to the specified log +; file. +; http://php.net/mysqlnd.debug +;mysqlnd.debug = + +; Defines which queries will be logged. +;mysqlnd.log_mask = 0 + +; Default size of the mysqlnd memory pool, which is used by result sets. +;mysqlnd.mempool_default_size = 16000 + +; Size of a pre-allocated buffer used when sending commands to MySQL in bytes. +;mysqlnd.net_cmd_buffer_size = 2048 + +; Size of a pre-allocated buffer used for reading data sent by the server in +; bytes. +;mysqlnd.net_read_buffer_size = 32768 + +; Timeout for network requests in seconds. +;mysqlnd.net_read_timeout = 31536000 + +; SHA-256 Authentication Plugin related. File with the MySQL server public RSA +; key. +;mysqlnd.sha256_server_public_key = + +[OCI8] + +; Connection: Enables privileged connections using external +; credentials (OCI_SYSOPER, OCI_SYSDBA) +; http://php.net/oci8.privileged-connect +;oci8.privileged_connect = Off + +; Connection: The maximum number of persistent OCI8 connections per +; process. Using -1 means no limit. +; http://php.net/oci8.max-persistent +;oci8.max_persistent = -1 + +; Connection: The maximum number of seconds a process is allowed to +; maintain an idle persistent connection. Using -1 means idle +; persistent connections will be maintained forever. +; http://php.net/oci8.persistent-timeout +;oci8.persistent_timeout = -1 + +; Connection: The number of seconds that must pass before issuing a +; ping during oci_pconnect() to check the connection validity. When +; set to 0, each oci_pconnect() will cause a ping. Using -1 disables +; pings completely. +; http://php.net/oci8.ping-interval +;oci8.ping_interval = 60 + +; Connection: Set this to a user chosen connection class to be used +; for all pooled server requests with Oracle 11g Database Resident +; Connection Pooling (DRCP). To use DRCP, this value should be set to +; the same string for all web servers running the same application, +; the database pool must be configured, and the connection string must +; specify to use a pooled server. +;oci8.connection_class = + +; High Availability: Using On lets PHP receive Fast Application +; Notification (FAN) events generated when a database node fails. The +; database must also be configured to post FAN events. +;oci8.events = Off + +; Tuning: This option enables statement caching, and specifies how +; many statements to cache. Using 0 disables statement caching. +; http://php.net/oci8.statement-cache-size +;oci8.statement_cache_size = 20 + +; Tuning: Enables statement prefetching and sets the default number of +; rows that will be fetched automatically after statement execution. +; http://php.net/oci8.default-prefetch +;oci8.default_prefetch = 100 + +; Compatibility. Using On means oci_close() will not close +; oci_connect() and oci_new_connect() connections. +; http://php.net/oci8.old-oci-close-semantics +;oci8.old_oci_close_semantics = Off + +[PostgreSQL] +; Allow or prevent persistent links. +; http://php.net/pgsql.allow-persistent +pgsql.allow_persistent = On + +; Detect broken persistent links always with pg_pconnect(). +; Auto reset feature requires a little overheads. +; http://php.net/pgsql.auto-reset-persistent +pgsql.auto_reset_persistent = Off + +; Maximum number of persistent links. -1 means no limit. +; http://php.net/pgsql.max-persistent +pgsql.max_persistent = -1 + +; Maximum number of links (persistent+non persistent). -1 means no limit. +; http://php.net/pgsql.max-links +pgsql.max_links = -1 + +; Ignore PostgreSQL backends Notice message or not. +; Notice message logging require a little overheads. +; http://php.net/pgsql.ignore-notice +pgsql.ignore_notice = 0 + +; Log PostgreSQL backends Notice message or not. +; Unless pgsql.ignore_notice=0, module cannot log notice message. +; http://php.net/pgsql.log-notice +pgsql.log_notice = 0 + +[bcmath] +; Number of decimal digits for all bcmath functions. +; http://php.net/bcmath.scale +bcmath.scale = 0 + +[browscap] +; http://php.net/browscap +;browscap = extra/browscap.ini + +[Session] +; Handler used to store/retrieve data. +; http://php.net/session.save-handler +session.save_handler = files + +; Argument passed to save_handler. In the case of files, this is the path +; where data files are stored. Note: Windows users have to change this +; variable in order to use PHP's session functions. +; +; The path can be defined as: +; +; session.save_path = "N;/path" +; +; where N is an integer. Instead of storing all the session files in +; /path, what this will do is use subdirectories N-levels deep, and +; store the session data in those directories. This is useful if +; your OS has problems with many files in one directory, and is +; a more efficient layout for servers that handle many sessions. +; +; NOTE 1: PHP will not create this directory structure automatically. +; You can use the script in the ext/session dir for that purpose. +; NOTE 2: See the section on garbage collection below if you choose to +; use subdirectories for session storage +; +; The file storage module creates files using mode 600 by default. +; You can change that by using +; +; session.save_path = "N;MODE;/path" +; +; where MODE is the octal representation of the mode. Note that this +; does not overwrite the process's umask. +; http://php.net/session.save-path +;session.save_path = "/tmp" + +; Whether to use strict session mode. +; Strict session mode does not accept an uninitialized session ID, and +; regenerates the session ID if the browser sends an uninitialized session ID. +; Strict mode protects applications from session fixation via a session adoption +; vulnerability. It is disabled by default for maximum compatibility, but +; enabling it is encouraged. +; https://wiki.php.net/rfc/strict_sessions +session.use_strict_mode = 0 + +; Whether to use cookies. +; http://php.net/session.use-cookies +session.use_cookies = 1 + +; http://php.net/session.cookie-secure +;session.cookie_secure = + +; This option forces PHP to fetch and use a cookie for storing and maintaining +; the session id. We encourage this operation as it's very helpful in combating +; session hijacking when not specifying and managing your own session id. It is +; not the be-all and end-all of session hijacking defense, but it's a good start. +; http://php.net/session.use-only-cookies +session.use_only_cookies = 1 + +; Name of the session (used as cookie name). +; http://php.net/session.name +session.name = PHPSESSID + +; Initialize session on request startup. +; http://php.net/session.auto-start +session.auto_start = 0 + +; Lifetime in seconds of cookie or, if 0, until browser is restarted. +; http://php.net/session.cookie-lifetime +session.cookie_lifetime = 0 + +; The path for which the cookie is valid. +; http://php.net/session.cookie-path +session.cookie_path = / + +; The domain for which the cookie is valid. +; http://php.net/session.cookie-domain +session.cookie_domain = + +; Whether or not to add the httpOnly flag to the cookie, which makes it +; inaccessible to browser scripting languages such as JavaScript. +; http://php.net/session.cookie-httponly +session.cookie_httponly = + +; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF) +; Current valid values are "Strict", "Lax" or "None". When using "None", +; make sure to include the quotes, as `none` is interpreted like `false` in ini files. +; https://tools.ietf.org/html/draft-west-first-party-cookies-07 +session.cookie_samesite = + +; Handler used to serialize data. php is the standard serializer of PHP. +; http://php.net/session.serialize-handler +session.serialize_handler = php + +; Defines the probability that the 'garbage collection' process is started on every +; session initialization. The probability is calculated by using gc_probability/gc_divisor, +; e.g. 1/100 means there is a 1% chance that the GC process starts on each request. +; Default Value: 1 +; Development Value: 1 +; Production Value: 1 +; http://php.net/session.gc-probability +session.gc_probability = 1 + +; Defines the probability that the 'garbage collection' process is started on every +; session initialization. The probability is calculated by using gc_probability/gc_divisor, +; e.g. 1/100 means there is a 1% chance that the GC process starts on each request. +; For high volume production servers, using a value of 1000 is a more efficient approach. +; Default Value: 100 +; Development Value: 1000 +; Production Value: 1000 +; http://php.net/session.gc-divisor +session.gc_divisor = 1000 + +; After this number of seconds, stored data will be seen as 'garbage' and +; cleaned up by the garbage collection process. +; http://php.net/session.gc-maxlifetime +session.gc_maxlifetime = 1440 + +; NOTE: If you are using the subdirectory option for storing session files +; (see session.save_path above), then garbage collection does *not* +; happen automatically. You will need to do your own garbage +; collection through a shell script, cron entry, or some other method. +; For example, the following script is the equivalent of setting +; session.gc_maxlifetime to 1440 (1440 seconds = 24 minutes): +; find /path/to/sessions -cmin +24 -type f | xargs rm + +; Check HTTP Referer to invalidate externally stored URLs containing ids. +; HTTP_REFERER has to contain this substring for the session to be +; considered as valid. +; http://php.net/session.referer-check +session.referer_check = + +; Set to {nocache,private,public,} to determine HTTP caching aspects +; or leave this empty to avoid sending anti-caching headers. +; http://php.net/session.cache-limiter +session.cache_limiter = nocache + +; Document expires after n minutes. +; http://php.net/session.cache-expire +session.cache_expire = 180 + +; trans sid support is disabled by default. +; Use of trans sid may risk your users' security. +; Use this option with caution. +; - User may send URL contains active session ID +; to other person via. email/irc/etc. +; - URL that contains active session ID may be stored +; in publicly accessible computer. +; - User may access your site with the same session ID +; always using URL stored in browser's history or bookmarks. +; http://php.net/session.use-trans-sid +session.use_trans_sid = 0 + +; Set session ID character length. This value could be between 22 to 256. +; Shorter length than default is supported only for compatibility reason. +; Users should use 32 or more chars. +; http://php.net/session.sid-length +; Default Value: 32 +; Development Value: 26 +; Production Value: 26 +session.sid_length = 26 + +; The URL rewriter will look for URLs in a defined set of HTML tags. +;
is special; if you include them here, the rewriter will +; add a hidden field with the info which is otherwise appended +; to URLs. tag's action attribute URL will not be modified +; unless it is specified. +; Note that all valid entries require a "=", even if no value follows. +; Default Value: "a=href,area=href,frame=src,form=" +; Development Value: "a=href,area=href,frame=src,form=" +; Production Value: "a=href,area=href,frame=src,form=" +; http://php.net/url-rewriter.tags +session.trans_sid_tags = "a=href,area=href,frame=src,form=" + +; URL rewriter does not rewrite absolute URLs by default. +; To enable rewrites for absolute paths, target hosts must be specified +; at RUNTIME. i.e. use ini_set() +; tags is special. PHP will check action attribute's URL regardless +; of session.trans_sid_tags setting. +; If no host is defined, HTTP_HOST will be used for allowed host. +; Example value: php.net,www.php.net,wiki.php.net +; Use "," for multiple hosts. No spaces are allowed. +; Default Value: "" +; Development Value: "" +; Production Value: "" +;session.trans_sid_hosts="" + +; Define how many bits are stored in each character when converting +; the binary hash data to something readable. +; Possible values: +; 4 (4 bits: 0-9, a-f) +; 5 (5 bits: 0-9, a-v) +; 6 (6 bits: 0-9, a-z, A-Z, "-", ",") +; Default Value: 4 +; Development Value: 5 +; Production Value: 5 +; http://php.net/session.hash-bits-per-character +session.sid_bits_per_character = 5 + +; Enable upload progress tracking in $_SESSION +; Default Value: On +; Development Value: On +; Production Value: On +; http://php.net/session.upload-progress.enabled +;session.upload_progress.enabled = On + +; Cleanup the progress information as soon as all POST data has been read +; (i.e. upload completed). +; Default Value: On +; Development Value: On +; Production Value: On +; http://php.net/session.upload-progress.cleanup +;session.upload_progress.cleanup = On + +; A prefix used for the upload progress key in $_SESSION +; Default Value: "upload_progress_" +; Development Value: "upload_progress_" +; Production Value: "upload_progress_" +; http://php.net/session.upload-progress.prefix +;session.upload_progress.prefix = "upload_progress_" + +; The index name (concatenated with the prefix) in $_SESSION +; containing the upload progress information +; Default Value: "PHP_SESSION_UPLOAD_PROGRESS" +; Development Value: "PHP_SESSION_UPLOAD_PROGRESS" +; Production Value: "PHP_SESSION_UPLOAD_PROGRESS" +; http://php.net/session.upload-progress.name +;session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS" + +; How frequently the upload progress should be updated. +; Given either in percentages (per-file), or in bytes +; Default Value: "1%" +; Development Value: "1%" +; Production Value: "1%" +; http://php.net/session.upload-progress.freq +;session.upload_progress.freq = "1%" + +; The minimum delay between updates, in seconds +; Default Value: 1 +; Development Value: 1 +; Production Value: 1 +; http://php.net/session.upload-progress.min-freq +;session.upload_progress.min_freq = "1" + +; Only write session data when session data is changed. Enabled by default. +; http://php.net/session.lazy-write +;session.lazy_write = On + +[Assertion] +; Switch whether to compile assertions at all (to have no overhead at run-time) +; -1: Do not compile at all +; 0: Jump over assertion at run-time +; 1: Execute assertions +; Changing from or to a negative value is only possible in php.ini! (For turning assertions on and off at run-time, see assert.active, when zend.assertions = 1) +; Default Value: 1 +; Development Value: 1 +; Production Value: -1 +; http://php.net/zend.assertions +zend.assertions = -1 + +; Assert(expr); active by default. +; http://php.net/assert.active +;assert.active = On + +; Throw an AssertionError on failed assertions +; http://php.net/assert.exception +;assert.exception = On + +; Issue a PHP warning for each failed assertion. (Overridden by assert.exception if active) +; http://php.net/assert.warning +;assert.warning = On + +; Don't bail out by default. +; http://php.net/assert.bail +;assert.bail = Off + +; User-function to be called if an assertion fails. +; http://php.net/assert.callback +;assert.callback = 0 + +; Eval the expression with current error_reporting(). Set to true if you want +; error_reporting(0) around the eval(). +; http://php.net/assert.quiet-eval +;assert.quiet_eval = 0 + +[COM] +; path to a file containing GUIDs, IIDs or filenames of files with TypeLibs +; http://php.net/com.typelib-file +;com.typelib_file = + +; allow Distributed-COM calls +; http://php.net/com.allow-dcom +;com.allow_dcom = true + +; autoregister constants of a component's typlib on com_load() +; http://php.net/com.autoregister-typelib +;com.autoregister_typelib = true + +; register constants casesensitive +; http://php.net/com.autoregister-casesensitive +;com.autoregister_casesensitive = false + +; show warnings on duplicate constant registrations +; http://php.net/com.autoregister-verbose +;com.autoregister_verbose = true + +; The default character set code-page to use when passing strings to and from COM objects. +; Default: system ANSI code page +;com.code_page= + +[mbstring] +; language for internal character representation. +; This affects mb_send_mail() and mbstring.detect_order. +; http://php.net/mbstring.language +;mbstring.language = Japanese + +; Use of this INI entry is deprecated, use global internal_encoding instead. +; internal/script encoding. +; Some encoding cannot work as internal encoding. (e.g. SJIS, BIG5, ISO-2022-*) +; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. +; The precedence is: default_charset < internal_encoding < iconv.internal_encoding +;mbstring.internal_encoding = + +; Use of this INI entry is deprecated, use global input_encoding instead. +; http input encoding. +; mbstring.encoding_translation = On is needed to use this setting. +; If empty, default_charset or input_encoding or mbstring.input is used. +; The precedence is: default_charset < input_encoding < mbsting.http_input +; http://php.net/mbstring.http-input +;mbstring.http_input = + +; Use of this INI entry is deprecated, use global output_encoding instead. +; http output encoding. +; mb_output_handler must be registered as output buffer to function. +; If empty, default_charset or output_encoding or mbstring.http_output is used. +; The precedence is: default_charset < output_encoding < mbstring.http_output +; To use an output encoding conversion, mbstring's output handler must be set +; otherwise output encoding conversion cannot be performed. +; http://php.net/mbstring.http-output +;mbstring.http_output = + +; enable automatic encoding translation according to +; mbstring.internal_encoding setting. Input chars are +; converted to internal encoding by setting this to On. +; Note: Do _not_ use automatic encoding translation for +; portable libs/applications. +; http://php.net/mbstring.encoding-translation +;mbstring.encoding_translation = Off + +; automatic encoding detection order. +; "auto" detect order is changed according to mbstring.language +; http://php.net/mbstring.detect-order +;mbstring.detect_order = auto + +; substitute_character used when character cannot be converted +; one from another +; http://php.net/mbstring.substitute-character +;mbstring.substitute_character = none + +; overload(replace) single byte functions by mbstring functions. +; mail(), ereg(), etc are overloaded by mb_send_mail(), mb_ereg(), +; etc. Possible values are 0,1,2,4 or combination of them. +; For example, 7 for overload everything. +; 0: No overload +; 1: Overload mail() function +; 2: Overload str*() functions +; 4: Overload ereg*() functions +; http://php.net/mbstring.func-overload +;mbstring.func_overload = 0 + +; enable strict encoding detection. +; Default: Off +;mbstring.strict_detection = On + +; This directive specifies the regex pattern of content types for which mb_output_handler() +; is activated. +; Default: mbstring.http_output_conv_mimetype=^(text/|application/xhtml\+xml) +;mbstring.http_output_conv_mimetype= + +; This directive specifies maximum stack depth for mbstring regular expressions. It is similar +; to the pcre.recursion_limit for PCRE. +; Default: 100000 +;mbstring.regex_stack_limit=100000 + +[gd] +; Tell the jpeg decode to ignore warnings and try to create +; a gd image. The warning will then be displayed as notices +; disabled by default +; http://php.net/gd.jpeg-ignore-warning +;gd.jpeg_ignore_warning = 1 + +[exif] +; Exif UNICODE user comments are handled as UCS-2BE/UCS-2LE and JIS as JIS. +; With mbstring support this will automatically be converted into the encoding +; given by corresponding encode setting. When empty mbstring.internal_encoding +; is used. For the decode settings you can distinguish between motorola and +; intel byte order. A decode setting cannot be empty. +; http://php.net/exif.encode-unicode +;exif.encode_unicode = ISO-8859-15 + +; http://php.net/exif.decode-unicode-motorola +;exif.decode_unicode_motorola = UCS-2BE + +; http://php.net/exif.decode-unicode-intel +;exif.decode_unicode_intel = UCS-2LE + +; http://php.net/exif.encode-jis +;exif.encode_jis = + +; http://php.net/exif.decode-jis-motorola +;exif.decode_jis_motorola = JIS + +; http://php.net/exif.decode-jis-intel +;exif.decode_jis_intel = JIS + +[Tidy] +; The path to a default tidy configuration file to use when using tidy +; http://php.net/tidy.default-config +;tidy.default_config = /usr/local/lib/php/default.tcfg + +; Should tidy clean and repair output automatically? +; WARNING: Do not use this option if you are generating non-html content +; such as dynamic images +; http://php.net/tidy.clean-output +tidy.clean_output = Off + +[soap] +; Enables or disables WSDL caching feature. +; http://php.net/soap.wsdl-cache-enabled +soap.wsdl_cache_enabled=1 + +; Sets the directory name where SOAP extension will put cache files. +; http://php.net/soap.wsdl-cache-dir +soap.wsdl_cache_dir="/tmp" + +; (time to live) Sets the number of second while cached file will be used +; instead of original one. +; http://php.net/soap.wsdl-cache-ttl +soap.wsdl_cache_ttl=86400 + +; Sets the size of the cache limit. (Max. number of WSDL files to cache) +soap.wsdl_cache_limit = 5 + +[sysvshm] +; A default size of the shared memory segment +;sysvshm.init_mem = 10000 + +[ldap] +; Sets the maximum number of open links or -1 for unlimited. +ldap.max_links = -1 + +[dba] +;dba.default_handler= + +[opcache] +; Determines if Zend OPCache is enabled +;opcache.enable=1 + +; Determines if Zend OPCache is enabled for the CLI version of PHP +;opcache.enable_cli=0 + +; The OPcache shared memory storage size. +;opcache.memory_consumption=128 + +; The amount of memory for interned strings in Mbytes. +;opcache.interned_strings_buffer=8 + +; The maximum number of keys (scripts) in the OPcache hash table. +; Only numbers between 200 and 1000000 are allowed. +;opcache.max_accelerated_files=10000 + +; The maximum percentage of "wasted" memory until a restart is scheduled. +;opcache.max_wasted_percentage=5 + +; When this directive is enabled, the OPcache appends the current working +; directory to the script key, thus eliminating possible collisions between +; files with the same name (basename). Disabling the directive improves +; performance, but may break existing applications. +;opcache.use_cwd=1 + +; When disabled, you must reset the OPcache manually or restart the +; webserver for changes to the filesystem to take effect. +;opcache.validate_timestamps=1 + +; How often (in seconds) to check file timestamps for changes to the shared +; memory storage allocation. ("1" means validate once per second, but only +; once per request. "0" means always validate) +;opcache.revalidate_freq=2 + +; Enables or disables file search in include_path optimization +;opcache.revalidate_path=0 + +; If disabled, all PHPDoc comments are dropped from the code to reduce the +; size of the optimized code. +;opcache.save_comments=1 + +; Allow file existence override (file_exists, etc.) performance feature. +;opcache.enable_file_override=0 + +; A bitmask, where each bit enables or disables the appropriate OPcache +; passes +;opcache.optimization_level=0x7FFFBFFF + +;opcache.dups_fix=0 + +; The location of the OPcache blacklist file (wildcards allowed). +; Each OPcache blacklist file is a text file that holds the names of files +; that should not be accelerated. The file format is to add each filename +; to a new line. The filename may be a full path or just a file prefix +; (i.e., /var/www/x blacklists all the files and directories in /var/www +; that start with 'x'). Line starting with a ; are ignored (comments). +;opcache.blacklist_filename= + +; Allows exclusion of large files from being cached. By default all files +; are cached. +;opcache.max_file_size=0 + +; Check the cache checksum each N requests. +; The default value of "0" means that the checks are disabled. +;opcache.consistency_checks=0 + +; How long to wait (in seconds) for a scheduled restart to begin if the cache +; is not being accessed. +;opcache.force_restart_timeout=180 + +; OPcache error_log file name. Empty string assumes "stderr". +;opcache.error_log= + +; All OPcache errors go to the Web server log. +; By default, only fatal errors (level 0) or errors (level 1) are logged. +; You can also enable warnings (level 2), info messages (level 3) or +; debug messages (level 4). +;opcache.log_verbosity_level=1 + +; Preferred Shared Memory back-end. Leave empty and let the system decide. +;opcache.preferred_memory_model= + +; Protect the shared memory from unexpected writing during script execution. +; Useful for internal debugging only. +;opcache.protect_memory=0 + +; Allows calling OPcache API functions only from PHP scripts which path is +; started from specified string. The default "" means no restriction +;opcache.restrict_api= + +; Mapping base of shared memory segments (for Windows only). All the PHP +; processes have to map shared memory into the same address space. This +; directive allows to manually fix the "Unable to reattach to base address" +; errors. +;opcache.mmap_base= + +; Enables and sets the second level cache directory. +; It should improve performance when SHM memory is full, at server restart or +; SHM reset. The default "" disables file based caching. +;opcache.file_cache= + +; Enables or disables opcode caching in shared memory. +;opcache.file_cache_only=0 + +; Enables or disables checksum validation when script loaded from file cache. +;opcache.file_cache_consistency_checks=1 + +; Implies opcache.file_cache_only=1 for a certain process that failed to +; reattach to the shared memory (for Windows only). Explicitly enabled file +; cache is required. +;opcache.file_cache_fallback=1 + +; Enables or disables copying of PHP code (text segment) into HUGE PAGES. +; This should improve performance, but requires appropriate OS configuration. +;opcache.huge_code_pages=1 + +; Validate cached file permissions. +;opcache.validate_permission=0 + +; Prevent name collisions in chroot'ed environment. +;opcache.validate_root=0 + +; If specified, it produces opcode dumps for debugging different stages of +; optimizations. +;opcache.opt_debug_level=0 + +[curl] +; A default value for the CURLOPT_CAINFO option. This is required to be an +; absolute path. +;curl.cainfo = + +[openssl] +; The location of a Certificate Authority (CA) file on the local filesystem +; to use when verifying the identity of SSL/TLS peers. Most users should +; not specify a value for this directive as PHP will attempt to use the +; OS-managed cert stores in its absence. If specified, this value may still +; be overridden on a per-stream basis via the "cafile" SSL stream context +; option. +;openssl.cafile= + +; If openssl.cafile is not specified or if the CA file is not found, the +; directory pointed to by openssl.capath is searched for a suitable +; certificate. This value must be a correctly hashed certificate directory. +; Most users should not specify a value for this directive as PHP will +; attempt to use the OS-managed cert stores in its absence. If specified, +; this value may still be overridden on a per-stream basis via the "capath" +; SSL stream context option. +;openssl.capath= + +; Local Variables: +; tab-width: 4 +; End: diff --git a/nginx/rootfs/etc/confd/templates/www.conf.tmpl b/nginx/rootfs/etc/confd/templates/www.conf.tmpl new file mode 100644 index 00000000..b5069c3d --- /dev/null +++ b/nginx/rootfs/etc/confd/templates/www.conf.tmpl @@ -0,0 +1,438 @@ +; Start a new pool named 'www'. +; the variable $pool can be used in any directive and will be replaced by the +; pool name ('www' here) +[www] + +; Per pool prefix +; It only applies on the following directives: +; - 'access.log' +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or /usr) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = nginx +group = nginx + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +listen = /var/run/php-fpm7/php-fpm7.sock + +; Set listen(2) backlog. +; Default Value: 511 (-1 on FreeBSD and OpenBSD) +;listen.backlog = 511 + +; Set permissions for unix socket, if one is used. In Linux, read/write +; permissions must be set in order to allow connections from a web server. Many +; BSD-derived systems allow connections regardless of permissions. +; Default Values: user and group are set as the running user +; mode is set to 0660 +listen.owner = nginx +listen.group = nginx +listen.mode = 0660 +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a comma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = +;listen.acl_groups = + +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. +; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original +; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address +; must be separated by a comma. If this value is left blank, connections will be +; accepted from any ip address. +; Default Value: any +;listen.allowed_clients = 127.0.0.1 + +; Specify the nice(2) priority to apply to the pool processes (only if set) +; The value can vary from -19 (highest priority) to 20 (lower priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool processes will inherit the master process priority +; unless it specified otherwise +; Default Value: no set +; process.priority = -19 + +; Set the process dumpable flag (PR_SET_DUMPABLE prctl) even if the process user +; or group is differrent than the master process user. It allows to create process +; core dump and ptrace the process for the pool user. +; Default Value: no +; process.dumpable = yes + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 5 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = 2 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 1 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 3 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; + +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +;pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. It shows the following informations: +; pool - the name of the pool; +; process manager - static, dynamic or ondemand; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; +; idle processes - the number of idle processes; +; active processes - the number of active processes; +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; +; max children reached - number of times, the process limit has been reached, +; when pm tries to start more children (works only for +; pm 'dynamic' and 'ondemand'); +; Value are updated in real time. +; Example output: +; pool: www +; process manager: static +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; +; By default the status page output is formatted as text/plain. Passing either +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: +; http://www.foo.bar/status +; http://www.foo.bar/status?json +; http://www.foo.bar/status?html +; http://www.foo.bar/status?xml +; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: +; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full +; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full +; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then informations are related to the +; last request the process has served. Otherwise informations are related to +; the current request being served. +; Example output: +; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: /usr/share/php7/fpm/status.html +; +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;pm.status_path = /status + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. This could be used to test from outside +; that FPM is alive and responding, or to +; - create a graph of FPM availability (rrd or such); +; - remove a server from a group if it is not responding (load balancing); +; - trigger alerts for the operating team (24/7). +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +;ping.path = /ping + +; This directive may be used to customize the response of a ping request. The +; response is formatted as text/plain with a 200 response code. +; Default Value: pong +;ping.response = pong + +; The access log file +; Default: not set +;access.log = log/php7/$pool.access.log + +; The access log format. +; The following syntax is allowed +; %%: the '%' character +; %C: %CPU used by the request +; it can accept the following format: +; - %{user}C for user CPU only +; - %{system}C for system CPU only +; - %{total}C for user + system CPU (default) +; %d: time taken to serve the request +; it can accept the following format: +; - %{seconds}d (default) +; - %{miliseconds}d +; - %{mili}d +; - %{microseconds}d +; - %{micro}d +; %e: an environment variable (same as $_ENV or $_SERVER) +; it must be associated with embraces to specify the name of the env +; variable. Some exemples: +; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e +; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e +; %f: script filename +; %l: content-length of the request (for POST request only) +; %m: request method +; %M: peak of memory allocated by PHP +; it can accept the following format: +; - %{bytes}M (default) +; - %{kilobytes}M +; - %{kilo}M +; - %{megabytes}M +; - %{mega}M +; %n: pool name +; %o: output header +; it must be associated with embraces to specify the name of the header: +; - %{Content-Type}o +; - %{X-Powered-By}o +; - %{Transfert-Encoding}o +; - .... +; %p: PID of the child that serviced the request +; %P: PID of the parent of the child that serviced the request +; %q: the query string +; %Q: the '?' character if query string exists +; %r: the request URI (without the query string, see %q and %Q) +; %R: remote IP address +; %s: status (response code) +; %t: server time the request was received +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %T: time the log has been written (the request has finished) +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsuled in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %u: remote user +; +; Default: "%R - %u %t \"%m %r\" %s" +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" + +; The log file for slow requests +; Default Value: not set +; Note: slowlog is mandatory if request_slowlog_timeout is set +;slowlog = log/php7/$pool.slow.log + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_slowlog_timeout = 0 + +; Depth of slow log stack trace. +; Default Value: 20 +;request_slowlog_trace_depth = 20 + +; The timeout for serving a single request after which the worker process will +; be killed. This option should be used when the 'max_execution_time' ini option +; does not stop script execution for some reason. A value of '0' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_terminate_timeout = 0 + +; The timeout set by 'request_terminate_timeout' ini option is not engaged after +; application calls 'fastcgi_finish_request' or when application has finished and +; shutdown functions are being called (registered via register_shutdown_function). +; This option will enable timeout limit to be applied unconditionally +; even in such cases. +; Default Value: no +;request_terminate_timeout_track_finished = no + +; Set open file descriptor rlimit. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Chroot to this directory at the start. This value must be defined as an +; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. +; Note: chrooting is a great security feature and should be used whenever +; possible. However, all PHP paths will be relative to the chroot +; (error_log, sessions.save_path, ...). +; Default Value: not set +;chroot = + +; Chdir to this directory at the start. +; Note: relative path can be used. +; Default Value: current directory or / when chroot +;chdir = /var/www + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +;catch_workers_output = yes + +; Decorate worker output with prefix and suffix containing information about +; the child that writes to the log and if stdout or stderr is used as well as +; log level and time. This options is used only if catch_workers_output is yes. +; Settings to "no" will output data as written to the stdout or stderr. +; Default value: yes +;decorate_workers_output = no + +; Clear environment in FPM workers +; Prevents arbitrary environment variables from reaching FPM worker processes +; by clearing the environment in workers before env vars specified in this +; pool configuration are added. +; Setting to "no" will make all environment variables available to PHP code +; via getenv(), $_ENV and $_SERVER. +; Default Value: yes +;clear_env = no + +; Limits the extensions of the main script FPM will allow to parse. This can +; prevent configuration mistakes on the web server side. You should only limit +; FPM to .php extensions to prevent malicious users to use other extensions to +; execute php code. +; Note: set an empty value to allow all extensions. +; Default Value: .php +;security.limit_extensions = .php .php3 .php4 .php5 .php7 + +; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from +; the current environment. +; Default Value: clean env +;env[HOSTNAME] = $HOSTNAME +;env[PATH] = /usr/local/bin:/usr/bin:/bin +;env[TMP] = /tmp +;env[TMPDIR] = /tmp +;env[TEMP] = /tmp + +; Additional php.ini defines, specific to this pool of workers. These settings +; overwrite the values previously defined in the php.ini. The directives are the +; same as the PHP SAPI: +; php_value/php_flag - you can set classic ini defines which can +; be overwritten from PHP call 'ini_set'. +; php_admin_value/php_admin_flag - these directives won't be overwritten by +; PHP call 'ini_set' +; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. + +; Defining 'extension' will load the corresponding shared extension from +; extension_dir. Defining 'disable_functions' or 'disable_classes' will not +; overwrite previously defined php.ini values, but will append the new value +; instead. + +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or /usr) + +; Default Value: nothing is defined by default except the values in php.ini and +; specified at startup with the -d argument +;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com +;php_flag[display_errors] = off +;php_admin_value[error_log] = /var/log/php7/$pool.error.log +;php_admin_flag[log_errors] = on +;php_admin_value[memory_limit] = 32M diff --git a/nginx/rootfs/etc/cont-init.d/02-fpm-install.sh b/nginx/rootfs/etc/cont-init.d/02-fpm-install.sh new file mode 100644 index 00000000..78ac88dd --- /dev/null +++ b/nginx/rootfs/etc/cont-init.d/02-fpm-install.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +mkdir /run/php-fpm7 &> /dev/null || true diff --git a/nginx/rootfs/etc/cont-init.d/02-ngnix-install.sh b/nginx/rootfs/etc/cont-init.d/02-ngnix-install.sh new file mode 100755 index 00000000..c7d12cc6 --- /dev/null +++ b/nginx/rootfs/etc/cont-init.d/02-ngnix-install.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +mkdir /run/nginx &> /dev/null || true diff --git a/nginx/rootfs/etc/nginx/modules/daemon.conf b/nginx/rootfs/etc/nginx/modules/daemon.conf new file mode 100644 index 00000000..ef1f9c1c --- /dev/null +++ b/nginx/rootfs/etc/nginx/modules/daemon.conf @@ -0,0 +1 @@ +daemon off; diff --git a/nginx/rootfs/etc/services.d/fpm/finish b/nginx/rootfs/etc/services.d/fpm/finish new file mode 100644 index 00000000..9030da41 --- /dev/null +++ b/nginx/rootfs/etc/services.d/fpm/finish @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -S1 +# -*- mode: sh -*- +# vi: set ft=sh: +s6-svscanctl -t /var/run/s6/services diff --git a/nginx/rootfs/etc/services.d/fpm/run b/nginx/rootfs/etc/services.d/fpm/run new file mode 100644 index 00000000..3d5b066c --- /dev/null +++ b/nginx/rootfs/etc/services.d/fpm/run @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -P +# -*- mode: sh -*- +# vi: set ft=sh: +/usr/sbin/php-fpm7 diff --git a/nginx/rootfs/etc/services.d/nginx/finish b/nginx/rootfs/etc/services.d/nginx/finish new file mode 100644 index 00000000..9030da41 --- /dev/null +++ b/nginx/rootfs/etc/services.d/nginx/finish @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -S1 +# -*- mode: sh -*- +# vi: set ft=sh: +s6-svscanctl -t /var/run/s6/services diff --git a/nginx/rootfs/etc/services.d/nginx/run b/nginx/rootfs/etc/services.d/nginx/run new file mode 100644 index 00000000..7503678e --- /dev/null +++ b/nginx/rootfs/etc/services.d/nginx/run @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -P +# -*- mode: sh -*- +# vi: set ft=sh: +/usr/sbin/nginx diff --git a/recast/.dockerignore b/recast/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/recast/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/recast/Dockerfile b/recast/Dockerfile new file mode 100644 index 00000000..fb5b00b1 --- /dev/null +++ b/recast/Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/crayfish:latest + +RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + composer install -d /var/www/crayfish/Recast && \ + ln -s /var/www/crayfish/Recast/src /var/www/html && \ + cleanup.sh + +COPY /rootfs / + +RUN chown -R nginx:nginx /var/www + +WORKDIR /var/www/crayfish/Recast/ diff --git a/recast/README.md b/recast/README.md new file mode 100644 index 00000000..d91a2b94 --- /dev/null +++ b/recast/README.md @@ -0,0 +1,26 @@ +# Recast + +Docker image for [Recast]. + +## Dependencies + +Requires `islandora/crayfish` docker image to build. Please refer to the +[Crayfish Image README](../crayfish/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Settings + +| Environment Variable | Etcd Key | Default | Description | +| :------------------- | :----------------- | :---------------------- | :------------------------------------- | +| RECAST_DRUPAL_URL | /recast/drupal/url | drupal:80 | Drupal URL | +| RECAST_FCREPO_URL | /recast/fcrepo/url | fcrepo/fcrepo/rest | Fcrepo Rest API URL | +| RECAST_GEMINI_URL | /recast/gemini/url | gemini:8000 | Gemini URL | +| RECAST_LOG_LEVEL | /recast/log/level | WARNING | The log level for Recast micro-service | + +## Logs + +| Path | Description | +| :---------------------------- | :---------- | +| /var/log/islandora/recast.log | Recast Log | + +[Recast]: https://github.com/Islandora/Crayfish/tree/master/Recast diff --git a/recast/rootfs/etc/confd/conf.d/config.yaml.toml b/recast/rootfs/etc/confd/conf.d/config.yaml.toml new file mode 100644 index 00000000..7ca1f581 --- /dev/null +++ b/recast/rootfs/etc/confd/conf.d/config.yaml.toml @@ -0,0 +1,7 @@ +[template] +src = "config.yaml.tmpl" +dest = "/var/www/crayfish/Recast/cfg/config.yaml" +uid = 100 +gid = 101 +mode = "0644" +keys = [ "/log/level", "/fcrepo", "/drupal", "/gemini" ] diff --git a/recast/rootfs/etc/confd/confd.toml b/recast/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..e5403c82 --- /dev/null +++ b/recast/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/recast" diff --git a/recast/rootfs/etc/confd/templates/config.yaml.tmpl b/recast/rootfs/etc/confd/templates/config.yaml.tmpl new file mode 100644 index 00000000..04b958af --- /dev/null +++ b/recast/rootfs/etc/confd/templates/config.yaml.tmpl @@ -0,0 +1,37 @@ +--- + +fedora_base_url: {{ getv "/fcrepo/url" "fcrepo/fcrepo/rest" }} +# if drupal_base_url contains a path, be sure to include trailing slash +# or relative paths will not resolve correctly. +drupal_base_url: {{ getv "/drupal/url" "drupal:80" }} +gemini_base_url: {{ getv "/gemini/url" "gemini:8000" }} + +debug: false + +log: + # Valid log levels are: + # DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY, NONE + # log level none won't open logfile + level: {{ getv "/log/level" "DEBUG" }} + file: /var/log/islandora/recast.log + +syn: + # toggles JWT security for service + enable: false + # Path to the syn config file for authentication. + # example can be found here: + # https://github.com/Islandora/Syn/blob/master/conf/syn-settings.example.xml + config: /var/www/crayfish/syn-settings.xml + +# Add namespace prefixes used by Fedora for recast service +# Must be inside an array to maintain the internal associative array. +namespaces: +- + acl: "http://www.w3.org/ns/auth/acl#" + fedora: "http://fedora.info/definitions/v4/repository#" + ldp: "http://www.w3.org/ns/ldp#" + memento: "http://mementoweb.org/ns#" + pcdm: "http://pcdm.org/models#" + pcdmuse: "http://pcdm.org/use#" + webac: "http://fedora.info/definitions/v4/webac#" + vcard: "http://www.w3.org/2006/vcard/ns#" diff --git a/sandbox/.dockerignore b/sandbox/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/sandbox/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/sandbox/Dockerfile b/sandbox/Dockerfile new file mode 100644 index 00000000..07dd4298 --- /dev/null +++ b/sandbox/Dockerfile @@ -0,0 +1,52 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/drupal:latest + +# Islandora based Drupal install. +RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + --mount=id=downloads,type=cache,target=/opt/downloads \ + php -d memory_limit=-1 /usr/bin/composer create-project islandora/drupal-project:8.8.1 \ + --prefer-dist \ + --no-interaction \ + --stability stable \ + --no-dev \ + --no-install \ + /var/www/drupal \ + && \ + php -d memory_limit=-1 /usr/bin/composer require --update-no-dev -- drush/drush:^9.7.1 && \ + wget -N -P /opt/downloads https://github.com/drush-ops/drush-launcher/releases/latest/download/drush.phar && \ + cp /opt/downloads/drush.phar /usr/local/bin/drush && \ + chmod a+x /usr/local/bin/drush && \ + mkdir /var/www/drupal/config && \ + mkdir -p /var/www/drupal/web/libraries && \ + chown -R nginx:nginx /var/www && \ + php -d memory_limit=-1 /usr/bin/composer require --update-no-dev -- \ + zaporylie/composer-drupal-optimizations:^1.0 \ + drupal/console:~1.0 \ + drupal/devel:^2.0 \ + drush/drush:^9.0 \ + drupal/rdfui:^1.0-beta1 \ + drupal/restui:^1.16 \ + drupal/search_api_solr:^3.8 \ + drupal/facets:^1.3 \ + drupal/content_browser:^1.0@alpha \ + drupal/matomo:^1.7 \ + drupal/pdf:1.x-dev \ + drupal/admin_toolbar:^2.0 \ + drupal/rest_oai_pmh:^1.0 \ + drupal/transliterate_filenames:^1.3 \ + islandora/carapace:dev-8.x-3.x \ + islandora/islandora_defaults:dev-8.x-1.x \ + islandora-rdm/islandora_fits:dev-master && \ + MASONRY_VERSION=3.3.2 && \ + wget -N -P /opt/downloads https://github.com/desandro/masonry/archive/v${MASONRY_VERSION}.zip && \ + unzip "/opt/downloads/v${MASONRY_VERSION}.zip" -d /var/www/drupal/web/libraries && \ + mv /var/www/drupal/web/libraries/masonry-${MASONRY_VERSION} /var/www/drupal/web/libraries/masonry && \ + PDFJS_VERSION=2.0.943 && \ + wget -N -P /opt/downloads https://github.com/mozilla/pdf.js/releases/download/v${PDFJS_VERSION}/pdfjs-${PDFJS_VERSION}-dist.zip && \ + mkdir -p /var/www/drupal/web/libraries/pdfjs.js && \ + unzip "/opt/downloads/pdfjs-${PDFJS_VERSION}-dist.zip" -d /var/www/drupal/web/libraries/pdfjs.js && \ + cleanup.sh + +VOLUME [ "/opt/keys/claw", "/var/www/drupal/config", "/var/www/drupal/web/sites" ] + +COPY rootfs / diff --git a/sandbox/README.md b/sandbox/README.md new file mode 100644 index 00000000..ae9e1142 --- /dev/null +++ b/sandbox/README.md @@ -0,0 +1,11 @@ +# Sandbox + +Islandora Sandbox image meant for demoing a functioning system. If you want to +build your own Islandora site site the composer folder for a template on how to +do that. + +## Dependencies + +Requires `islandora/drupal` docker image to build. Please refer to the +[Drupal Image README](../drupal/README.md) for additional information including +additional settings, volumes, ports, etc. diff --git a/sandbox/rootfs/etc/confd/conf.d/blazegraph.properties.toml b/sandbox/rootfs/etc/confd/conf.d/blazegraph.properties.toml new file mode 100644 index 00000000..fcebdff1 --- /dev/null +++ b/sandbox/rootfs/etc/confd/conf.d/blazegraph.properties.toml @@ -0,0 +1,7 @@ +[template] +src = "blazegraph.properties.tmpl" +dest = "/var/run/islandora/blazegraph.properties" +uid = 0 +gid = 0 +mode = "0700" +keys = [ "/" ] diff --git a/sandbox/rootfs/etc/confd/conf.d/inference.nt.toml b/sandbox/rootfs/etc/confd/conf.d/inference.nt.toml new file mode 100644 index 00000000..966edce8 --- /dev/null +++ b/sandbox/rootfs/etc/confd/conf.d/inference.nt.toml @@ -0,0 +1,7 @@ +[template] +src = "inference.nt.tmpl" +dest = "/var/run/islandora/inference.nt" +uid = 0 +gid = 0 +mode = "0700" +keys = [ "/" ] diff --git a/sandbox/rootfs/etc/confd/conf.d/sandbox-setup.sh.toml b/sandbox/rootfs/etc/confd/conf.d/sandbox-setup.sh.toml new file mode 100644 index 00000000..f677eb46 --- /dev/null +++ b/sandbox/rootfs/etc/confd/conf.d/sandbox-setup.sh.toml @@ -0,0 +1,7 @@ +[template] +src = "sandbox-setup.sh.tmpl" +dest = "/var/run/islandora/sandbox-setup.sh" +uid = 0 +gid = 0 +mode = "0700" +keys = [ "/" ] diff --git a/sandbox/rootfs/etc/confd/confd.toml b/sandbox/rootfs/etc/confd/confd.toml new file mode 100644 index 00000000..925d1a48 --- /dev/null +++ b/sandbox/rootfs/etc/confd/confd.toml @@ -0,0 +1,6 @@ +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/drupal" diff --git a/sandbox/rootfs/etc/confd/templates/blazegraph.properties.tmpl b/sandbox/rootfs/etc/confd/templates/blazegraph.properties.tmpl new file mode 100644 index 00000000..c8a6bba3 --- /dev/null +++ b/sandbox/rootfs/etc/confd/templates/blazegraph.properties.tmpl @@ -0,0 +1,12 @@ +com.bigdata.rdf.store.AbstractTripleStore.textIndex=false +com.bigdata.rdf.store.AbstractTripleStore.axiomsClass=com.bigdata.rdf.axioms.OwlAxioms +com.bigdata.rdf.sail.isolatableIndices=false +com.bigdata.rdf.store.AbstractTripleStore.justify=true +com.bigdata.rdf.sail.truthMaintenance=true +com.bigdata.rdf.sail.namespace=islandora +com.bigdata.rdf.store.AbstractTripleStore.quads=false +com.bigdata.namespace.islandora.lex.com.bigdata.btree.BTree.branchingFactor=400 +com.bigdata.journal.Journal.groupCommit=false +com.bigdata.namespace.islandora.spo.com.bigdata.btree.BTree.branchingFactor=1024 +com.bigdata.rdf.store.AbstractTripleStore.geoSpatial=false +com.bigdata.rdf.store.AbstractTripleStore.statementIdentifiers=false diff --git a/sandbox/rootfs/etc/confd/templates/inference.nt.tmpl b/sandbox/rootfs/etc/confd/templates/inference.nt.tmpl new file mode 100644 index 00000000..c2629934 --- /dev/null +++ b/sandbox/rootfs/etc/confd/templates/inference.nt.tmpl @@ -0,0 +1,2 @@ + . + . \ No newline at end of file diff --git a/sandbox/rootfs/etc/confd/templates/sandbox-setup.sh.tmpl b/sandbox/rootfs/etc/confd/templates/sandbox-setup.sh.tmpl new file mode 100644 index 00000000..d8bfa58c --- /dev/null +++ b/sandbox/rootfs/etc/confd/templates/sandbox-setup.sh.tmpl @@ -0,0 +1,178 @@ +#!/usr/bin/with-contenv bash +set -ex + +function drush { + s6-setuidgid nginx php -d memory_limit=-1 /usr/local/bin/drush ${@} +} + +function main { + local broker_host="{{ getv "/broker/host" "activemq" }}" + local broker_port="{{ getv "/broker/port" "61613" }}" + local broker_url="tcp://${broker_host}:${broker_port}" + local gemini_host="{{ getv "/gemini/host" "gemini" }}" + local gemini_port="{{ getv "/gemini/port" "8000" }}" + local gemini_url="http://${gemini_host}:${gemini_port}" + local solr_host="{{ getv "/solr/host" "solr" }}" + local solr_port="{{ getv "/solr/port" "8983" }}" + local matamo_url="{{ getv "/matomo/url" "http://matomo.localhost/" }}" + local cantaloupe_url="{{ getv "/cantaloupe/url" "http://cantaloupe/cantaloupe/iiif/2" }}" + local triplestore_host="{{ getv "/triplestore/host" "blazegraph" }}" + local triplestore_port="{{ getv "/triplestore/port" "80" }}" + local triplestore_url="http://${triplestore_host}:${triplestore_port}/bigdata" + local fcrepo_host="{{ getv "/fcrepo/host" "fcrepo.localhost" }}" + local fcrepo_port="{{ getv "/fcrepo/host" "80" }}" + local fcrepo_url= + + # Indexing fails if port 80 is given explicitly. + if [[ "${fcrepo_port}" == "80" ]]; then + fcrepo_url="http://${fcrepo_host}/fcrepo/rest/" + else + fcrepo_url="http://${fcrepo_host}:${fcrepo_port}/fcrepo/rest/" + fi + + # Create keys prior to any installation as other services depend on them. + if [[ ! -f "/opt/keys/claw/private.key" ]]; then + openssl genrsa -out "/opt/keys/claw/private.key" 2048 + openssl rsa -pubout -in "/opt/keys/claw/private.key" -out "/opt/keys/claw/public.key" + chmod a=r /opt/keys/claw/* + fi + + # Hackity hack sack. + if ! grep flysystem /var/www/drupal/web/sites/default/settings.php &> /dev/null; then + cat <<-EOT >> /var/www/drupal/web/sites/default/settings.php + +\$settings['flysystem'] = [ + 'fedora' => [ + 'driver' => 'fedora', + 'config' => [ + 'root' => '${fcrepo_url}', + ], + ], +]; +EOT + fi + + # Ensure JWT is setup before any module installations run. + drush en --uri "http://drupal:80/" --no-interaction --yes jwt + drush config-import -y --partial --source=/opt/islandora/configs/jwt + + # Ensure Islandora settings are correct, before install hooks run. + drush en --uri "http://drupal:80/" --no-interaction --yes islandora + drush -y cset --input-format=yaml islandora.settings broker_url "${broker_url}" + drush -y cset --input-format=yaml islandora.settings gemini_url "${gemini_url}" + + # Need access to Solr before we can actually import the right config. + if timeout 60 wait-for-open-port.sh "${solr_host}" "${solr_port}" ; then + echo "Solr Found" + else + echo "Could not connect to Solr" + exit 1 + fi + + # Need access to Solr before we can actually import the right config. + if timeout 60 wait-for-open-port.sh "${fcrepo_host}" "${fcrepo_port}" ; then + echo "Fcrepo Found" + else + echo "Could not connect to Fcrepo" + exit 1 + fi + + # Need access to activemq so we can publish the creation of before we can actually import the right config. + if timeout 60 wait-for-open-port.sh ${broker_host} ${broker_port}; then + echo "Broker Found" + else + echo "Could not connect to Broker" + exit 1 + fi + + # Need access to gemini before importing features. + if timeout 60 wait-for-open-port.sh "${gemini_host}" "${gemini_port}"; then + echo "Gemini Found" + else + echo "Could not connect to Gemini" + exit 1 + fi + + if timeout 60 wait-for-open-port.sh "${triplestore_host}" "${triplestore_port}"; then + echo "Triplestore Found" + else + echo "Could not connect to Triplestore" + exit 1 + fi + + # Setup namespace / inference. + curl -X POST -H "Content-type: text/plain" --data-binary @/var/run/islandora/blazegraph.properties "${triplestore_url}/namespace" + curl -X POST -H "Content-type: text/plain" --data-binary @/var/run/islandora/inference.nt "${triplestore_url}/namespace/islandora/sparql" + + # From: islandora-playbook/inventory/vagrant/group_vars/webserver/drupal.yml + drush en --uri "http://drupal:80/" --no-interaction --yes \ + admin_toolbar \ + basic_auth \ + content_browser \ + controlled_access_terms_defaults \ + devel \ + facets \ + islandora_breadcrumbs \ + islandora_defaults \ + islandora_fits \ + islandora_iiif \ + islandora_oaipmh \ + islandora_search \ + matomo \ + pdf \ + rdf \ + responsive_image \ + rest \ + restui \ + search_api_solr \ + search_api_solr_defaults \ + serialization \ + simpletest \ + syslog \ + transliterate_filenames + + # search_api.index.default_solr_index? + drush -y cset search_api.server.default_solr_server backend_config.connector_config.host "${solr_host}" + drush -y cset search_api.server.default_solr_server backend_config.connector_config.port "${solr_port}" + + # From: islandora-playbook/roles/internal/webserver-app/tasks/drupal.yml + drush -y pm-uninstall search + drush -y "then" -y carapace + drush -y config-set system.theme default carapace + drush -y config-set matomo.settings site_id 1 + drush -y config-set matomo.settings url_http "${matamo_url}" + drush fim --uri "http://drupal:80/" --no-interaction --yes islandora_core_feature,controlled_access_terms_defaults,islandora_defaults,islandora_search + + # Fix what the features overides. + drush -y cset search_api.server.default_solr_server backend_config.connector_config.host "${solr_host}" + drush -y cset search_api.server.default_solr_server backend_config.connector_config.port "${solr_port}" + + # Export the configuration into shared volume. + drush -y solr-gsc default_solr_server /tmp/solr_config.zip 7.1 + mkdir -p /opt/solr/server/solr/ISLANDORA/conf || true + mkdir -p /opt/solr/server/solr/ISLANDORA/data || true + unzip -o /tmp/solr_config.zip -d /opt/solr/server/solr/ISLANDORA/conf + # The uid:gid "100:1000" is "solr:solr" inside of the solr container. + chown -R 100:1000 /opt/solr/server/solr/ISLANDORA + curl -s "http://${solr_host}:${solr_port}/solr/admin/cores?action=CREATE&name=ISLANDORA&instanceDir=ISLANDORA&config=solrconfig.xml&dataDir=data" &> /dev/null + + # From: islandora-playbook/post-install.yml + drush -y urol fedoraadmin admin + drush -y cset --input-format=yaml jsonld.settings remove_jsonld_format true + drush -y cset --input-format=yaml islandora.settings broker_url "${broker_url}" + drush -y cset --input-format=yaml islandora.settings gemini_url "${gemini_url}" + drush -y cset --input-format=yaml islandora.settings gemini_pseudo_bundles.0 "islandora_object:node" + drush -y cset --input-format=yaml islandora.settings gemini_pseudo_bundles.1 "image:media" + drush -y cset --input-format=yaml islandora.settings gemini_pseudo_bundles.2 "file:media" + drush -y cset --input-format=yaml islandora.settings gemini_pseudo_bundles.3 "audio:media" + drush -y cset --input-format=yaml islandora.settings gemini_pseudo_bundles.4 "video:media" + drush -y cset --input-format=yaml media.settings standalone_url true + drush -y cset --input-format=yaml islandora_iiif.settings iiif_server "${cantaloupe_url}" + drush -y cset --input-format=yaml openseadragon.settings manifest_view iiif_manifest + drush -y --uri "http://drupal:80/" --userid=1 mim --group=islandora + s6-setuidgid nginx mkdir -p /var/www/drupal/web/simpletest /var/www/drupal/web/sites/simpletest + + # Cache clear. + drush -y cr +} +main diff --git a/sandbox/rootfs/etc/cont-init.d/04-sandbox-setup.sh b/sandbox/rootfs/etc/cont-init.d/04-sandbox-setup.sh new file mode 100644 index 00000000..fda8eefa --- /dev/null +++ b/sandbox/rootfs/etc/cont-init.d/04-sandbox-setup.sh @@ -0,0 +1,3 @@ +#!/usr/bin/with-contenv bash +set -e +/var/run/islandora/sandbox-setup.sh \ No newline at end of file diff --git a/sandbox/rootfs/opt/islandora/configs/jwt/jwt.config.yml b/sandbox/rootfs/opt/islandora/configs/jwt/jwt.config.yml new file mode 100644 index 00000000..49199f09 --- /dev/null +++ b/sandbox/rootfs/opt/islandora/configs/jwt/jwt.config.yml @@ -0,0 +1,2 @@ +algorithm: RS256 +key_id: islandora_rsa_key diff --git a/sandbox/rootfs/opt/islandora/configs/jwt/key.key.islandora_rsa_key.yml b/sandbox/rootfs/opt/islandora/configs/jwt/key.key.islandora_rsa_key.yml new file mode 100644 index 00000000..9140f3fb --- /dev/null +++ b/sandbox/rootfs/opt/islandora/configs/jwt/key.key.islandora_rsa_key.yml @@ -0,0 +1,17 @@ +uuid: 7f805322-14df-4eac-bfa1-d2f800fccbe3 +langcode: en +status: true +dependencies: + module: + - jwt +id: islandora_rsa_key +label: 'Islandora RSA Key' +description: '' +key_type: jwt_rs +key_type_settings: + algorithm: RS256 +key_provider: file +key_provider_settings: + file_location: /opt/keys/claw/private.key +key_input: none +key_input_settings: { } diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..1fe4034e --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,11 @@ +rootProject.name = "docker" + +// Include any folder that has a Dockerfile as a sub-project. +rootProject.projectDir + .walk() + .maxDepth(1) // Only immediate directories. + .filter { it.isDirectory && it.resolve("Dockerfile").exists() } // Must have a Dockerfile. + .forEach { + // Include as a sub-project. + include(it.relativeTo(rootProject.projectDir).path) + } \ No newline at end of file diff --git a/solr/.dockerignore b/solr/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/solr/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/solr/Dockerfile b/solr/Dockerfile new file mode 100644 index 00000000..3d2cc3c1 --- /dev/null +++ b/solr/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/java:latest + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + SOLR_VERSION="7.1.0" && \ + install-apache-service.sh \ + --name solr \ + --version "${SOLR_VERSION}" \ + --key "38D2EA16DDF5FC722EBC433FDC92616F177050F6" \ + --mirror "https://archive.apache.org/dist/lucene/solr/${SOLR_VERSION}" \ + --file "solr-${SOLR_VERSION}.tgz" \ + docs example licenses server/solr/configsets + +WORKDIR /opt/solr + +EXPOSE 8983 + +VOLUME [ "/opt/solr/server/solr" ] + +COPY rootfs / diff --git a/solr/README.md b/solr/README.md new file mode 100644 index 00000000..e64cb57a --- /dev/null +++ b/solr/README.md @@ -0,0 +1,40 @@ +# Solr + +Docker image for [Solr] version 7.1.0. + +Please refer to the [Solr Documentation] for more in-depth information. + +As a quick example this will bring up an instance of [Solr], and allow you +to view on . + +```bash +docker run --rm -ti -p 8983:8983 islandora/solr +``` + +## Dependencies + +Requires `islandora/java` docker image to build. Please refer to the +[Java Image README](../java/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Ports + +| Port | Description | +| :--- | :---------- | +| 8983 | HTTP | + +## Volumes + +| Path | Description | +| :-------------------- | :----------------------------------------------- | +| /opt/solr/server/solr | Location of configuration and data for all cores | + +## Logs + +| Path | Description | +| :----------------------------- | :------------- | +| /opt/solr/server/logs/solr.log | [Solr Logging] | + +[Solr Documentation]: https://lucene.apache.org/solr/guide/7_1/ +[Solr Logging]: https://lucene.apache.org/solr/guide/7_1/configuring-logging.html +[Solr]: https://lucene.apache.org/solr/ diff --git a/solr/rootfs/etc/services.d/solr/finish b/solr/rootfs/etc/services.d/solr/finish new file mode 100644 index 00000000..f8984dd3 --- /dev/null +++ b/solr/rootfs/etc/services.d/solr/finish @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -S1 +# -*- mode: sh -*- +# vi: set ft=sh : +s6-svscanctl -t /var/run/s6/services diff --git a/solr/rootfs/etc/services.d/solr/run b/solr/rootfs/etc/services.d/solr/run new file mode 100644 index 00000000..dc64b621 --- /dev/null +++ b/solr/rootfs/etc/services.d/solr/run @@ -0,0 +1,5 @@ +#!/usr/bin/execlineb -P +# -*- mode: sh -*- +# vi: set ft=sh : +with-contenv +s6-setuidgid solr /opt/solr/bin/solr start -f diff --git a/tomcat/.dockerignore b/tomcat/.dockerignore new file mode 100644 index 00000000..b43bf86b --- /dev/null +++ b/tomcat/.dockerignore @@ -0,0 +1 @@ +README.md diff --git a/tomcat/Dockerfile b/tomcat/Dockerfile new file mode 100644 index 00000000..4cd17e18 --- /dev/null +++ b/tomcat/Dockerfile @@ -0,0 +1,24 @@ +# syntax=docker/dockerfile:experimental +FROM islandora/java:latest + +RUN --mount=id=downloads,type=cache,target=/opt/downloads \ + TOMCAT_VERSION="9.0.34" && \ + install-apache-service.sh \ + --name tomcat \ + --version "${TOMCAT_VERSION}" \ + --key "A9C5DF4D22E99998D9875A5110C01C5A2F6059E7" \ + --mirror "https://archive.apache.org/dist/tomcat/tomcat-9/v${TOMCAT_VERSION}/bin" \ + --file "apache-tomcat-${TOMCAT_VERSION}.tar.gz" \ + webapps/docs webapps/examples + +# Install reverse proxy to redirect from 80 to 8080. +RUN --mount=type=cache,target=/var/cache/apk \ + --mount=type=cache,target=/etc/cache/apk \ + apk-install.sh nginx && \ + cleanup.sh + +WORKDIR /opt/tomcat + +EXPOSE 8080 + +COPY rootfs / diff --git a/tomcat/README.md b/tomcat/README.md new file mode 100644 index 00000000..af0059f1 --- /dev/null +++ b/tomcat/README.md @@ -0,0 +1,75 @@ +# Tomcat + +Docker image for [Tomcat] version 9.0.34. + +Please refer to the [Tomcat Documentation] for more in-depth information. + +As a quick example this will bring up an instance of [Tomcat], and allow you +to view the manager webapp on . + +```bash +docker run --rm -ti \ + -p 80:80 \ + islandora/tomcat +``` + +## Dependencies + +Requires `islandora/java` docker image to build. Please refer to the +[Java Image README](../java/README.md) for additional information including +additional settings, volumes, ports, etc. + +## Ports + +| Port | Description | +| :--- | :---------- | +| 8005 | Shut-down | +| 8009 | [AJP] | +| 8080 | HTTP | +| 8443 | HTTPS | + +## Settings + +> N.B. For all of the settings below images that descend from +> ``islandora/tomcat`` will apply prefix to every setting. So for example +> `CATALINA_OPTS` would become `FCREPO_CATALINA_OPTS` this is to allow for +> different settings on a per-service basis. + +| Environment Variable | Etcd Key | Default | Description | +| :---------------------------------- | :----------------------------------- | :---------- | :------------------------------------------------------------------------ | +| CATALINA_OPTS | /catalina/opts | | | +| JAVA_OPTS | /java/opts | | | +| TOMCAT_ADMIN_NAME | /tomcat/admin/name | admin | The user name of the manager webapp admin user | +| TOMCAT_ADMIN_PASSWORD | /tomcat/admin/password | password | The password for the manager webapp admin user | +| TOMCAT_ADMIN_ROLES | /tomcat/admin/roles | manager-gui | Comma separated list of roles the user has | +| TOMCAT_MANAGER_REMOTE_ADDRESS_VALVE | /tomcat/manager/remote/address/valve | ^.*$ | Allows / blocks access to manager app to addresses which match this regex | + +Additional users/groups/etc can be defined by adding more environment variables, +following the above conventions: + +| Environment Variable | Etcd Key | Description | +| :-------------------------- | :--------------------------- | :----------------------------------------- | +| TOMCAT_USER_{USER}_NAME | /tomcat/user/{USER}/name | The user name | +| TOMCAT_USER_{USER}_PASSWORD | /tomcat/user/{USER}/password | The password for the user | +| TOMCAT_USER_{USER}_ROLES | /tomcat/user/{USER}/roles | Comma separated list of roles the user has | + +> N.B. These do not have defaults. + +For example to add a new user `someone` you would need to define the following: + +| Environment Variable | Etcd Key | Value | +| :--------------------------- | :---------------------------- | :------- | +| TOMCAT_USER_SOMEONE_NAME | /tomcat/user/someone/name | someone | +| TOMCAT_USER_SOMEONE_PASSWORD | /tomcat/user/someone/password | password | +| TOMCAT_USER_SOMEONE_ROLES | /tomcat/user/someone/roles | admin | + +## Logs + +| Path | Description | +| :---------------- | :--------------- | +| /opt/tomcat/logs/ | [Tomcat Logging] | + +[AJP]: https://tomcat.apache.org/tomcat-9.0-doc/config/ajp.html +[Tomcat Documentation]: https://tomcat.apache.org/tomcat-9.0-doc/ +[Tomcat Logging]: https://tomcat.apache.org/tomcat-9.0-doc/logging.html +[Tomcat]: https://tomcat.apache.org/ diff --git a/tomcat/rootfs/etc/confd/conf.d/manager.context.toml b/tomcat/rootfs/etc/confd/conf.d/manager.context.toml new file mode 100644 index 00000000..0480ca66 --- /dev/null +++ b/tomcat/rootfs/etc/confd/conf.d/manager.context.toml @@ -0,0 +1,6 @@ +[template] +src = "manager.context.xml.tmpl" +dest = "/opt/tomcat/webapps/manager/META-INF/context.xml" +uid = 100 +gid = 1000 +keys = ["/tomcat/manager/remote/address/valve"] diff --git a/tomcat/rootfs/etc/confd/conf.d/setenv.sh.toml b/tomcat/rootfs/etc/confd/conf.d/setenv.sh.toml new file mode 100644 index 00000000..7f0fbd2f --- /dev/null +++ b/tomcat/rootfs/etc/confd/conf.d/setenv.sh.toml @@ -0,0 +1,6 @@ +[template] +src = "setenv.sh.tmpl" +dest = "/opt/tomcat/bin/setenv.sh" +uid = 100 +gid = 1000 +keys = ["/java/opts", "/catalina/opts"] diff --git a/tomcat/rootfs/etc/confd/conf.d/tomcat-users.toml b/tomcat/rootfs/etc/confd/conf.d/tomcat-users.toml new file mode 100644 index 00000000..196ecdde --- /dev/null +++ b/tomcat/rootfs/etc/confd/conf.d/tomcat-users.toml @@ -0,0 +1,6 @@ +[template] +src = "tomcat-users.xml.tmpl" +dest = "/opt/tomcat/conf/tomcat-users.xml" +uid = 100 +gid = 1000 +keys = ["/tomcat/admin/user", "/tomcat/admin/password"] diff --git a/tomcat/rootfs/etc/confd/templates/manager.context.xml.tmpl b/tomcat/rootfs/etc/confd/templates/manager.context.xml.tmpl new file mode 100644 index 00000000..ab46261c --- /dev/null +++ b/tomcat/rootfs/etc/confd/templates/manager.context.xml.tmpl @@ -0,0 +1,23 @@ + + + + + + + diff --git a/tomcat/rootfs/etc/confd/templates/setenv.sh.tmpl b/tomcat/rootfs/etc/confd/templates/setenv.sh.tmpl new file mode 100755 index 00000000..789681e5 --- /dev/null +++ b/tomcat/rootfs/etc/confd/templates/setenv.sh.tmpl @@ -0,0 +1,3 @@ +#!/bin/sh +export JAVA_OPTS="{{ getv "/java/opts" "" }}" +export CATALINA_OPTS="{{ getv "/catalina/opts" "" }}" diff --git a/tomcat/rootfs/etc/confd/templates/tomcat-users.xml.tmpl b/tomcat/rootfs/etc/confd/templates/tomcat-users.xml.tmpl new file mode 100644 index 00000000..7e1c42cc --- /dev/null +++ b/tomcat/rootfs/etc/confd/templates/tomcat-users.xml.tmpl @@ -0,0 +1,9 @@ + + + + {{ range $dir := lsdir "/user" }} + {{ end }} + diff --git a/tomcat/rootfs/etc/cont-init.d/02-ngnix-install.sh b/tomcat/rootfs/etc/cont-init.d/02-ngnix-install.sh new file mode 100755 index 00000000..c7d12cc6 --- /dev/null +++ b/tomcat/rootfs/etc/cont-init.d/02-ngnix-install.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +mkdir /run/nginx &> /dev/null || true diff --git a/tomcat/rootfs/etc/nginx/conf.d/default.conf b/tomcat/rootfs/etc/nginx/conf.d/default.conf new file mode 100644 index 00000000..965977b2 --- /dev/null +++ b/tomcat/rootfs/etc/nginx/conf.d/default.conf @@ -0,0 +1,8 @@ +server { + listen 80; + location / { + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header Host $http_host; + proxy_pass "http://127.0.0.1:8080"; + } +} diff --git a/tomcat/rootfs/etc/nginx/modules/daemon.conf b/tomcat/rootfs/etc/nginx/modules/daemon.conf new file mode 100644 index 00000000..ef1f9c1c --- /dev/null +++ b/tomcat/rootfs/etc/nginx/modules/daemon.conf @@ -0,0 +1 @@ +daemon off; diff --git a/tomcat/rootfs/etc/services.d/nginx/finish b/tomcat/rootfs/etc/services.d/nginx/finish new file mode 100644 index 00000000..9030da41 --- /dev/null +++ b/tomcat/rootfs/etc/services.d/nginx/finish @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -S1 +# -*- mode: sh -*- +# vi: set ft=sh: +s6-svscanctl -t /var/run/s6/services diff --git a/tomcat/rootfs/etc/services.d/nginx/run b/tomcat/rootfs/etc/services.d/nginx/run new file mode 100644 index 00000000..7503678e --- /dev/null +++ b/tomcat/rootfs/etc/services.d/nginx/run @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -P +# -*- mode: sh -*- +# vi: set ft=sh: +/usr/sbin/nginx diff --git a/tomcat/rootfs/etc/services.d/tomcat/finish b/tomcat/rootfs/etc/services.d/tomcat/finish new file mode 100644 index 00000000..f8984dd3 --- /dev/null +++ b/tomcat/rootfs/etc/services.d/tomcat/finish @@ -0,0 +1,4 @@ +#!/usr/bin/execlineb -S1 +# -*- mode: sh -*- +# vi: set ft=sh : +s6-svscanctl -t /var/run/s6/services diff --git a/tomcat/rootfs/etc/services.d/tomcat/run b/tomcat/rootfs/etc/services.d/tomcat/run new file mode 100644 index 00000000..b66a361b --- /dev/null +++ b/tomcat/rootfs/etc/services.d/tomcat/run @@ -0,0 +1,6 @@ +#!/usr/bin/execlineb -P +# -*- mode: sh -*- +# vi: set ft=sh : +with-contenv +s6-setuidgid tomcat +/opt/tomcat/bin/catalina.sh run diff --git a/tomcat/rootfs/usr/local/bin/install-war-into-tomcat.sh b/tomcat/rootfs/usr/local/bin/install-war-into-tomcat.sh new file mode 100755 index 00000000..616c7e16 --- /dev/null +++ b/tomcat/rootfs/usr/local/bin/install-war-into-tomcat.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash + +# Exit non-zero if any command fails. +set -e + +readonly PROGNAME=$(basename $0) +readonly ARGS="$@" + +readonly DOWNLOAD_CACHE_DIRECTORY=/opt/downloads + +function usage { + cat <<- EOF + usage: $PROGNAME options [FILE]... + + Installs the given war into tomcat. Makes use of the Buildkit cache, + by first downlading to ${DOWNLOAD_CACHE_DIRECTORY}. + + OPTIONS: + -n --name The name to use for the unpacked war. + -u --url The location to download the war from. + -u --file The location to copy the war from. + -k --key A sha256 key used to verify the intended war was downloaded successfully. + -h --help Show this help. + -x --debug Debug this script. + + The options --file and --url are mutually exclusive. + + Examples: + Install Blazegraph as bigdata: + $PROGNAME \\ + --name "bigdata" \\ + --url "https://github.com/blazegraph/database/releases/download/BLAZEGRAPH_RELEASE_CANDIDATE_2_1_5/blazegraph.war" \\ + --key "b22f1a1aa8e536443db9a57da63720813374ef59e4021cfa9ad0e98f9a420e85" +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --name) args="${args}-n ";; + --url) args="${args}-u ";; + --file) args="${args}-f ";; + --key) args="${args}-k ";; + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + while getopts "n:u:f:k:hx" OPTION + do + case $OPTION in + n) + readonly NAME=${OPTARG} + readonly DEPLOY_DIRECTORY="/opt/tomcat/webapps/${NAME}" + ;; + u) + readonly URL=${OPTARG} + ;; + f) + readonly FILE=${OPTARG} + ;; + k) + readonly KEY=${OPTARG} + ;; + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + if [[ -z $NAME || -z $KEY ]]; then + echo "Missing one or more required options: --name --key" + exit 1 + fi + + if [[ -z $URL && -z $FILE ]]; then + echo "Missing required options you must specify either: --url OR --file" + exit 1 + fi + + return 0 +} + +function validate { + local war="${1}" + sha256sum "${war}" | cut -f1 -d' ' | xargs test "${KEY}" == +} + +function unpack { + local war="${1}" + s6-setuidgid tomcat mkdir "${DEPLOY_DIRECTORY}" + s6-setuidgid tomcat unzip "${war}" -d "${DEPLOY_DIRECTORY}" +} + +function main { + cmdline ${ARGS} + local war=${FILE} + if [[ $URL ]]; then + war="/opt/downloads/$(basename ${URL})" + # Expects that the RUN uses ${DOWNLOAD_CACHE_DIRECTORY} as a cache + # https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md#run---mounttypecache + wget -N -P ${DOWNLOAD_CACHE_DIRECTORY} ${URL} + fi + validate "${war}" + unpack "${war}" +} +main From cfdfc23bf43d2f6980f93a90326425b4aac3c131 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Thu, 14 May 2020 17:18:18 +0100 Subject: [PATCH 02/10] Missing files from previous commit. --- base/rootfs/etc/services.d/.gitignore | 2 -- base/rootfs/etc/services.d/confd/finish | 2 ++ base/rootfs/etc/services.d/confd/run | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) delete mode 100644 base/rootfs/etc/services.d/.gitignore create mode 100644 base/rootfs/etc/services.d/confd/finish create mode 100644 base/rootfs/etc/services.d/confd/run diff --git a/base/rootfs/etc/services.d/.gitignore b/base/rootfs/etc/services.d/.gitignore deleted file mode 100644 index d6b7ef32..00000000 --- a/base/rootfs/etc/services.d/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/base/rootfs/etc/services.d/confd/finish b/base/rootfs/etc/services.d/confd/finish new file mode 100644 index 00000000..b8f06413 --- /dev/null +++ b/base/rootfs/etc/services.d/confd/finish @@ -0,0 +1,2 @@ +#!/usr/bin/with-contenv bash +s6-svscanctl -t /var/run/s6/services \ No newline at end of file diff --git a/base/rootfs/etc/services.d/confd/run b/base/rootfs/etc/services.d/confd/run new file mode 100644 index 00000000..70bd8c0d --- /dev/null +++ b/base/rootfs/etc/services.d/confd/run @@ -0,0 +1,17 @@ +#!/usr/bin/with-contenv bash +set -e + +readonly ETCD_HOST=${ETCD_HOST:-etcd} +readonly ETCD_HOST_PORT=${ETCD_HOST_PORT:-2379} +readonly ETCD_CONNECTION_TIMEOUT=${ETCD_CONNECTION_TIMEOUT:-0} +readonly CONFD_LOG_LEVEL=${CONFD_LOG_LEVEL:-error} +readonly CONFD_POLLING_INTERVAL=${CONFD_POLLING_INTERVAL:-5} + +echo "Looking for etcd server... http://${ETCD_HOST}:${ETCD_HOST_PORT}" +if timeout ${ETCD_CONNECTION_TIMEOUT} wait-for-open-port.sh ${ETCD_HOST} ${ETCD_HOST_PORT} &> /dev/null; then + echo "Found etcd server..." + confd -log-level ${CONFD_LOG_LEVEL} -interval ${CONFD_POLLING_INTERVAL} -backend etcdv3 -node ${ETCD_HOST}:${ETCD_HOST_PORT} +else + echo "Timeout exceeded using env backend..." + confd -log-level ${CONFD_LOG_LEVEL} -interval ${CONFD_POLLING_INTERVAL} -backend env +fi \ No newline at end of file From 11bb5dd5743056f92aba57b5287dda683a3923c3 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Thu, 14 May 2020 17:27:04 +0100 Subject: [PATCH 03/10] Add missing volumes. --- docker-compose.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 0c465283..bacde384 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,9 +31,13 @@ networks: default: internal: true volumes: + activemq-data: + blazegraph-data: drupal-config-data: drupal-sites-data: + cantaloupe-data: etcd-data: + fcrepo-data: jwt-data: jwt-public-data: karaf-data: @@ -70,6 +74,8 @@ services: command: --interval 1 --no-pull activemq: image: islandora/activemq:latest + volumes: + - activemq-data:/opt/activemq/data labels: # Do not expose in production. - traefik.http.services.activemq.loadbalancer.server.port=8161 @@ -83,6 +89,8 @@ services: - activemq blazegraph: image: islandora/blazegraph:latest + volumes: + - blazegraph-data:/data networks: default: aliases: @@ -93,6 +101,8 @@ services: - traefik.http.routers.blazegraph_http.entrypoints=http cantaloupe: image: islandora/cantaloupe:latest + volumes: + - cantaloupe-data:/data crayfits: image: islandora/crayfits:latest depends_on: @@ -129,6 +139,7 @@ services: fcrepo: image: islandora/fcrepo:latest volumes: + - fcrepo-data:/data - jwt-data:/opt/keys/claw/ depends_on: - activemq From 8230c194354eb59a4a7dc44296fcf296e8357358 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Thu, 14 May 2020 17:54:26 +0100 Subject: [PATCH 04/10] Add missing route to cantaloupe. --- docker-compose.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index bacde384..04bcf2ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -103,6 +103,14 @@ services: image: islandora/cantaloupe:latest volumes: - cantaloupe-data:/data + networks: + default: + aliases: + - cantaloupe.localhost + labels: + - traefik.http.services.cantaloupe.loadbalancer.server.port=80 + - traefik.http.routers.cantaloupe_http.service=cantaloupe + - traefik.http.routers.cantaloupe_http.entrypoints=http crayfits: image: islandora/crayfits:latest depends_on: From b2c10fc6b1d67693b49c8076ffb063499cae3ef4 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Tue, 19 May 2020 13:29:03 +0100 Subject: [PATCH 05/10] Changed cache layer of composer to avoid deadlocks, and added documentation. --- README.md | 594 ++++++++++++++++++++++++++++++++++++++++- commands/continuous.sh | 129 --------- commands/drush.sh | 69 ++++- commands/etcdctl.sh | 78 +++++- commands/mysql.sh | 69 ++++- commands/shell.sh | 71 ++++- composer/Dockerfile | 2 +- crayfits/Dockerfile | 2 +- docker-compose.yml | 2 +- gemini/Dockerfile | 2 +- homarus/Dockerfile | 2 +- houdini/Dockerfile | 2 +- hypercube/Dockerfile | 2 +- milliner/Dockerfile | 2 +- recast/Dockerfile | 2 +- sandbox/Dockerfile | 2 +- 16 files changed, 883 insertions(+), 147 deletions(-) delete mode 100755 commands/continuous.sh diff --git a/README.md b/README.md index 0ebca8a1..9c9d4267 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,597 @@ -# ISLE: Docker Prototype +# ISLE: Docker Prototype [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](./LICENSE) +- [Introduction](#introduction) +- [Requirements](#requirements) +- [Building](#building) + - [Build All Images](#build-all-images) + - [Build Specific Image](#build-specific-image) + - [Building Continuously](#building-continuously) +- [Running](#running) +- [Scripts](#scripts) +- [Docker Images](#docker-images) +- [Docker Compose](#docker-compose) + - [Watchtower](#watchtower) + - [Traefik](#traefik) +- [ETCD](#etcd) +- [Customizing the Drupal Installation](#customizing-the-drupal-installation) +- [Design Considerations](#design-considerations) + - [Confd](#confd) + - [S6 Overlay](#s6-overlay) + - [Image Hierarchy](#image-hierarchy) + - [Folder Layout](#folder-layout) + - [Build System](#build-system) +- [Design Constraints](#design-constraints) +- [To Do](#to-do) + ## Introduction -Experiments with Docker and Islandora. +This repository provides a number of docker images and an example +[docker-compose.yml](./docker-compose.yml) for running the demo version of +Islandora. It is not yet in a production ready state. + +## Requirements + +To build the Docker images using the provided Gradle build scripts requires: + +- [Docker 18.09+](https://docs.docker.com/get-docker/) +- [OpenJDK or Oracle JDK 8+](https://www.java.com/en/download/) + +To run the Docker images with Docker Compose requires: + +- [Docker 18.06+](https://docs.docker.com/get-docker/) +- [Docker Compose 1.22+](https://docs.docker.com/compose/install/) + +That being said the images themselves are compatible with older versions of +Docker, if you require running on an older version you'll need to write your own +docker-compose file. + +## Building + +The build scripts rely on Gradle and should function equally well across +platforms. The only difference being the script you call to interact with gradle +(the following assumes you are executing from the **root directory** of the +project): + +**Linux or OSX:** + +```bash +./gradlew +``` + +**Windows:** + +```bash +gradlew.bat +``` + +For the remaining examples the **Linux or OSX** call method will be used, if +using Windows substitute the call to Gradle script. + +Gradle is a project/task based build system to query all the available tasks use +the following command. + +```bash +./gradlew tasks --all +``` + +Which should return something akin to: + +```bash +> Task :tasks + +------------------------------------------------------------ +Tasks runnable from root project +------------------------------------------------------------ + +... + +Islandora tasks +--------------- +abuild:build - Creates Docker image. +activemq:build - Creates Docker image. +alpaca:build - Creates Docker image. +base:build - Creates Docker image. + +... +``` + +In Gradle each Project maps onto a folder in the file system path where it is +delimited by ``:`` instead of ``/`` (Unix) or ``\`` (Windows). + +The root project ``:`` can be omitted. + +So if you want to run a particular task ``taskname`` that resided in the project +folder ``project/subproject`` you would specify it like so: + +```bash +./gradlew :project:subproject:taskname +``` + +To get more verbose output from Gradle use the ``--info`` argument like so: + +```bash +./gradlew :PROJECT:TASK --info +``` + +To build all the docker images you can use the following command: + +### Build All Images + +The following will build all the images in the correct order. + +```bash +./gradlew build +``` + +### Build Specific Image + +To build a specific image and it's dependencies, for example +``islandora/tomcat``, you can use the following: + +```bash +./gradlew tomcat:build +``` + +### Building Continuously + +It is often helpful to build continuously where-in any change you make to any of +the Dockerfiles or other project files, will automatically trigger the building +of that image and any downstream dependencies. To do this add the +``--continuous`` flag like so: + +```bash +./gradlew build --continuous +``` + +When this is combined with the use of ``watchtower`` and +``restart: unless-stopped`` in the [docker-compose.yml](./docker-compose.yml) +file. Images will be redeployed with the latest changes while you develop +automatically. See the [Docker Compose](#Docker-Compose) section of this +document for more details. + +## Running + +At the moment the example [docker-compose.yml] is the only orchestration +mechanism provided to launch all the containers, and have them work as a whole. + +To start the containers use the following command: + +```bash +docker-compose up -d +``` + +With [Docker Compose] there are many features such as displaying logs among +other things for which you can find detailed descriptions in the +[Docker Composer CLI Documentation](https://docs.docker.com/compose/reference/overview/) + +For more information on the structure and design of the example +[docker-compose.yml] file see the [Docker Compose](#Docker-Compose) section of +this document. + +## Scripts + +Some helper scripts are provided to make development and testing more pleasurable. + +- [./commands/drush.sh](./commands/drush.sh) - Wrapper around [drush] in the ``drupal service`` container. +- [./commands/etcdctrl.sh](./commands/etcdctrl.sh) - Wrapper around [etcdctrl] in the ``etcd service`` container. +- [./commands/mysql.sh](./commands/mysql.sh) - Wrapper around [mysql] client in the ``database service`` container. +- [./commands/open-in-browser.sh](./commands/shell.sh) - Attempts to open the given service in the users browser. +- [./commands/shell.sh](./commands/shell.sh) - Open ``ash`` shell in the given service container. + +All of the above commands include a usage statement, which can be accessed with ``-h`` flag like so: + +```bash +$ ./commands/shell.sh + usage: shell.sh SERVICE + + Opens an ash shell in the given SERVICE's container. + + OPTIONS: + -h --help Show this help. + -x --debug Debug this script. + + Examples: + shell.sh database +``` + +## Docker Images + +The following docker images are provided: + +- [abuild](./abuild/README.md) +- [activemq](./activemq/README.md) +- [alpaca](./alpaca/README.md) +- [base](./base/README.md) +- [blazegraph](./blazegraph/README.md) +- [build](./build/README.md) +- [cantaloupe](./cantaloupe/README.md) +- [composer](./composer/README.md) +- [crayfish](./crayfish/README.md) +- [crayfits](./crayfits/README.md) +- [drupal](./drupal/README.md) +- [fcrepo](./fcrepo/README.md) +- [fits](./fits/README.md) +- [gemini](./gemini/README.md) +- [homarus](./homarus/README.md) +- [houdini](./houdini/README.md) +- [hypercube](./hypercube/README.md) +- [imagemagick](./imagemagick/README.md) +- [java](./java/README.md) +- [karaf](./karaf/README.md) +- [mariadb](./mariadb/README.md) +- [matomo](./matomo/README.md) +- [milliner](./milliner/README.md) +- [nginx](./nginx/README.md) +- [recast](./recast/README.md) +- [sandbox](./sandbox/README.md) +- [solr](./solr/README.md) +- [tomcat](./tomcat/README.md) + +Many are intermediate images used to build other images in the list, for example +[java](./java/README.md). Please see the README of each image to find out what +settings, and ports, are exposed and what functionality it provides. + +## Docker Compose + +The example [docker-compose.yml] provided with this repository is a +template for those who wish to use [Docker Compose] for orchestration. The +images are not limited to running via [Docker Compose]. As time permits +additional tooling will be added to support [Kubernetes], and deploying to +[Amazon Elastic Container Service], and perhaps others. + +The example [docker-compose.yml] runs the [sandbox](./sandbox/README.md) version +of Islandora, for the purposes of testing the images, and Islandora. When +creating a ``docker-compose.yml`` for running a production Islandora 8 site. you +will use your own image. Please see +[Customizing the Drupal Installation](#customizing-the-drupal-installation) for +instructions on how to do so. + +In addition to the images provided as described in the section +[Docker Images](#docker-images). Several others are used by the +[docker-compose.yml] file. + +### Watchtower + +The [watchtower](https://hub.docker.com/r/v2tec/watchtower/) container monitors +the running Docker containers and watches for changes to the images that those +containers were originally started from. If watchtower detects that an image has +changed, it will automatically restart the container using the new image. This +allows for automatic deployment, and overall faster development time. + +Note however Watchtower will not restart stopped container or containers that +exited due to error. To ensure a container is always running, unless explicitly +stopped, add ``restart: unless-stopped`` property to the container in the +[docker-compose.yml] file. For example: + +```yaml +database: + image: islandora/mariadb:latest + restart: unless-stopped +``` + +### Traefik + +The [traefik](https://containo.us/traefik/) container acts as a reverse proxy, +and exposes some containers through port ``80`` on the localhost via the +[loopback](https://www.tldp.org/LDP/nag/node66.html). This allows access to the +following urls. + +- +- +- +- +- + +Note if you cannot map ``traefik`` to the hosts port 80, you will need to +manually modify your ``/etc/hosts`` file and add entries for each of the urls +above like so, assuming the IP of ``traefik`` container is ``x.x.x.x`` on its +virtual network, and you can access that address from your machine. + +```properties +x.x.x.x activemq.localhost +x.x.x.x blazegraph.localhost +x.x.x.x drupal.localhost +x.x.x.x fcrepo.localhost +x.x.x.x matomo.localhost +``` + +Since Drupal passes its ``Base URL`` along to other services in AS2 as a means +of allowing them to find their way back. As well as having services like Fedora +exposed at the same URL they are accessed by the micro-services to end users. We +need to allow containers within the network to be accessible via the same URL, +though not by routing through ``traefik`` since it is and edge router. + +So alias like the following are defined: + +```yaml +drupal: + image: islandora/sandbox:latest + # ... + networks: + default: + aliases: + - drupal.localhost +``` + +These are set on the ``default`` network as that is the internal network (no +access to the outside) on which all containers reside. + +## ETCD + +The [etcd](https://github.com/etcd-io/etcd) container is a distributed reliable +key-value store, which this project uses for configuration settings and secrets. +Chosen in particular for it's existing integration with +[Kubernetes](https://kubernetes.io/docs/concepts/overview/components/#etcd). + +Alternatively if removed from the [docker-compose.yml] file or explicitly not +started the containers will fall back to pulling configuration from +**environment variables**. + +A convenience script is provided that allows for users to put and get key/values +from the store after it has been started. For example changing the log level of +[houdini](./houdini/README.md) to ``DEBUG``. + +```bash +./commands/etcdctl.sh put /houdini/log/level DEBUG +``` + +Or checking what the current log level is set to (*if not set to the default, in +which case the key/value store is not used*): + +```bash +./commands/etcdctl.sh get /houdini/log/level +``` + +## Customizing the Drupal Installation + +This needs to be thought about more in-depth, and needs fleshing out. Ideally we +will provide the base image for building / deploying the Drupal image. End users +will consume these containers and provide their ``composer.json`` and +``composer.lock`` files, along with the name of the +[Drupal Installation Profile] (either the one we will provide or one they create +on their own). At the moment the [composer](./composer/README.md) project is +provided as an early example of this. Where it user the +``islandora/nginx as compose`` image to perform do the composer installation, +and ``islandora/drupal`` as the container to run the installation created in the +previous step. + +Additionally more documentation is needed to describe how developers can use the +existing images for local development allowing them to create their own +``composer.json`` and ``composer.lock`` files as well as any custom modules, +themes, etc. + +## Design Considerations + +All of the images build by this project are derived from the +[Alpine Docker Image](https://hub.docker.com/_/alpine) which is a Linux +distribution built around ``musl`` ``libc`` and ``BusyBox``. The image is only 5 +MB in size and has access to a package repository. It has been chosen for its +small size, and ease of generating custom packages (as is done in the +[imagemagick](./imagemagick/README.md) image). + +The [base](./base/README.md) image includes two tools essential to the +functioning of all the images. + +- [Confd](https://github.com/kelseyhightower/confd) - Configuration Management +- [S6 Overlay](https://github.com/just-containers/s6-overlay) - Process Manager / Initialization system. + +### Confd + +``confd`` is used for all Configuration Management, it is how images are +customized on startup and during runtime. For each Docker image there will be a +folder ``rootfs/etc/confd`` that has the following layout: + +```bash +./rootfs/etc/confd +├── conf.d +│ └── file.ext.toml +├── confd.toml +└── templates + └── file.ext.tmpl +``` + +``confd.toml`` Is the configuration of ``confd`` and will typically limit the +namespace from which ``confd`` will read key values. For example in ``activemq``: + +```toml +backend = "env" +confdir = "/etc/confd" +log-level = "debug" +interval = 600 +noop = false +prefix = "/activemq" +``` + +The prefix is set to ``/activemq`` which means only keys / value pairs under +this prefix can be used by templates. We restrict images by prefix to force them +to define their own settings, reducing dependencies between images, and to allow +for greater customization. For example you could have Gemini use PostgreSQL as a +backend and Drupal using MariaDB since they do not share the same Database +configuration. + +The ``file.ext.toml`` and ``file.ext.tmpl`` work as a pair where the ``toml`` +file defines where the template will be render to and who owns it, and the +``tmpl`` file being the template in question. Ideally these files should match +the same name of the file they are generating minus the ``toml`` or ``tmpl`` +suffix. This is to make the discovery of them easier. + +### S6 Overlay + +From this tool we only really take advantage of two features: + +- Initialization scripts (*found in ``rootfs/etc/cont-init.d``*) +- Service scripts (*found in ``rootfs/etc/services.d``*) + +Initialization scripts are run when the container is started and they execute in +alphabetical order. So to control the execution order they are prefix with +numbers. + +One initialization script ``01-confd-render-templates.sh`` is shared by all the +images. It does a first past render of the ``confd`` templates so subsequent +scripts can run. The rest of the scripts do the minimal steps required to get +the container into a ready state before the Service scripts start. + +The services scripts have the following structure: + +```bash +./rootfs/etc/services.d +└── SERVICE_NAME + ├── finish + └── run +``` + +The ``run`` script is responsible for starting the service in the +**foreground**. The ``finish`` script can perform any cleanup necessary before +stopping the service, but in general it is used to kill the container, like so: + +```bash +s6-svscanctl -t /var/run/s6/services +``` + +There are only a few Service scripts: + +- activemq +- confd +- fpm +- karaf +- mysqld +- nginx +- solr +- tomcat + +Of these only ``confd`` is running in every container, it periodically listens +for changes in either ``etcd`` or the ``environment variables`` and will +re-render the templates upon any change. + +### Image Hierarchy + +In order to save space and reduce the amount of duplication across images, they +are arranged in a hierarchy, that roughly follows below: + +```bash +├── abuild +│ └── imagemagick +└── base + ├── java + │ ├── activemq + │ ├── karaf + │ │ └── alpaca + │ ├── solr + │ └── tomcat + │ ├── blazegraph + │ ├── cantaloupe + │ ├── fcrepo + │ └── fits + ├── mariadb + └── nginx + ├── composer + ├── crayfish + │ ├── gemini + │ ├── homarus + │ ├── houdini (consumes "imagemagick" as well during its build stage) + │ ├── hypercube + │ ├── milliner + │ └── recast + ├── crayfits + ├── drupal + │ └── sandbox + └── matomo +``` + +[abuild](./abuild/README.md) and [imagemagick](./imagemagick/README.md) stand +outside of the hierarchy as they are use only to build packages that are +consumed by other images during their build stage. + +### Folder Layout + +To make reasoning about what files go where each image follows the same +filesystem layout for copying files into the image. + +A folder called ``rootfs`` maps directly onto the linux filesystem of the final +image. So for example ``rootfs/opt/islandora/configs/jwt`` will be +``/opt/islandora/configs/jwt`` in the generated image. + +### Build System + +Gradle is used as the build system, it is setup such that it will automatically +detect which folders should be considered +[projects](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html) and +what dependencies exist between them. The only caveat is +that the projects cannot be nested, though that use case does not really apply. + +The dependencies are resolved by parsing the Dockerfile and looking for ``FROM`` +statements to determine which images are required to build it. + +This means to add a new Docker image to the project you do not need to modify +the build scripts, simply add a new folder and place your Dockerfile inside of +it and it will be discovered built in the correct order relative to the other +images. + +## Design Constraints + +To be able to support a wide variety of backends for ``confd``, as well as +orchestration tools, all calls to ``getv`` **must provide a default**. With the +exception of keys that do not get used unless defined like +``DRUPAL_SITE_{SITE}_NAME``. This means the whatever backend for configuration, +wether it be ``etcd``, ``consul``, or ``environment variables``, containers can +successfully start without any other container present. + +This does not completely remove dependencies between containers, for example, +when the [sandbox](../docker/sandbox/README.md) starts it requires a running +[fcrepo](../docker/fcrepo/README.md) to be able to ingest nodes created by +``islandora_default`` features. In these cases an initialization script can +block until another container is available or a timeout has been reached. For +example: + +```bash +local fcrepo_host="{{ getv "/fcrepo/host" "fcrepo.localhost" }}" +local fcrepo_port="{{ getv "/fcrepo/host" "80" }}" +local fcrepo_url= + +# Indexing fails if port 80 is given explicitly. +if [[ "${fcrepo_port}" == "80" ]]; then + fcrepo_url="http://${fcrepo_host}/fcrepo/rest/" +else + fcrepo_url="http://${fcrepo_host}:${fcrepo_port}/fcrepo/rest/" +fi + +#... + +# Need access to Solr before we can actually import the right config. +if timeout 60 wait-for-open-port.sh "${fcrepo_host}" "${fcrepo_port}" ; then + echo "Fcrepo Found" +else + echo "Could not connect to Fcrepo" + exit 1 +fi +``` + +This allows container to start up in any order, and to be orchestrated by any tool. + +## To Do -## Design Decisions +- Blazegraph isn't working +- Check if Cantaloupe is working +- Add support for multiple backends to fedora (currently only file is being used) +- Confirm all derivative generation is working and check if additional tools + need to be build or custom builds of say `poppler utils` or some other tool, + etc are needed (need to test against a variety of inputs for each mimetype as + there may be some edge case). +- Change public/private key generation to not rely on shared volume use the configuration management +- Do we need to support tls for etcd? Probably not since it shouldn't be exposed outside of the network. Though would be nice. +- Change solr configuration to no rely on shared volume +- Ideally no shared volumes as then container can be more easily moved between nodes in a cluster +- Get working under ECS/EKS +- Get working with Kubernetes with auto scaling for micro-services +- Developer workflow documentation / examples -1. Reasonable defaults for a development environment, requiring no Environment variables / Etcd keys exist prior to running. \ No newline at end of file +[Amazon Elastic Container Service]: https://aws.amazon.com/ecs/ +[Docker Compose]: https://docs.docker.com/compose/ +[docker-compose.yml]: ./docker-compose.yml +[Drupal Installation Profile]: https://www.drupal.org/docs/8/distributions/creating-distributions/how-to-write-a-drupal-8-installation-profile +[drush]: https://drushcommands.com/ +[etcdctrl]: https://etcd.io/docs/v3.4.0/dev-guide/interacting_v3/ +[Kubernetes]: https://kubernetes.io/ +[mysql]: https://dev.mysql.com/doc/refman/8.0/en/mysql.html diff --git a/commands/continuous.sh b/commands/continuous.sh deleted file mode 100755 index 1b05a697..00000000 --- a/commands/continuous.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env bash - -set -e - -readonly PROGNAME=$(basename $0) -readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -readonly ROOT="$(realpath ${PROGDIR}/..)" -readonly ARGS="$@" - -# Kill background processes. -trap "kill 0" SIGINT - -function usage { - cat <<- EOF - usage: $PROGNAME options [images, and services...] - - Continously builds and runs the given images and services. - - OPTIONS: - -h --help Show this help. - -x --debug Debug this script. - - Examples: - $PROGNAME -EOF -} - -function cmdline { - local arg= - for arg - do - local delim="" - case "$arg" in - # Translate --gnu-long-options to -g (short options) - --help) args="${args}-h ";; - --debug) args="${args}-x ";; - # Pass through anything else - *) [[ "${arg:0:1}" == "-" ]] || delim="\"" - args="${args}${delim}${arg}${delim} ";; - esac - done - - # Reset the positional parameters to the short options - eval set -- $args - - while getopts "hx" OPTION - do - case $OPTION in - h) - usage - exit 0 - ;; - x) - readonly DEBUG='-x' - set -x - ;; - esac - done - - # All remaning parameters are files to be removed from the repo if --strip was specified. - shift $((OPTIND-1)) - readonly INPUT=("$@") - - return 0 -} - -function contains { - local search="${1}"; shift - local list=("$@") - local i= - for i in "${list[@]}"; - do - if [[ "${search}" == "${i}" ]] - then - return 0 - fi - done - return 1 -} - -function services { - docker-compose -f "${ROOT}/docker-compose.yml" config --services -} - -function images { - pushd ${PROGDIR}/.. &> /dev/null - ./gradlew tasks --all -q | grep -e ":build[^a-zA-Z]" | sed -e 's/^\([a-zA-Z0-9_-]*\):build.*$/\1/' - popd &> /dev/null -} - -function main { - cmdline ${ARGS} - local images=($(images)) - local services=($(services)) - local build=() - local run=() - local i= - local unknown_arg="true" - - for i in "${INPUT[@]}" - do - unknown_argument="true" - if contains "${i}" "${images[@]}"; then - build+=(${i}) - unknown_argument="false" - fi - if contains "${i}" "${services[@]}"; then - run+=(${i}) - unknown_argument="false" - fi - if [[ "${unknown_argument}" == "true" ]]; then - echo "Error unknown image or service: ${i}" - exit 1 - fi - done - - pushd ${PROGDIR}/.. &> /dev/null - if [ -z "${INPUT}" ]; then - ./gradlew build --continuous & - docker-compose -f "${ROOT}/docker-compose.yml" up & - else - ./gradlew $(printf ":%q:build " ${build[@]}) --continuous & - docker-compose -f "${ROOT}/docker-compose.yml" up watchtower ${run[@]} & - fi - popd &> /dev/null - - wait -} -main diff --git a/commands/drush.sh b/commands/drush.sh index 787885fc..09c9d9c7 100755 --- a/commands/drush.sh +++ b/commands/drush.sh @@ -1,4 +1,71 @@ #!/usr/bin/env bash +set -e + +readonly PROGNAME=$(basename $0) readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" readonly ROOT="$(realpath ${PROGDIR}/..)" -docker-compose -f "${ROOT}/docker-compose.yml" exec drupal s6-setuidgid nginx php -d memory_limit=-1 /usr/local/bin/drush ${@} + +readonly ARGS="$@" + +function usage { + cat <<- EOF + usage: $PROGNAME [OPTIONS] + + Executes the drush command inside of the drupal service container. + + OPTIONS: + -h --help Show this help. + -x --debug Debug this script. + + Additionally any options that are provided by the drush command. + + Examples: + Clear the Drupal cache: + $PROGNAME cr +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + # Ignore illegal options as they get passed to mysql. + while getopts "hx" OPTION &> /dev/null; + do + case $OPTION in + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + # Check if the service exists and is running. + [[ "$(docker-compose ps -q drupal)" == "" ]] && (echo "Drupal service is not running."; exit 1) + + return 0 +} + +function main { + cmdline ${ARGS} + docker-compose -f "${ROOT}/docker-compose.yml" exec drupal s6-setuidgid nginx php -d memory_limit=-1 /usr/local/bin/drush ${ARGS} +} +main diff --git a/commands/etcdctl.sh b/commands/etcdctl.sh index 21412a97..86069c96 100755 --- a/commands/etcdctl.sh +++ b/commands/etcdctl.sh @@ -1,4 +1,80 @@ #!/usr/bin/env bash +set -e + +readonly PROGNAME=$(basename $0) readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" readonly ROOT="$(realpath ${PROGDIR}/..)" -docker-compose -f "${ROOT}/docker-compose.yml" exec etcd etcdctl ${@} + +readonly ARGS="$@" + +function usage { + cat <<- EOF + usage: $PROGNAME [OPTIONS] [PARAMS] + + Executes etcdctl command inside of the etcd service container. + + OPTIONS: + -h --help Show this help. + -x --debug Debug this script. + + Additionally any options that are provided by etcdctl. + + Examples: + Display etcdctl help: + $PROGNAME help + + Put a key/value into the store: + $PROGNAME put /houdini/log/level DEBUG + + Get a value for the given key: + $PROGNAME get /houdini/log/level + + Get help for sub-command: + $PROGNAME put -h +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + # Ignore illegal options as they get passed to etcdctl. + while getopts "hx" OPTION &> /dev/null; + do + case $OPTION in + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + # Check if the service exists and is running. + [[ "$(docker-compose ps -q etcd)" == "" ]] && (echo "Etcd service is not running."; exit 1) + + return 0 +} + +function main { + cmdline ${ARGS} + docker-compose -f "${ROOT}/docker-compose.yml" exec etcd etcdctl ${ARGS} +} +main diff --git a/commands/mysql.sh b/commands/mysql.sh index dc8619c3..b436ffbd 100755 --- a/commands/mysql.sh +++ b/commands/mysql.sh @@ -1,4 +1,71 @@ #!/usr/bin/env bash +set -e + +readonly PROGNAME=$(basename $0) readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" readonly ROOT="$(realpath ${PROGDIR}/..)" -docker-compose -f "${ROOT}/docker-compose.yml" exec database mysql ${@} + +readonly ARGS="$@" + +function usage { + cat <<- EOF + usage: $PROGNAME [OPTIONS] [database] + + Executes the mysql client inside of the database service container. + + OPTIONS: + -h --help Show this help. + -x --debug Debug this script. + + Additionally any options that are provided by the mysql client. + + Examples: + $PROGNAME -u root -p drupal_default +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + # Ignore illegal options as they get passed to mysql. + while getopts "hx" OPTION &> /dev/null; + do + case $OPTION in + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + # Check if the service exists and is running. + [[ "$(docker-compose ps -q database)" == "" ]] && (echo "Database service is not running."; exit 1) + + return 0 +} + +function main { + cmdline ${ARGS} + docker-compose -f "${ROOT}/docker-compose.yml" exec database mysql ${ARGS} +} +main + diff --git a/commands/shell.sh b/commands/shell.sh index 1b64b15d..59ee29cf 100755 --- a/commands/shell.sh +++ b/commands/shell.sh @@ -1,4 +1,73 @@ #!/usr/bin/env bash +set -e + +readonly PROGNAME=$(basename $0) readonly PROGDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" readonly ROOT="$(realpath ${PROGDIR}/..)" -docker-compose -f "${ROOT}/docker-compose.yml" exec ${1} ash + +readonly ARGS="$@" + +function usage { + cat <<- EOF + usage: $PROGNAME SERVICE + + Opens an ash shell in the given SERVICE's container. + + OPTIONS: + -h --help Show this help. + -x --debug Debug this script. + + Examples: + $PROGNAME database +EOF +} + +function cmdline { + local arg= + for arg + do + local delim="" + case "$arg" in + # Translate --gnu-long-options to -g (short options) + --help) args="${args}-h ";; + --debug) args="${args}-x ";; + # Pass through anything else + *) [[ "${arg:0:1}" == "-" ]] || delim="\"" + args="${args}${delim}${arg}${delim} ";; + esac + done + + # Reset the positional parameters to the short options + eval set -- $args + + while getopts "hx" OPTION + do + case $OPTION in + h) + usage + exit 0 + ;; + x) + readonly DEBUG='-x' + set -x + ;; + esac + done + + shift $((OPTIND-1)) + readonly SERVICE="${1}" + + # Check if the service exists and is running. + [[ -z ${SERVICE} ]] && (echo "No SERVICE specified."; usage; exit 1) + docker-compose ps ${SERVICE} &> /dev/null || ( echo "Service ${SERVICE} does not exist."; exit 1 ) + [[ "$(docker-compose ps -q ${SERVICE})" == "" ]] && (echo "Service ${SERVICE} is not running."; exit 1) + + return 0 +} + +function main { + cmdline ${ARGS} + docker-compose -f "${ROOT}/docker-compose.yml" exec ${SERVICE} ash +} +main + diff --git a/composer/Dockerfile b/composer/Dockerfile index 1d031abe..c77dcfb4 100644 --- a/composer/Dockerfile +++ b/composer/Dockerfile @@ -8,7 +8,7 @@ ARG composer_project="drupal/recommended-project" WORKDIR /build -RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ +RUN --mount=type=cache,target=/root/.composer/cache \ composer create-project --no-install ${composer_project} /build && \ composer require -- drush/drush && \ composer install diff --git a/crayfits/Dockerfile b/crayfits/Dockerfile index dd2ae0ee..1d244769 100644 --- a/crayfits/Dockerfile +++ b/crayfits/Dockerfile @@ -3,7 +3,7 @@ FROM islandora/nginx:latest ARG COMMIT=4e0faeb31f84e74e7cecc083b2f096d55e425fbb -RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ +RUN --mount=type=cache,target=/root/.composer/cache \ --mount=id=downloads,type=cache,target=/opt/downloads \ git-clone-cached.sh \ --url https://github.com/roblib/CrayFits.git \ diff --git a/docker-compose.yml b/docker-compose.yml index 04bcf2ee..bbe064b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,7 +24,7 @@ # For other images you can use the command: # # ./commands/open-in-browser SERVICE -version: "3.8" +version: "3.7" networks: external: internal: false diff --git a/gemini/Dockerfile b/gemini/Dockerfile index 562a6573..fc4a8ea9 100644 --- a/gemini/Dockerfile +++ b/gemini/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:experimental FROM islandora/crayfish:latest -RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ +RUN --mount=type=cache,target=/root/.composer/cache \ composer install -d /var/www/crayfish/Gemini && \ ln -s /var/www/crayfish/Gemini/src /var/www/html && \ cleanup.sh diff --git a/homarus/Dockerfile b/homarus/Dockerfile index 3905fce6..3a80f011 100644 --- a/homarus/Dockerfile +++ b/homarus/Dockerfile @@ -8,7 +8,7 @@ RUN --mount=type=cache,target=/var/cache/apk \ && \ cleanup.sh -RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ +RUN --mount=type=cache,target=/root/.composer/cache \ composer install -d /var/www/crayfish/Homarus && \ ln -s /var/www/crayfish/Homarus/src /var/www/html && \ cleanup.sh diff --git a/houdini/Dockerfile b/houdini/Dockerfile index 1bce19af..903fd9dc 100644 --- a/houdini/Dockerfile +++ b/houdini/Dockerfile @@ -7,7 +7,7 @@ RUN --mount=type=cache,target=/var/cache/apk \ --mount=type=cache,target=/etc/cache/apk \ --mount=type=bind,from=imagemagick,source=/home/builder/packages/x86_64,target=/packages \ --mount=type=bind,from=imagemagick,source=/etc/apk/keys,target=/etc/apk/keys \ - --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + --mount=type=cache,target=/root/.composer/cache \ apk add /packages/imagemagick-*.apk && \ composer install -d /var/www/crayfish/Houdini && \ php /var/www/crayfish/Houdini/bin/console cache:clear && \ diff --git a/hypercube/Dockerfile b/hypercube/Dockerfile index 8b85ae4e..94110563 100644 --- a/hypercube/Dockerfile +++ b/hypercube/Dockerfile @@ -3,7 +3,7 @@ FROM islandora/crayfish:latest RUN --mount=type=cache,target=/var/cache/apk \ --mount=type=cache,target=/etc/cache/apk \ - --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ + --mount=type=cache,target=/root/.composer/cache \ apk-install.sh \ poppler-utils \ tesseract-ocr \ diff --git a/milliner/Dockerfile b/milliner/Dockerfile index e54d4e29..92cd6e97 100644 --- a/milliner/Dockerfile +++ b/milliner/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:experimental FROM islandora/crayfish:latest -RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ +RUN --mount=type=cache,target=/root/.composer/cache \ composer install -d /var/www/crayfish/Milliner && \ ln -s /var/www/crayfish/Milliner/src /var/www/html && \ cleanup.sh diff --git a/recast/Dockerfile b/recast/Dockerfile index fb5b00b1..735f137e 100644 --- a/recast/Dockerfile +++ b/recast/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:experimental FROM islandora/crayfish:latest -RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ +RUN --mount=type=cache,target=/root/.composer/cache \ composer install -d /var/www/crayfish/Recast && \ ln -s /var/www/crayfish/Recast/src /var/www/html && \ cleanup.sh diff --git a/sandbox/Dockerfile b/sandbox/Dockerfile index 07dd4298..1f86ca5b 100644 --- a/sandbox/Dockerfile +++ b/sandbox/Dockerfile @@ -2,7 +2,7 @@ FROM islandora/drupal:latest # Islandora based Drupal install. -RUN --mount=id=composer,sharing=locked,type=cache,target=/root/.composer/cache \ +RUN --mount=type=cache,target=/root/.composer/cache \ --mount=id=downloads,type=cache,target=/opt/downloads \ php -d memory_limit=-1 /usr/bin/composer create-project islandora/drupal-project:8.8.1 \ --prefer-dist \ From 288d42634b313b281fe651e791ced3049fc17422 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Wed, 20 May 2020 15:56:26 +0100 Subject: [PATCH 06/10] Fix default upload sizes to something more reasonable. --- .../etc/confd/templates/org.ops4j.pax.logging.cfg.tmpl | 6 +++--- nginx/rootfs/etc/confd/templates/nginx.conf.tmpl | 10 +++++----- nginx/rootfs/etc/confd/templates/php.ini.tmpl | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/alpaca/rootfs/etc/confd/templates/org.ops4j.pax.logging.cfg.tmpl b/alpaca/rootfs/etc/confd/templates/org.ops4j.pax.logging.cfg.tmpl index 762a1076..31c51588 100644 --- a/alpaca/rootfs/etc/confd/templates/org.ops4j.pax.logging.cfg.tmpl +++ b/alpaca/rootfs/etc/confd/templates/org.ops4j.pax.logging.cfg.tmpl @@ -1,5 +1,5 @@ # Root logger -log4j.rootLogger={{ getv "logger/root/level" "WARN" }}, out, osgi:* +log4j.rootLogger={{ getv "/logger/root/level" "WARN" }}, out, osgi:* log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer # File appender @@ -20,7 +20,7 @@ log4j.appender.camel.append=false log4j.appender.camel.maxFileSize=1MB log4j.appender.camel.maxBackupIndex=10 -log4j.logger.org.apache.camel={{ getv "logger/camel/level" "WARN" }}, camel +log4j.logger.org.apache.camel={{ getv "/logger/camel/level" "WARN" }}, camel # Islandora Logger log4j.appender.islandora=org.apache.log4j.RollingFileAppender @@ -31,4 +31,4 @@ log4j.appender.islandora.append=false log4j.appender.islandora.maxFileSize=1MB log4j.appender.islandora.maxBackupIndex=10 -log4j.logger.ca.islandora.camel={{ getv "logger/islandora/level" "WARN" }}, islandora +log4j.logger.ca.islandora.camel={{ getv "/logger/islandora/level" "WARN" }}, islandora diff --git a/nginx/rootfs/etc/confd/templates/nginx.conf.tmpl b/nginx/rootfs/etc/confd/templates/nginx.conf.tmpl index 76cec9f3..105042d6 100644 --- a/nginx/rootfs/etc/confd/templates/nginx.conf.tmpl +++ b/nginx/rootfs/etc/confd/templates/nginx.conf.tmpl @@ -1,13 +1,13 @@ user nginx; # Set number of worker processes automatically based on number of CPU cores. -worker_processes {{ getv "nginx/worker/processes" "auto" }}; +worker_processes {{ getv "/nginx/worker/processes" "auto" }}; # Enables the use of JIT for regular expressions to speed-up their processing. pcre_jit on; # Configures default error logger. -error_log /var/log/nginx/error.log {{ getv "nginx/error/log/level" "warn" }}; +error_log /var/log/nginx/error.log {{ getv "/nginx/error/log/level" "warn" }}; # Includes files with directives to load dynamic modules. include /etc/nginx/modules/*.conf; @@ -16,7 +16,7 @@ include /etc/nginx/modules/*.conf; events { # The maximum number of simultaneous connections that can be opened by # a worker process. - worker_connections {{ getv "nginx/worker/connections" "1024" }}; + worker_connections {{ getv "/nginx/worker/connections" "1024" }}; } http { @@ -36,11 +36,11 @@ http { # indicated by the request header Content-Length. If the stated content # length is greater than this size, then the client receives the HTTP # error code 413. Set to 0 to disable. - client_max_body_size {{ getv "nginx/client/max/body/size" "1m" }}; + client_max_body_size {{ getv "/nginx/client/max/body/size" "0" }}; # Timeout for keep-alive connections. Server will close connections after # this time. - keepalive_timeout {{ getv "nginx/keepalive/timeout" "65" }}; + keepalive_timeout {{ getv "/nginx/keepalive/timeout" "65" }}; # Sendfile copies data between one FD and other from within the kernel, # which is more efficient than read() + write(). diff --git a/nginx/rootfs/etc/confd/templates/php.ini.tmpl b/nginx/rootfs/etc/confd/templates/php.ini.tmpl index 50251c26..8327548b 100644 --- a/nginx/rootfs/etc/confd/templates/php.ini.tmpl +++ b/nginx/rootfs/etc/confd/templates/php.ini.tmpl @@ -398,7 +398,7 @@ max_input_time = {{ getv "/php/max/input/time" "60" }} ; Maximum amount of memory a script may consume (128MB) ; http://php.net/memory-limit -memory_limit = {{ getv "/php/memory/limit" "128M" }} +memory_limit = {{ getv "/php/memory/limit" "256M" }} ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Error handling and logging ; @@ -686,7 +686,7 @@ auto_globals_jit = On ; Its value may be 0 to disable the limit. It is ignored if POST data reading ; is disabled through enable_post_data_reading. ; http://php.net/post-max-size -post_max_size = {{ getv "/php/post/max/size" "8M" }} +post_max_size = {{ getv "/php/post/max/size" "0" }} ; Automatically add files before PHP document. ; http://php.net/auto-prepend-file @@ -838,7 +838,7 @@ file_uploads = On ; Maximum allowed size for uploaded files. ; http://php.net/upload-max-filesize -upload_max_filesize = {{ getv "/php/upload/max/filesize" "2M" }} +upload_max_filesize = {{ getv "/php/upload/max/filesize" "128M" }} ; Maximum number of files that can be uploaded via a single request max_file_uploads = {{ getv "/php/max/file/uploads" "20" }} From 3a0b6ada6dddc6565064b12856c46edc1f1ac94d Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Wed, 20 May 2020 16:11:11 +0100 Subject: [PATCH 07/10] Bumped the timeouts for slower machines --- README.md | 2 +- base/rootfs/usr/local/bin/wait-for-mysql.sh | 2 +- fcrepo/rootfs/etc/cont-init.d/03-fcrepo-setup.sh | 2 +- .../rootfs/etc/confd/templates/sandbox-setup.sh.tmpl | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 9c9d4267..517a8ab3 100644 --- a/README.md +++ b/README.md @@ -560,7 +560,7 @@ fi #... # Need access to Solr before we can actually import the right config. -if timeout 60 wait-for-open-port.sh "${fcrepo_host}" "${fcrepo_port}" ; then +if timeout 300 wait-for-open-port.sh "${fcrepo_host}" "${fcrepo_port}" ; then echo "Fcrepo Found" else echo "Could not connect to Fcrepo" diff --git a/base/rootfs/usr/local/bin/wait-for-mysql.sh b/base/rootfs/usr/local/bin/wait-for-mysql.sh index ea1c8959..32e6f610 100755 --- a/base/rootfs/usr/local/bin/wait-for-mysql.sh +++ b/base/rootfs/usr/local/bin/wait-for-mysql.sh @@ -95,7 +95,7 @@ function cmdline { function main { cmdline ${ARGS} - local duration=${TIMEOUT:-60} + local duration=${TIMEOUT:-300} echo "Waiting for up to ${duration} seconds to connect to Database ${DB_HOST}:${DB_PORT}" if timeout ${duration} wait-for-open-port.sh ${DB_HOST} ${DB_PORT}; then echo "Database found" diff --git a/fcrepo/rootfs/etc/cont-init.d/03-fcrepo-setup.sh b/fcrepo/rootfs/etc/cont-init.d/03-fcrepo-setup.sh index e3bc45ae..fb146fd4 100644 --- a/fcrepo/rootfs/etc/cont-init.d/03-fcrepo-setup.sh +++ b/fcrepo/rootfs/etc/cont-init.d/03-fcrepo-setup.sh @@ -2,4 +2,4 @@ set -e # Key needs to be present before startup otherwise, # it will ignore subsequent requests even if the key has been generated. -timeout 60 bash -c 'until [[ -f /opt/keys/claw/public.key ]]; do sleep 1; done' +timeout 300 bash -c 'until [[ -f /opt/keys/claw/public.key ]]; do sleep 1; done' diff --git a/sandbox/rootfs/etc/confd/templates/sandbox-setup.sh.tmpl b/sandbox/rootfs/etc/confd/templates/sandbox-setup.sh.tmpl index d8bfa58c..cee64113 100644 --- a/sandbox/rootfs/etc/confd/templates/sandbox-setup.sh.tmpl +++ b/sandbox/rootfs/etc/confd/templates/sandbox-setup.sh.tmpl @@ -62,7 +62,7 @@ EOT drush -y cset --input-format=yaml islandora.settings gemini_url "${gemini_url}" # Need access to Solr before we can actually import the right config. - if timeout 60 wait-for-open-port.sh "${solr_host}" "${solr_port}" ; then + if timeout 300 wait-for-open-port.sh "${solr_host}" "${solr_port}" ; then echo "Solr Found" else echo "Could not connect to Solr" @@ -70,7 +70,7 @@ EOT fi # Need access to Solr before we can actually import the right config. - if timeout 60 wait-for-open-port.sh "${fcrepo_host}" "${fcrepo_port}" ; then + if timeout 300 wait-for-open-port.sh "${fcrepo_host}" "${fcrepo_port}" ; then echo "Fcrepo Found" else echo "Could not connect to Fcrepo" @@ -78,7 +78,7 @@ EOT fi # Need access to activemq so we can publish the creation of before we can actually import the right config. - if timeout 60 wait-for-open-port.sh ${broker_host} ${broker_port}; then + if timeout 300 wait-for-open-port.sh ${broker_host} ${broker_port}; then echo "Broker Found" else echo "Could not connect to Broker" @@ -86,14 +86,14 @@ EOT fi # Need access to gemini before importing features. - if timeout 60 wait-for-open-port.sh "${gemini_host}" "${gemini_port}"; then + if timeout 300 wait-for-open-port.sh "${gemini_host}" "${gemini_port}"; then echo "Gemini Found" else echo "Could not connect to Gemini" exit 1 fi - if timeout 60 wait-for-open-port.sh "${triplestore_host}" "${triplestore_port}"; then + if timeout 300 wait-for-open-port.sh "${triplestore_host}" "${triplestore_port}"; then echo "Triplestore Found" else echo "Could not connect to Triplestore" From 89d299eb7d149620e12e0fc7db9ec9d4f73494f2 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Thu, 21 May 2020 16:35:18 +0100 Subject: [PATCH 08/10] Testing Actions and Remote caching of images. --- .github/workflows/master.yml | 38 +++++++++++++++++++++ .github/workflows/push.yml | 31 +++++++++++++++++ README.md | 1 + activemq/Dockerfile | 2 +- alpaca/Dockerfile | 2 +- blazegraph/Dockerfile | 2 +- build.gradle.kts | 66 +++++++++++++++++++++++++++++++----- cantaloupe/Dockerfile | 2 +- composer/Dockerfile | 4 +-- crayfish/Dockerfile | 2 +- crayfits/Dockerfile | 2 +- docker-compose.yml | 34 +++++++++---------- drupal/Dockerfile | 2 +- fcrepo/Dockerfile | 2 +- fits/Dockerfile | 2 +- gemini/Dockerfile | 2 +- gradle.properties | 12 +++++-- homarus/Dockerfile | 2 +- houdini/Dockerfile | 4 +-- hypercube/Dockerfile | 2 +- imagemagick/Dockerfile | 2 +- java/Dockerfile | 2 +- karaf/Dockerfile | 2 +- mariadb/Dockerfile | 2 +- matomo/Dockerfile | 2 +- milliner/Dockerfile | 2 +- nginx/Dockerfile | 2 +- recast/Dockerfile | 2 +- sandbox/Dockerfile | 2 +- solr/Dockerfile | 2 +- tomcat/Dockerfile | 2 +- 31 files changed, 182 insertions(+), 54 deletions(-) create mode 100644 .github/workflows/master.yml create mode 100644 .github/workflows/push.yml diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 00000000..1b09c35f --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,38 @@ +name: CI +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + name: Build and Push Docker Images + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Java + uses: actions/setup-java@v1 + with: + java-version: 8 + - name: Setup Gradle Cache + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Log in to Docker Hub + run: docker login -u '${{ secrets.REGISTRY_USER }}' -p '${{ secrets.REGISTRY_PASS }}' '${{ secrets.REGISTRY_URL }}' + - name: Build Docker images + uses: eskatos/gradle-command-action@v1 + with: + arguments: build '-Prepository=${{ secrets.REPOSITORY }}' --info + - name: Push Docker images + uses: eskatos/gradle-command-action@v1 + with: + arguments: push '-Prepository=${{ secrets.REPOSITORY }}' '-PregistryUrl=${{ secrets.REGISTRY_URL }}' '-PregistryUsername=${{ secrets.REGISTRY_USER }}' '-PregistryPassword=${{ secrets.REGISTRY_PASS }}' --info + # @todo add tests. diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 00000000..bbb073c3 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,31 @@ +name: CI +on: + push: + branches: + - '*/*' + - '*' + - '!master' +jobs: + build: + name: Build Docker Images + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Java + uses: actions/setup-java@v1 + with: + java-version: 8 + - name: Setup Gradle Cache + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Build Docker images + uses: eskatos/gradle-command-action@v1 + with: + arguments: build --info + # @todo add tests. diff --git a/README.md b/README.md index 517a8ab3..ee878b95 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # ISLE: Docker Prototype [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](./LICENSE) +![CI](https://github.com/nigelgbanks/docker-experiment/workflows/CI/badge.svg?branch=master) - [Introduction](#introduction) - [Requirements](#requirements) diff --git a/activemq/Dockerfile b/activemq/Dockerfile index 1945cd25..98ca7c81 100644 --- a/activemq/Dockerfile +++ b/activemq/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/java:latest +FROM local/java:latest RUN --mount=id=downloads,type=cache,target=/opt/downloads \ ACTIVEMQ_VERSION="5.14.5" && \ diff --git a/alpaca/Dockerfile b/alpaca/Dockerfile index f71b7c28..13f41cab 100644 --- a/alpaca/Dockerfile +++ b/alpaca/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/karaf:latest +FROM local/karaf:latest # Install common features and repos RUN bin/start && \ diff --git a/blazegraph/Dockerfile b/blazegraph/Dockerfile index 27bfc93b..d5981331 100644 --- a/blazegraph/Dockerfile +++ b/blazegraph/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/tomcat:latest +FROM local/tomcat:latest RUN --mount=id=downloads,type=cache,target=/opt/downloads \ BLAZEGRAPH_VERSION=CANDIDATE_2_1_5 && \ diff --git a/build.gradle.kts b/build.gradle.kts index 07ae98f8..40f05b85 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,7 @@ import com.bmuschko.gradle.docker.tasks.image.DockerPushImage +import com.bmuschko.gradle.docker.tasks.image.Dockerfile +import com.bmuschko.gradle.docker.tasks.image.Dockerfile.From +import com.bmuschko.gradle.docker.tasks.image.Dockerfile.FromInstruction plugins { id("com.bmuschko.docker-remote-api") version "6.4.0" @@ -10,6 +13,17 @@ extensions.findByName("buildScan")?.withGroovyBuilder { } val repository: String by project +val cacheRepository: String by project + +val registryUrl: String by project +val registryUsername: String by project +val registryPassword: String by project + +// https://docs.docker.com/engine/reference/builder/#from +// FROM [--platform=] [AS ] +// FROM [--platform=] [:] [AS ] +// FROM [--platform=] [@] [AS ] +val extractProjectDependenciesFromDockerfileRegex = """FROM[ \t]+(:?--platform=[^ ]+[ \t]+)?local/([^ :@]+):(.*)""".toRegex() subprojects { // Make all build directories relative to the root, only supports projects up to a depth of one for now. @@ -27,28 +41,58 @@ subprojects { "$image:${version}" ) + tags.split(' ').filter { it.isNotEmpty() }.map { "$image:$it" } + val createDockerfile = tasks.register("createDockerFile") { + instructionsFromTemplate(projectDir.resolve("Dockerfile")) + // Update the FROM instructions to use the repository given. + instructions.set( + instructions.get().toMutableList().map { instruction -> + if (instruction.keyword == FromInstruction.KEYWORD) { + extractProjectDependenciesFromDockerfileRegex.find(instruction.text)?.let { + val image = it.groupValues[2] + val remainder = it.groupValues[3] + FromInstruction(From("$repository/$image:$remainder")) + } ?: instruction + } + else { + instruction + } + } + ) + destFile.set(buildDir.resolve("Dockerfile")) + } + + val prepareContext = tasks.register("prepareContext") { + from(projectDir) + from(createDockerfile.map { it.destFile.get() }) + into(buildDir.resolve("context")) + } + val buildDockerImage = tasks.register("build") { group = "islandora" description = "Creates Docker image." images.addAll(imageTags) - inputDir.set(projectDir) + inputDir.set(layout.dir(prepareContext.map { it.destinationDir })) + // Use the remote cache to build this image if possible. + cacheFrom.add("$cacheRepository/${project.name}:latest") + // Allow image to be used as a cache when building on other machine. + buildArgs.put("BUILDKIT_INLINE_CACHE", "1") } tasks.register("push") { images.set(buildDockerImage.map { it.images.get() }) + registryCredentials { + url.set(registryUrl) + username.set(registryUsername) + password.set(registryPassword) + } } } } -// https://docs.docker.com/engine/reference/builder/#from -// FROM [--platform=] [AS ] -// FROM [--platform=] [:] [AS ] -// FROM [--platform=] [@] [AS ] -val extractProjectDependenciesFromDockerfileRegex = """FROM[ \t]+(:?--platform=[^ ]+[ \t]+)?islandora/([^ :@]+)""".toRegex() subprojects { tasks.withType { - val contents = inputDir.get().asFile.resolve("Dockerfile").readText() - // Extract the image name without the prefix 'islandora' it should match an existing project. + val contents = projectDir.resolve("Dockerfile").readText() + // Extract the image name without the prefix 'local' it should match an existing project. val matches = extractProjectDependenciesFromDockerfileRegex.findAll(contents) // If the project is found and it has a build task, link the dependency. @@ -64,6 +108,7 @@ subprojects { // This used to replace the FROM statements such that the referred to the Image ID rather // than "latest". Though this is currently broken when BuildKit is enabled: // https://github.com/moby/moby/issues/39769 + // Now it uses whatever repository we're building / latest since that is variable. } } } @@ -80,6 +125,10 @@ open class DockerBuildImage : DefaultTask() { @PathSensitive(PathSensitivity.RELATIVE) val inputDir = project.objects.directoryProperty() + @Input + @get:Optional + val cacheFrom = project.objects.listProperty() + @Input @get:Optional val buildArgs = project.objects.mapProperty() @@ -103,6 +152,7 @@ open class DockerBuildImage : DefaultTask() { @TaskAction fun exec() { val command = mutableListOf("docker", "build") + command.addAll(cacheFrom.get().flatMap { listOf("--cache-from", it) }) command.addAll(buildArgs.get().flatMap { listOf("--build-arg", "${it.key}=${it.value}") }) command.addAll(images.get().flatMap { listOf("--tag", it) }) command.addAll(listOf("--iidfile", imageIdFile.get().asFile.absolutePath)) diff --git a/cantaloupe/Dockerfile b/cantaloupe/Dockerfile index 34cfb679..6e2c0ed0 100644 --- a/cantaloupe/Dockerfile +++ b/cantaloupe/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/tomcat:latest +FROM local/tomcat:latest # Opted for OpenJPG over Kakadu but that could be changed. # Check 00-cantaloupe-setup-environment for the defaults. diff --git a/composer/Dockerfile b/composer/Dockerfile index c77dcfb4..43242e68 100644 --- a/composer/Dockerfile +++ b/composer/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/nginx:latest as composer +FROM local/nginx:latest as composer # Overwrite with your own arguments or explicitly copy in # your own composer.json / composer.lock files instead. @@ -13,7 +13,7 @@ RUN --mount=type=cache,target=/root/.composer/cache \ composer require -- drush/drush && \ composer install -FROM islandora/drupal:latest +FROM local/drupal:latest RUN --mount=type=bind,from=composer,source=/build,target=/build \ cp -r /build/* /var/www/drupal && \ diff --git a/crayfish/Dockerfile b/crayfish/Dockerfile index 869e5837..0552df85 100644 --- a/crayfish/Dockerfile +++ b/crayfish/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/nginx:latest +FROM local/nginx:latest ARG COMMIT=1.1.1 diff --git a/crayfits/Dockerfile b/crayfits/Dockerfile index 1d244769..23645496 100644 --- a/crayfits/Dockerfile +++ b/crayfits/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/nginx:latest +FROM local/nginx:latest ARG COMMIT=4e0faeb31f84e74e7cecc083b2f096d55e425fbb diff --git a/docker-compose.yml b/docker-compose.yml index bbe064b5..caa7171d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -73,7 +73,7 @@ services: - /var/run/docker.sock:/var/run/docker.sock command: --interval 1 --no-pull activemq: - image: islandora/activemq:latest + image: ${REPOSITORY:-local}/activemq:latest volumes: - activemq-data:/opt/activemq/data labels: @@ -82,13 +82,13 @@ services: - traefik.http.routers.activemq_http.service=activemq - traefik.http.routers.activemq_http.entrypoints=http alpaca: - image: islandora/alpaca:latest + image: ${REPOSITORY:-local}/alpaca:latest volumes: - karaf-data:/opt/karaf/data depends_on: - activemq blazegraph: - image: islandora/blazegraph:latest + image: ${REPOSITORY:-local}/blazegraph:latest volumes: - blazegraph-data:/data networks: @@ -100,7 +100,7 @@ services: - traefik.http.routers.blazegraph_http.service=blazegraph - traefik.http.routers.blazegraph_http.entrypoints=http cantaloupe: - image: islandora/cantaloupe:latest + image: ${REPOSITORY:-local}/cantaloupe:latest volumes: - cantaloupe-data:/data networks: @@ -112,19 +112,19 @@ services: - traefik.http.routers.cantaloupe_http.service=cantaloupe - traefik.http.routers.cantaloupe_http.entrypoints=http crayfits: - image: islandora/crayfits:latest + image: ${REPOSITORY:-local}/crayfits:latest depends_on: - fits # Database is chosen as the service name rather than mariadb, # as institutions may want to swap out back-ends later and # database is a more sensible default. database: - image: islandora/mariadb:latest + image: ${REPOSITORY:-local}/mariadb:latest volumes: - mysql-data:/var/lib/mysql - mysql-files:/var/lib/mysql-files drupal: - image: islandora/sandbox:latest + image: ${REPOSITORY:-local}/sandbox:latest restart: unless-stopped volumes: - drupal-config-data:/var/www/drupal/config @@ -145,7 +145,7 @@ services: - traefik.http.routers.drupal_http.service=drupal - traefik.http.routers.drupal_http.entrypoints=http fcrepo: - image: islandora/fcrepo:latest + image: ${REPOSITORY:-local}/fcrepo:latest volumes: - fcrepo-data:/data - jwt-data:/opt/keys/claw/ @@ -162,27 +162,27 @@ services: - traefik.http.routers.fcrepo_http.service=fcrepo - traefik.http.routers.fcrepo_http.entrypoints=http fits: - image: islandora/fits + image: ${REPOSITORY:-local}/fits gemini: - image: islandora/gemini:latest + image: ${REPOSITORY:-local}/gemini:latest volumes: - jwt-data:/opt/keys/claw/ depends_on: - database homarus: - image: islandora/homarus:latest + image: ${REPOSITORY:-local}/homarus:latest volumes: - jwt-data:/opt/keys/claw/ houdini: - image: islandora/houdini:latest + image: ${REPOSITORY:-local}/houdini:latest volumes: - jwt-data:/opt/keys/claw/ hypercube: - image: islandora/hypercube:latest + image: ${REPOSITORY:-local}/hypercube:latest volumes: - jwt-data:/opt/keys/claw/ matomo: - image: islandora/matomo + image: ${REPOSITORY:-local}/matomo volumes: - matomo-config-data:/opt/matomo/config depends_on: @@ -196,15 +196,15 @@ services: - traefik.http.routers.matomo_http.service=matomo - traefik.http.routers.matomo_http.entrypoints=http milliner: - image: islandora/milliner:latest + image: ${REPOSITORY:-local}/milliner:latest volumes: - jwt-data:/opt/keys/claw/ recast: - image: islandora/recast:latest + image: ${REPOSITORY:-local}/recast:latest volumes: - jwt-data:/opt/keys/claw/ solr: - image: islandora/solr:latest + image: ${REPOSITORY:-local}/solr:latest volumes: - solr-data:/opt/solr/server/solr labels: diff --git a/drupal/Dockerfile b/drupal/Dockerfile index 9a1b2e61..47fef43f 100644 --- a/drupal/Dockerfile +++ b/drupal/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/nginx:latest +FROM local/nginx:latest RUN mkdir -p /var/www/drupal && chown nginx:nginx /var/www/drupal diff --git a/fcrepo/Dockerfile b/fcrepo/Dockerfile index b4039f75..091d4853 100644 --- a/fcrepo/Dockerfile +++ b/fcrepo/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/tomcat:latest +FROM local/tomcat:latest RUN --mount=id=downloads,type=cache,target=/opt/downloads \ FCREPO_VERSION="5.1.0" && \ diff --git a/fits/Dockerfile b/fits/Dockerfile index da33a380..06c370f5 100644 --- a/fits/Dockerfile +++ b/fits/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/tomcat:latest +FROM local/tomcat:latest RUN --mount=id=downloads,type=cache,target=/opt/downloads \ FITSSERVLET_VERSION=1.2.1 && \ diff --git a/gemini/Dockerfile b/gemini/Dockerfile index fc4a8ea9..b2fb897a 100644 --- a/gemini/Dockerfile +++ b/gemini/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/crayfish:latest +FROM local/crayfish:latest RUN --mount=type=cache,target=/root/.composer/cache \ composer install -d /var/www/crayfish/Gemini && \ diff --git a/gradle.properties b/gradle.properties index 00257aa5..832caa9f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,16 @@ org.gradle.parallel=true # Will be used as a tag to denote the images release. version=0.0.1 -# The repository path to use for all images. -repository=islandora +# The repository path to use for all images when building, defaults to local. +# Override to push to your choosen remote repository. +repository=local +# The remote repository to use as a cache for images when building. +# Override to pull from your private remote repository. +cacheRepository=nigelgbanks +# Location and credentials for pushing images. +registryUrl=https://index.docker.io/v1 +registryUsername= +registryPassword= # Tags to add to all images, can be overridden on a project by project basis. tags= # By default all images have a 'prod' environment, even if they ignore it in the build, although some also have 'dev'. diff --git a/homarus/Dockerfile b/homarus/Dockerfile index 3a80f011..a0e54691 100644 --- a/homarus/Dockerfile +++ b/homarus/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/crayfish:latest +FROM local/crayfish:latest RUN --mount=type=cache,target=/var/cache/apk \ --mount=type=cache,target=/etc/cache/apk \ diff --git a/houdini/Dockerfile b/houdini/Dockerfile index 903fd9dc..2f9d8681 100644 --- a/houdini/Dockerfile +++ b/houdini/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:experimental -FROM islandora/imagemagick:latest as imagemagick +FROM local/imagemagick:latest as imagemagick -FROM islandora/crayfish:latest +FROM local/crayfish:latest RUN --mount=type=cache,target=/var/cache/apk \ --mount=type=cache,target=/etc/cache/apk \ diff --git a/hypercube/Dockerfile b/hypercube/Dockerfile index 94110563..76c737e6 100644 --- a/hypercube/Dockerfile +++ b/hypercube/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/crayfish:latest +FROM local/crayfish:latest RUN --mount=type=cache,target=/var/cache/apk \ --mount=type=cache,target=/etc/cache/apk \ diff --git a/imagemagick/Dockerfile b/imagemagick/Dockerfile index 8bb93f1d..fb551073 100644 --- a/imagemagick/Dockerfile +++ b/imagemagick/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/abuild:latest +FROM local/abuild:latest RUN --mount=type=cache,target=/var/cache/apk \ --mount=type=cache,target=/etc/cache/apk \ diff --git a/java/Dockerfile b/java/Dockerfile index 38d192c3..d7423098 100644 --- a/java/Dockerfile +++ b/java/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/base:latest +FROM local/base:latest # Install packages and tools required by all downstream images. RUN --mount=type=cache,target=/var/cache/apk \ diff --git a/karaf/Dockerfile b/karaf/Dockerfile index 36a2a374..1d4bec38 100644 --- a/karaf/Dockerfile +++ b/karaf/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/java:latest +FROM local/java:latest RUN --mount=id=downloads,type=cache,target=/opt/downloads \ KARAF_VERSION="4.0.8" && \ diff --git a/mariadb/Dockerfile b/mariadb/Dockerfile index dd867c90..fffb5ed5 100644 --- a/mariadb/Dockerfile +++ b/mariadb/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/base:latest +FROM local/base:latest RUN --mount=type=cache,target=/var/cache/apk \ --mount=type=cache,target=/etc/cache/apk \ diff --git a/matomo/Dockerfile b/matomo/Dockerfile index 9bc3f4ac..1f9ce442 100644 --- a/matomo/Dockerfile +++ b/matomo/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/nginx:latest +FROM local/nginx:latest RUN --mount=id=downloads,type=cache,target=/opt/downloads \ MATOMO_VERSION=3.13.5 && \ diff --git a/milliner/Dockerfile b/milliner/Dockerfile index 92cd6e97..50369c33 100644 --- a/milliner/Dockerfile +++ b/milliner/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/crayfish:latest +FROM local/crayfish:latest RUN --mount=type=cache,target=/root/.composer/cache \ composer install -d /var/www/crayfish/Milliner && \ diff --git a/nginx/Dockerfile b/nginx/Dockerfile index d9ffe1f6..02586fde 100644 --- a/nginx/Dockerfile +++ b/nginx/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/base:latest +FROM local/base:latest RUN --mount=type=cache,target=/var/cache/apk \ --mount=type=cache,target=/etc/cache/apk \ diff --git a/recast/Dockerfile b/recast/Dockerfile index 735f137e..27e27481 100644 --- a/recast/Dockerfile +++ b/recast/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/crayfish:latest +FROM local/crayfish:latest RUN --mount=type=cache,target=/root/.composer/cache \ composer install -d /var/www/crayfish/Recast && \ diff --git a/sandbox/Dockerfile b/sandbox/Dockerfile index 1f86ca5b..92651595 100644 --- a/sandbox/Dockerfile +++ b/sandbox/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/drupal:latest +FROM local/drupal:latest # Islandora based Drupal install. RUN --mount=type=cache,target=/root/.composer/cache \ diff --git a/solr/Dockerfile b/solr/Dockerfile index 3d2cc3c1..59e4456b 100644 --- a/solr/Dockerfile +++ b/solr/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/java:latest +FROM local/java:latest RUN --mount=id=downloads,type=cache,target=/opt/downloads \ SOLR_VERSION="7.1.0" && \ diff --git a/tomcat/Dockerfile b/tomcat/Dockerfile index 4cd17e18..df9aad3a 100644 --- a/tomcat/Dockerfile +++ b/tomcat/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:experimental -FROM islandora/java:latest +FROM local/java:latest RUN --mount=id=downloads,type=cache,target=/opt/downloads \ TOMCAT_VERSION="9.0.34" && \ From 2d2dfef5d2e0966af6a715dc82a909132349e5a9 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 22 May 2020 14:22:56 +0100 Subject: [PATCH 09/10] Support building without buildkit, use multiple remote-cache images if possible when building --- README.md | 26 ++++++- alpaca/Dockerfile | 2 + build.gradle.kts | 176 +++++++++++++++++++++++++++++++++++++--------- gradle.properties | 3 + karaf/Dockerfile | 2 - 5 files changed, 173 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index ee878b95..fe36b609 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ - [Requirements](#requirements) - [Building](#building) - [Build All Images](#build-all-images) + - [Building without Buildkit](#building-without-buildkit) - [Build Specific Image](#build-specific-image) - [Building Continuously](#building-continuously) - [Running](#running) @@ -24,6 +25,7 @@ - [Folder Layout](#folder-layout) - [Build System](#build-system) - [Design Constraints](#design-constraints) +- [Issues / FAQ](#issues--faq) - [To Do](#to-do) ## Introduction @@ -34,7 +36,7 @@ Islandora. It is not yet in a production ready state. ## Requirements -To build the Docker images using the provided Gradle build scripts requires: +To build the Docker images using the provided Gradle build scripts with [BuildKit] requires: - [Docker 18.09+](https://docs.docker.com/get-docker/) - [OpenJDK or Oracle JDK 8+](https://www.java.com/en/download/) @@ -126,6 +128,15 @@ The following will build all the images in the correct order. ./gradlew build ``` +### Building without Buildkit + +If you are having trouble building, consider building without BuildKit as it's +supported by older versions of Docker. + +```bash +./gradlew build -PuseBuildKit=false +``` + ### Build Specific Image To build a specific image and it's dependencies, for example @@ -571,6 +582,18 @@ fi This allows container to start up in any order, and to be orchestrated by any tool. +## Issues / FAQ + +**Question:** I'm getting the following error when building: + +```bash +failed to solve with frontend dockerfile.v0: failed to solve with frontend gateway.v0: runc did not terminate successfully: context canceled +``` + +**Answer:** If possible upgrade Docker to the latest version, and switch to using the +[Overlay2](https://docs.docker.com/storage/storagedriver/overlayfs-driver/#configure-docker-with-the-overlay-or-overlay2-storage-driver) +filesystem with Docker. If that doesn't work trying building [without BuildKit](#building-without-buildkit). + ## To Do - Blazegraph isn't working @@ -589,6 +612,7 @@ This allows container to start up in any order, and to be orchestrated by any to - Developer workflow documentation / examples [Amazon Elastic Container Service]: https://aws.amazon.com/ecs/ +[Buildkit]: https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md [Docker Compose]: https://docs.docker.com/compose/ [docker-compose.yml]: ./docker-compose.yml [Drupal Installation Profile]: https://www.drupal.org/docs/8/distributions/creating-distributions/how-to-write-a-drupal-8-installation-profile diff --git a/alpaca/Dockerfile b/alpaca/Dockerfile index 13f41cab..0d983e17 100644 --- a/alpaca/Dockerfile +++ b/alpaca/Dockerfile @@ -38,4 +38,6 @@ RUN chown -R karaf:karaf /opt/karaf COPY rootfs / +VOLUME [ "/opt/karaf/data/" ] + ENV JAVA_OPTS="-Dfile.encoding=UTF-8 -Dnet.sf.ehcache.skipUpdateCheck=true -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+UseParNewGC -XX:MaxPermSize=128m -Xms512m -Xmx8g" \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 40f05b85..df109087 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,8 @@ +import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage import com.bmuschko.gradle.docker.tasks.image.DockerPushImage import com.bmuschko.gradle.docker.tasks.image.Dockerfile -import com.bmuschko.gradle.docker.tasks.image.Dockerfile.From -import com.bmuschko.gradle.docker.tasks.image.Dockerfile.FromInstruction +import com.bmuschko.gradle.docker.tasks.image.Dockerfile.* +import java.lang.RuntimeException plugins { id("com.bmuschko.docker-remote-api") version "6.4.0" @@ -12,6 +13,7 @@ extensions.findByName("buildScan")?.withGroovyBuilder { setProperty("termsOfServiceAgree", "yes") } +val useBuildKit: String by project val repository: String by project val cacheRepository: String by project @@ -25,6 +27,49 @@ val registryPassword: String by project // FROM [--platform=] [@] [AS ] val extractProjectDependenciesFromDockerfileRegex = """FROM[ \t]+(:?--platform=[^ ]+[ \t]+)?local/([^ :@]+):(.*)""".toRegex() +// If Buildkit is enabled instructions are left as is otherwise Buildkit specific flags are removed. +val extractBuildkitFlagFromInstruction = """(--mount.+ \\)""".toRegex() +val preprocessRunInstruction: (Instruction) -> Instruction = if (useBuildKit.toBoolean()) { + // No-op + { instruction -> instruction } +} else { + // Strip Buildkit specific flags. + { instruction -> + // Assumes only mount flags are used and each one is separated onto it's own line. + val text = instruction.text.replace(extractBuildkitFlagFromInstruction, """\\""") + GenericInstruction(text) + } +} + +data class BindMount(val from: String, val source: String, val target: String) { + companion object { + private val EXTRACT_BIND_MOUNT_REGEX = """--mount=type=bind,([^\\]+)""".toRegex() + + fun fromInstruction(instruction: Instruction) = EXTRACT_BIND_MOUNT_REGEX.find(instruction.text)?.let { + val properties = it.groupValues[1].split(',').map { property -> + val parts = property.split('=') + Pair(parts[0], parts[1]) + }.toMap() + BindMount(properties["from"]!!, properties["source"]!!, properties["target"]!!) + } + } + // eg. COPY --from=imagemagick /home/builder/packages/x86_64 /packages + fun toCopyInstruction() = GenericInstruction("COPY --from=${from} $source $target") +} + +fun extractBindMountFlagFromInstruction() { + +} +//--mount=type=bind,from=imagemagick,source=/home/builder/packages/x86_64,target=/packages +// Generate a list of image tages for the given image, using the project, version and tag properties. +fun imagesTags(image: String, project: Project): Set { + val tags: String by project + return setOf( + "$image:latest", + "$image:${project.version}" + ) + tags.split(' ').filter { it.isNotEmpty() }.map { "$image:$it" } +} + subprojects { // Make all build directories relative to the root, only supports projects up to a depth of one for now. buildDir = rootProject.buildDir.resolve(projectDir.relativeTo(rootDir)) @@ -33,31 +78,66 @@ subprojects { // If there is a docker file in the project add the appropriate tasks. if (projectDir.resolve("Dockerfile").exists()) { apply(plugin = "com.bmuschko.docker-remote-api") - val tags: String by project - val image = "$repository/$name" - - val imageTags = setOf( - "$image:latest", - "$image:${version}" - ) + tags.split(' ').filter { it.isNotEmpty() }.map { "$image:$it" } + + val imageTags = imagesTags("$repository/$name", project) + val cachedImageTags = imagesTags("$cacheRepository/$name", project) val createDockerfile = tasks.register("createDockerFile") { instructionsFromTemplate(projectDir.resolve("Dockerfile")) - // Update the FROM instructions to use the repository given. - instructions.set( - instructions.get().toMutableList().map { instruction -> - if (instruction.keyword == FromInstruction.KEYWORD) { - extractProjectDependenciesFromDockerfileRegex.find(instruction.text)?.let { - val image = it.groupValues[2] - val remainder = it.groupValues[3] - FromInstruction(From("$repository/$image:$remainder")) - } ?: instruction + // To simplify processing the instructions group them by keyword. + val originalInstructions = instructions.get().toList() + val groupedInstructions = mutableListOf>>( + Pair(originalInstructions.first().keyword, mutableListOf(originalInstructions.first())) + ) + originalInstructions.drop(1).forEach { instruction -> + // An empty keyword means the line of text belongs to the previous instruction keyword. + if (instruction.keyword != "") { + groupedInstructions.add(Pair(instruction.keyword, mutableListOf(instruction))) + } + else { + groupedInstructions.last().second.add(instruction) + } + } + // Using bind mounts from other images needs to be mapped to COPY instructions, if not using Buildkit. + // Add these COPY instructions prior to the RUN instructions that used the bind mount. + val iterator = groupedInstructions.listIterator() + while(iterator.hasNext()) { + val (keyword, instructions) = iterator.next() + when (keyword) { + RunCommandInstruction.KEYWORD -> { + // Get any bind mount flags and convert them into copy instructions. + val bindMounts = instructions.mapNotNull { instruction-> + BindMount.fromInstruction(instruction) + } + bindMounts.forEach { bindMount -> + // Add before RUN instruction, previous is safe here as there has to always be at least a + // single FROM instruction preceeding it. + iterator.previous() + iterator.add(Pair(CopyFileInstruction.KEYWORD, mutableListOf(bindMount.toCopyInstruction()))) + iterator.next() + } } - else { - instruction + } + } + // Process instructions in place, and flatten to list. + val processedInstructions = groupedInstructions.flatMap { (keyword, instructions) -> + when (keyword) { + // Use the 'repository' name for the images when building, defaults to 'local'. + FromInstruction.KEYWORD -> { + instructions.map { instruction -> + extractProjectDependenciesFromDockerfileRegex.find(instruction.text)?.let { + val name = it.groupValues[2] + val remainder = it.groupValues[3] + FromInstruction(From("$repository/$name:$remainder")) + } ?: instruction + } + } + // Strip Buildkit flags if applicable. + RunCommandInstruction.KEYWORD -> instructions.map { preprocessRunInstruction(it) } + else -> instructions } } - ) + instructions.set(processedInstructions) destFile.set(buildDir.resolve("Dockerfile")) } @@ -67,19 +147,34 @@ subprojects { into(buildDir.resolve("context")) } - val buildDockerImage = tasks.register("build") { - group = "islandora" - description = "Creates Docker image." - images.addAll(imageTags) - inputDir.set(layout.dir(prepareContext.map { it.destinationDir })) - // Use the remote cache to build this image if possible. - cacheFrom.add("$cacheRepository/${project.name}:latest") - // Allow image to be used as a cache when building on other machine. - buildArgs.put("BUILDKIT_INLINE_CACHE", "1") + val buildDockerImage = if (useBuildKit.toBoolean()) { + tasks.register("build") { + group = "islandora" + description = "Creates Docker image." + images.addAll(imageTags) + inputDir.set(layout.dir(prepareContext.map { it.destinationDir })) + // Use the remote cache to build this image if possible. + cacheFrom.addAll(cachedImageTags) + // Allow image to be used as a cache when building on other machine. + buildArgs.put("BUILDKIT_INLINE_CACHE", "1") + } + } else { + tasks.register("build") { + group = "islandora" + description = "Creates Docker image." + images.addAll(imageTags) + inputDir.set(layout.dir(prepareContext.map { it.destinationDir })) + } } tasks.register("push") { - images.set(buildDockerImage.map { it.images.get() }) + images.set(buildDockerImage.map { + when (it) { + is DockerBuildKitBuildImage -> it.images.get() + is DockerBuildImage -> it.images.get() + else -> throw RuntimeException("Impossible to reach this state, but we must satisfy the type system.") + } + }) registryCredentials { url.set(registryUrl) username.set(registryUsername) @@ -120,7 +215,7 @@ subprojects { // Override the DockerBuildImage command to use the CLI since BuildKit is not supported in the java docker api. // https://github.com/docker-java/docker-java/issues/1381 -open class DockerBuildImage : DefaultTask() { +open class DockerBuildKitBuildImage : DefaultTask() { @InputDirectory @PathSensitive(PathSensitivity.RELATIVE) val inputDir = project.objects.directoryProperty() @@ -149,10 +244,25 @@ open class DockerBuildImage : DefaultTask() { imageIdFile.set(project.buildDir.resolve(".docker/${path.replace(":", "_")}-imageId.txt")) } + private fun cacheFromValid(image: String): Boolean { + return try { + val result = project.exec { + environment("DOCKER_CLI_EXPERIMENTAL", "enabled") + workingDir = inputDir.get().asFile + commandLine = listOf("docker", "manifest", "inspect", image) + } + result.exitValue == 0; + } + catch (e: Exception) { + logger.error("Failed to find cache image: ${image}, either it does not exist, or authentication failed.") + false + } + } + @TaskAction fun exec() { val command = mutableListOf("docker", "build") - command.addAll(cacheFrom.get().flatMap { listOf("--cache-from", it) }) + command.addAll(cacheFrom.get().filter { cacheFromValid(it) }.flatMap { listOf("--cache-from", it) }) command.addAll(buildArgs.get().flatMap { listOf("--build-arg", "${it.key}=${it.value}") }) command.addAll(images.get().flatMap { listOf("--tag", it) }) command.addAll(listOf("--iidfile", imageIdFile.get().asFile.absolutePath)) diff --git a/gradle.properties b/gradle.properties index 832caa9f..d45db1e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,9 @@ org.gradle.parallel=true # Will be used as a tag to denote the images release. version=0.0.1 +# The project can be build with/without Buildkit for those on older versions of Docker earlier than '18.09' or who +# cannot use the 'overlay2' filesystem with Docker due to using an earlier kernel version than 4.0. +useBuildKit=true # The repository path to use for all images when building, defaults to local. # Override to push to your choosen remote repository. repository=local diff --git a/karaf/Dockerfile b/karaf/Dockerfile index 1d4bec38..6aefbf81 100644 --- a/karaf/Dockerfile +++ b/karaf/Dockerfile @@ -20,6 +20,4 @@ WORKDIR /opt/karaf EXPOSE 8101 1099 44444 8181 -VOLUME [ "/opt/karaf/data/" ] - COPY rootfs / From 2e08c0e831a3c82e28abe83056753a4a4bb062a1 Mon Sep 17 00:00:00 2001 From: Nigel Banks Date: Fri, 22 May 2020 16:43:22 +0100 Subject: [PATCH 10/10] Changing the location of the cached repository in preparation for the move --- README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fe36b609..24e2916a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ISLE: Docker Prototype [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](./LICENSE) -![CI](https://github.com/nigelgbanks/docker-experiment/workflows/CI/badge.svg?branch=master) +![CI](https://github.com/Islandora-Devops/isle-buildkit/workflows/CI/badge.svg?branch=master) - [Introduction](#introduction) - [Requirements](#requirements) diff --git a/gradle.properties b/gradle.properties index d45db1e6..f72501a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ useBuildKit=true repository=local # The remote repository to use as a cache for images when building. # Override to pull from your private remote repository. -cacheRepository=nigelgbanks +cacheRepository=islandora # Location and credentials for pushing images. registryUrl=https://index.docker.io/v1 registryUsername=